From defaf7397ab43f45d5d8bacb6807e1b6008e2dea Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 9 Dec 2021 00:46:48 -0500 Subject: [PATCH] arcade: finish it up 0.12 feature parityyyyyyyy! --- src/engine/dispatch.h | 1 + src/engine/engine.cpp | 14 ++-- src/engine/platform/arcade.cpp | 117 ++++++++++++++++++++++++++++----- src/engine/platform/arcade.h | 5 +- src/engine/playback.cpp | 28 ++++++-- 5 files changed, 131 insertions(+), 34 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 1d8b4f6a8..e93708b47 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -23,6 +23,7 @@ enum DivDispatchCmds { DIV_CMD_SAMPLE_FREQ, DIV_CMD_FM_LFO, + DIV_CMD_FM_LFO_WAVE, DIV_CMD_FM_TL, DIV_CMD_FM_AR, DIV_CMD_FM_FB, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1d89a9d3a..f921a2d72 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -824,19 +824,13 @@ bool DivEngine::init(String outName) { if (dispatch->isStereo()) { for (int i=0; i16383) bbOut[0][i]=16383; - ilBuffer[i<<1]=bbOut[0][i]*2; - if (bbOut[1][i]<-16384) bbOut[1][i]=-16384; - if (bbOut[1][i]>16383) bbOut[1][i]=16383; - ilBuffer[1+(i<<1)]=bbOut[1][i]*2; + ilBuffer[i<<1]=bbOut[0][i]; + ilBuffer[1+(i<<1)]=bbOut[1][i]; } } else { for (int i=0; i16383) bbOut[0][i]=16383; - ilBuffer[i<<1]=bbOut[0][i]*2; - ilBuffer[1+(i<<1)]=bbOut[0][i]*2; + ilBuffer[i<<1]=bbOut[0][i]; + ilBuffer[1+(i<<1)]=bbOut[0][i]; } } diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 6553a2869..85706ed0a 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -9,6 +9,9 @@ static unsigned short chanOffs[8]={ static unsigned short opOffs[4]={ 0x00, 0x08, 0x10, 0x18 }; +static int pcmRates[6]={ + 65,65,90,131,180,255 +}; static bool isOutput[8][4]={ // 1 3 2 4 {false,false,false,true}, @@ -50,12 +53,39 @@ void DivPlatformArcade::acquire(short* bufL, short* bufR, size_t start, size_t l OPM_Clock(&fm,o,NULL,NULL,NULL); OPM_Clock(&fm,o,NULL,NULL,NULL); OPM_Clock(&fm,o,NULL,NULL,NULL); - - //if (o[0]<-32768) o[0]=-32768; - //if (o[0]>32767) o[0]=32767; - //if (o[1]<-32768) o[1]=-32768; - //if (o[1]>32767) o[1]=32767; + pcmCycles+=31250; + if (pcmCycles>=rate) { + pcmCycles-=rate; + + // do a PCM cycle + pcmL=0; pcmR=0; + for (int i=8; i<13; i++) { + if (chan[i].pcm.sample>=0) { + DivSample* s=parent->song.sample[chan[i].pcm.sample]; + if (s->depth==8) { + pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL); + pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR); + } else { + pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8; + pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8; + } + chan[i].pcm.pos+=chan[i].pcm.freq; + if (chan[i].pcm.pos>=(s->rendLength<<8)) { + chan[i].pcm.sample=-1; + } + } + } + } + + o[0]+=pcmL; + o[1]+=pcmR; + + if (o[0]<-32768) o[0]=-32768; + if (o[0]>32767) o[0]=32767; + + if (o[1]<-32768) o[1]=-32768; + if (o[1]>32767) o[1]=32767; bufL[h]=o[0]; bufR[h]=o[1]; @@ -87,9 +117,7 @@ void DivPlatformArcade::tick() { for (int i=0; i<8; i++) { if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>2)-64; - //writes.emplace(chanOffs[i]+0xa4,freqt>>8); - //writes.emplace(chanOffs[i]+0xa0,freqt&0xff); + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; writes.emplace(i+0x28,hScale(chan[i].freq>>6)); writes.emplace(i+0x30,chan[i].freq<<2); chan[i].freqChanged=false; @@ -104,6 +132,16 @@ void DivPlatformArcade::tick() { int DivPlatformArcade::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { + if (c.chan>7) { + chan[c.chan].pcm.sample=c.value%12; + if (chan[c.chan].pcm.sample>=parent->song.sampleLen) { + chan[c.chan].pcm.sample=-1; + break; + } + chan[c.chan].pcm.pos=0; + chan[c.chan].pcm.freq=pcmRates[parent->song.sample[chan[c.chan].pcm.sample]->rate]; + break; + } DivInstrument* ins=parent->getIns(chan[c.chan].ins); for (int i=0; i<4; i++) { @@ -127,8 +165,8 @@ int DivPlatformArcade::dispatch(DivCommand c) { } } if (chan[c.chan].insChanged) { - rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|0xc0); - //rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4)); + rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); + rWrite(chanOffs[c.chan]+0x38,((ins->fm.fms&7)<<4)|(ins->fm.ams&3)); } chan[c.chan].insChanged=false; @@ -139,11 +177,19 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: + if (c.chan>7) { + chan[c.chan].pcm.sample=-1; + } chan[c.chan].keyOff=true; chan[c.chan].active=false; break; case DIV_CMD_VOLUME: { chan[c.chan].vol=c.value; + if (c.chan>7) { + chan[c.chan].chVolL=c.value; + chan[c.chan].chVolR=c.value; + break; + } DivInstrument* ins=parent->getIns(chan[c.chan].ins); for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; @@ -168,6 +214,15 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { // TODO + if (c.chan>7) { + chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); + chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); + } else { + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + chan[c.chan].chVolL=((c.value>>4)==1); + chan[c.chan].chVolR=((c.value&15)==1); + rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7)); + } break; } case DIV_CMD_PITCH: { @@ -203,42 +258,62 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; } case DIV_CMD_FM_LFO: { - rWrite(0x22,(c.value&7)|((c.value>>4)<<3)); + if (c.chan>7) break; + rWrite(0x18,c.value); + break; + } + case DIV_CMD_FM_LFO_WAVE: { + if (c.chan>7) break; + rWrite(0x1b,c.value&3); break; } case DIV_CMD_FM_MULT: { + if (c.chan>7) break; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; - rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+0x40,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { + if (c.chan>7) break; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrument* ins=parent->getIns(chan[c.chan].ins); if (isOutput[ins->fm.alg][c.value]) { - rWrite(baseAddr+0x40,127-(((127-c.value2)*(chan[c.chan].vol&0x7f))/127)); + rWrite(baseAddr+0x60,127-(((127-c.value2)*(chan[c.chan].vol&0x7f))/127)); } else { - rWrite(baseAddr+0x40,c.value2); + rWrite(baseAddr+0x60,c.value2); } break; } case DIV_CMD_FM_AR: { + if (c.chan>7) break; DivInstrument* ins=parent->getIns(chan[c.chan].ins); if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x80,(c.value2&31)|(op.rs<<6)); } } else { DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x80,(c.value2&31)|(op.rs<<6)); } - break; } + case DIV_CMD_STD_NOISE_FREQ: { + if (c.chan!=7) break; + if (c.value) { + if (c.value>0x1f) { + rWrite(0x0f,0x80); + } else { + rWrite(0x0f,0x80|(0x1f-c.value)); + } + } else { + rWrite(0x0f,0); + } + } case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -249,6 +324,9 @@ int DivPlatformArcade::dispatch(DivCommand c) { break; case DIV_CMD_PRE_NOTE: break; + case DIV_CMD_SAMPLE_FREQ: + chan[c.chan].pcm.freq=c.value; + break; default: //printf("WARNING: unimplemented command %d\n",c.cmd); break; @@ -275,6 +353,11 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) { } lastBusy=60; + pcmCycles=0; + pcmL=0; + pcmR=0; + + rWrite(0x19,0xff); extMode=false; diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 00ab548e7..fbfa9f772 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -22,7 +22,7 @@ class DivPlatformArcade: public DivDispatch { unsigned char freq; PCMChannel(): sample(-1), pos(0), len(0), freq(0) {} } pcm; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), chVolL(8), chVolR(8) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), chVolL(127), chVolR(127) {} }; Channel chan[13]; struct QueuedWrite { @@ -33,9 +33,8 @@ class DivPlatformArcade: public DivDispatch { }; std::queue writes; opm_t fm; - int psgClocks; - int psgOut; int delay; + int pcmL, pcmR, pcmCycles; unsigned char lastBusy; bool extMode; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index fef14e3ac..086193af9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -33,6 +33,7 @@ const char* cmdName[DIV_CMD_MAX]={ "SAMPLE_FREQ", "FM_LFO", + "FM_LFO_WAVE", "FM_TL", "FM_AR", "FM_FB", @@ -167,9 +168,14 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char switch (song.system) { case DIV_SYSTEM_GENESIS: case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_ARCADE: switch (effect) { - case 0x10: // LFO - dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); + case 0x10: // LFO or noise mode + if (song.system==DIV_SYSTEM_ARCADE) { + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); + } else { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); + } break; case 0x11: // FB dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); @@ -191,8 +197,17 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); } break; - case 0x18: // EXT - dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); + case 0x17: // arcade LFO + if (song.system==DIV_SYSTEM_ARCADE) { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); + } + break; + case 0x18: // EXT or LFO waveform + if (song.system==DIV_SYSTEM_ARCADE) { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal)); + } else { + dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); + } break; case 0x19: // AR global dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); @@ -209,6 +224,11 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x1d: // AR op4 dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&31)); break; + case 0x20: // PCM frequency + if (song.system==DIV_SYSTEM_ARCADE) { + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); + } + break; default: return false; }