diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a1e6a0b06..32b13f9e5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -621,7 +621,6 @@ void DivEngine::renderSamples() { s->rendLength=(double)s->length/samplePitches[s->pitch]; s->rendData=new short[s->rendLength]; int k=0; - printf("Volume: %d\n",s->vol); float mult=(float)(s->vol+100)/150.0f; for (double j=0; jlength; j+=samplePitches[s->pitch]) { if (k>=s->rendLength) { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index d5e838639..90c221fc1 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -3,6 +3,8 @@ #include #include +#include "genesisshared.h" + void DivPlatformGenesis::acquire(int& l, int& r) { static short o[2]; @@ -52,38 +54,9 @@ void DivPlatformGenesis::acquire(int& l, int& r) { r=(o[1]<<7)+psgOut; } -static unsigned short chanOffs[6]={ - 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 -}; -static unsigned short opOffs[4]={ - 0x00, 0x04, 0x08, 0x0c -}; -static unsigned char konOffs[6]={ - 0, 1, 2, 4, 5, 6 -}; -static bool isOutput[8][4]={ - // 1 3 2 4 - {false,false,false,true}, - {false,false,false,true}, - {false,false,false,true}, - {false,false,false,true}, - {false,false,true ,true}, - {false,true ,true ,true}, - {false,true ,true ,true}, - {true ,true ,true ,true}, -}; -static unsigned char dtTable[8]={ - 7,6,5,0,1,2,3,0 -}; -static int dacRates[6]={ - 160,160,116,80,58,40 -}; -static int orderedOps[4]={ - 0,2,1,3 -}; - void DivPlatformGenesis::tick() { for (int i=0; i<6; i++) { + if (i==2 && extMode) continue; if (chan[i].keyOn || chan[i].keyOff) { writes.emplace(0x28,0x00|konOffs[i]); chan[i].keyOff=false; @@ -98,6 +71,7 @@ void DivPlatformGenesis::tick() { } for (int i=0; i<6; i++) { + if (i==2 && extMode) continue; if (chan[i].freqChanged) { chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE+chan[i].pitch))/ONE_SEMITONE; if (chan[i].freq>=82432) { @@ -158,8 +132,6 @@ int DivPlatformGenesis::octave(int freq) { return 1; } -#define rWrite(a,v) pendingWrites[a]=v; - int DivPlatformGenesis::dispatch(DivCommand c) { if (c.chan>5) { c.chan-=6; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index bd60dae66..f33572d73 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -1,6 +1,9 @@ #include "genesisext.h" +#include "../engine.h" #include +#include "genesisshared.h" + int DivPlatformGenesisExt::dispatch(DivCommand c) { if (c.chan<2) { return DivPlatformGenesis::dispatch(c); @@ -9,33 +12,245 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { c.chan-=3; return DivPlatformGenesis::dispatch(c); } + int ch=c.chan-2; + int ordch=orderedOps[ch]; switch (c.cmd) { - case DIV_CMD_NOTE_ON: - chan[c.chan].freq=16.4f*pow(2.0f,((float)c.value/12.0f)); - chan[c.chan].active=true; + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(opChan[ch].ins); + + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=ins->fm.op[ordch]; + if (isOutput[ins->fm.alg][ordch]) { + if (!opChan[ch].active || opChan[ch].insChanged) { + rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + } + } else { + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x40,op.tl); + } + } + if (opChan[ch].insChanged) { + rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7)); + rWrite(baseAddr+0x70,op.d2r&31); + rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4)); + rWrite(baseAddr+0x90,op.ssgEnv&15); + } + if (opChan[ch].insChanged) { // TODO how does this work? + rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3)); + rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + } + opChan[ch].insChanged=false; + + opChan[ch].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].freqChanged=true; + opChan[ch].keyOn=true; + opChan[ch].active=true; break; + } case DIV_CMD_NOTE_OFF: - chan[c.chan].active=false; + opChan[ch].keyOff=true; + opChan[ch].active=false; break; - case DIV_CMD_VOLUME: - chan[c.chan].vol=c.value; + case DIV_CMD_VOLUME: { + opChan[ch].vol=c.value; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; + DivInstrumentFM::Operator op=ins->fm.op[ordch]; + if (isOutput[ins->fm.alg][ordch]) { + rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + } else { + rWrite(baseAddr+0x40,op.tl); + } break; + } + case DIV_CMD_GET_VOLUME: { + return opChan[ch].vol; + break; + } + case DIV_CMD_INSTRUMENT: + if (opChan[ch].ins!=c.value) { + opChan[ch].insChanged=true; + } + opChan[ch].ins=c.value; + break; + case DIV_CMD_PANNING: { + switch (c.value) { + case 0x01: + opChan[ch].pan=1; + break; + case 0x10: + opChan[ch].pan=2; + break; + default: + opChan[ch].pan=3; + break; + } + DivInstrument* ins=parent->getIns(opChan[ch].ins); + // TODO: ??? + rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + break; + } + case DIV_CMD_PITCH: { + opChan[ch].pitch=c.value; + opChan[ch].freqChanged=true; + break; + } + case DIV_CMD_NOTE_PORTA: { + int destFreq=644.0f*pow(2.0f,((float)c.value2/12.0f)); + bool return2=false; + if (destFreq>opChan[ch].baseFreq) { + opChan[ch].baseFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + if (opChan[ch].baseFreq>=destFreq) { + opChan[ch].baseFreq=destFreq; + return2=true; + } + } else { + opChan[ch].baseFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + if (opChan[ch].baseFreq<=destFreq) { + opChan[ch].baseFreq=destFreq; + return2=true; + } + } + opChan[ch].freqChanged=true; + if (return2) return 2; + break; + } + case DIV_CMD_SAMPLE_MODE: { + // ignored on extended channel 3 mode. + break; + } + case DIV_CMD_LEGATO: { + opChan[ch].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f)); + opChan[ch].freqChanged=true; + break; + } + case DIV_CMD_FM_MULT: { // TODO + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); + break; + } + case DIV_CMD_FM_TL: { // TODO + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + DivInstrument* ins=parent->getIns(opChan[ch].ins); + if (isOutput[ins->fm.alg][c.value]) { + rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); + } else { + rWrite(baseAddr+0x40,c.value2); + } + break; + } + case DIV_CMD_FM_AR: { + DivInstrument* ins=parent->getIns(opChan[ch].ins); + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator op=ins->fm.op[i]; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + } + } else { + DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + } + break; + } case DIV_CMD_GET_VOLMAX: return 127; break; + case DIV_CMD_PRE_PORTA: + break; default: + printf("WARNING: unimplemented command %d\n",c.cmd); break; } return 1; } +static int opChanOffsL[4]={ + 0xa9, 0xaa, 0xa8, 0xa2 +}; + +static int opChanOffsH[4]={ + 0xad, 0xae, 0xac, 0xa6 +}; + void DivPlatformGenesisExt::tick() { + if (extMode) { + bool writeSomething=false; + unsigned char writeMask=2; + for (int i=0; i<4; i++) { + writeMask|=opChan[i].active<<(4+i); + if (opChan[i].keyOn || opChan[i].keyOff) { + writeSomething=true; + writeMask&=~(1<<(4+i)); + opChan[i].keyOff=false; + } + } + if (writeSomething) { + writes.emplace(0x28,writeMask); + } + } + DivPlatformGenesis::tick(); + + bool writeNoteOn=false; + unsigned char writeMask=2; + if (extMode) for (int i=0; i<4; i++) { + if (opChan[i].freqChanged) { + opChan[i].freq=(opChan[i].baseFreq*(ONE_SEMITONE+opChan[i].pitch))/ONE_SEMITONE; + if (opChan[i].freq>=82432) { + opChan[i].freqH=((opChan[i].freq>>15)&7)|0x38; + opChan[i].freqL=(opChan[i].freq>>7)&0xff; + } else if (opChan[i].freq>=41216) { + opChan[i].freqH=((opChan[i].freq>>14)&7)|0x30; + opChan[i].freqL=(opChan[i].freq>>6)&0xff; + } else if (opChan[i].freq>=20608) { + opChan[i].freqH=((opChan[i].freq>>13)&7)|0x28; + opChan[i].freqL=(opChan[i].freq>>5)&0xff; + } else if (opChan[i].freq>=10304) { + opChan[i].freqH=((opChan[i].freq>>12)&7)|0x20; + opChan[i].freqL=(opChan[i].freq>>4)&0xff; + } else if (opChan[i].freq>=5152) { + opChan[i].freqH=((opChan[i].freq>>11)&7)|0x18; + opChan[i].freqL=(opChan[i].freq>>3)&0xff; + } else if (opChan[i].freq>=2576) { + opChan[i].freqH=((opChan[i].freq>>10)&7)|0x10; + opChan[i].freqL=(opChan[i].freq>>2)&0xff; + } else if (opChan[i].freq>=1288) { + opChan[i].freqH=((opChan[i].freq>>9)&7)|0x08; + opChan[i].freqL=(opChan[i].freq>>1)&0xff; + } else { + opChan[i].freqH=(opChan[i].freq>>8)&7; + opChan[i].freqL=opChan[i].freq&0xff; + } + writes.emplace(opChanOffsH[i],opChan[i].freqH); + writes.emplace(opChanOffsL[i],opChan[i].freqL); + } + writeMask|=opChan[i].active<<(4+i); + if (opChan[i].keyOn) { + writeNoteOn=true; + writeMask|=1<<(4+i); + opChan[i].keyOn=false; + } + } + if (writeNoteOn) { + writes.emplace(0x28,writeMask); + } } int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate) { DivPlatformGenesis::init(parent,channels,sugRate); + for (int i=0; i<4; i++) { + opChan[i].vol=127; + } + + // channel 3 mode + writes.emplace(0x27,0x40); extMode=true; return 13; } diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 1c67f42eb..a83995a14 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -3,17 +3,17 @@ #include "genesis.h" class DivPlatformGenesisExt: public DivPlatformGenesis { - struct Channel { + struct OpChannel { unsigned char freqH, freqL; int freq, baseFreq, pitch; unsigned char ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff; - signed char vol; + int vol; unsigned char pan; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {} + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {} }; - Channel opChan[4]; + OpChannel opChan[4]; public: int dispatch(DivCommand c); void tick(); diff --git a/src/engine/platform/genesisshared.h b/src/engine/platform/genesisshared.h new file mode 100644 index 000000000..8fd58120c --- /dev/null +++ b/src/engine/platform/genesisshared.h @@ -0,0 +1,31 @@ +static unsigned short chanOffs[6]={ + 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 +}; +static unsigned short opOffs[4]={ + 0x00, 0x04, 0x08, 0x0c +}; +static unsigned char konOffs[6]={ + 0, 1, 2, 4, 5, 6 +}; +static bool isOutput[8][4]={ + // 1 3 2 4 + {false,false,false,true}, + {false,false,false,true}, + {false,false,false,true}, + {false,false,false,true}, + {false,false,true ,true}, + {false,true ,true ,true}, + {false,true ,true ,true}, + {true ,true ,true ,true}, +}; +static unsigned char dtTable[8]={ + 7,6,5,0,1,2,3,0 +}; +static int dacRates[6]={ + 160,160,116,80,58,40 +}; +static int orderedOps[4]={ + 0,2,1,3 +}; + +#define rWrite(a,v) pendingWrites[a]=v; diff --git a/src/main.cpp b/src/main.cpp index cf827ca2c..dc78b9f49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include "ta-log.h" #include "engine/engine.h" -#define DIV_VERSION "dev4" +#define DIV_VERSION "dev5" DivEngine e;