From 6a5a58ad134b02df5b8c8fd3b8b160fc79d1a643 Mon Sep 17 00:00:00 2001 From: cam900 Date: Thu, 11 Jul 2024 20:16:41 +0900 Subject: [PATCH] Sample playable, but still incomplete --- src/engine/platform/opl.cpp | 148 +++++++++++++++++++++++++++++++++--- src/engine/platform/opl.h | 12 ++- 2 files changed, 145 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index b9188d1db..f72479ee3 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -947,10 +947,95 @@ double DivPlatformOPL::NOTE_ADPCMB(int note) { return 0; } +double DivPlatformOPL::NOTE_OPL4(int ch, int note) { // TODO + if (pcmChanOffs<0) return 0; + if (chan[ch].sample>=0 && chan[ch].samplesong.sampleLen) { + double off=65535.0*(double)(parent->getSample(chan[ch].sample)->centerRate)/8363.0; + return parent->calcBaseFreq((double)chipClock/768,off,note,false); + } + return 0; +} + void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i=pcmChanOffs) { // OPL4 PCM chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LOG((chan[i].vol&0x7f),(0x7f*chan[i].std.vol.val)/chan[i].macroVolMul,0x7f); + immWrite(PCM_ADDR_TL+(i-pcmChanOffs),((0x7f-chan[i].outVol)<<1)|(chan[i].levelDirect?1:0)); + } + + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + DivSample* s=parent->getSample(chan[i].sample); + unsigned char ctrl=0; + double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0; + chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,(524288*768))); + if (chan[i].freq<0x400) chan[i].freq=0x400; + if (chan[i].freq>0x4000000) chan[i].freq=0x4000000; + if (chan[i].freq>=0x2000000) { + chan[i].freqH=15; + } else if (chan[i].freq>=0x1000000) { + chan[i].freqH=14; + } else if (chan[i].freq>=0x800000) { + chan[i].freqH=13; + } else if (chan[i].freq>=0x400000) { + chan[i].freqH=12; + } else if (chan[i].freq>=0x200000) { + chan[i].freqH=11; + } else if (chan[i].freq>=0x100000) { + chan[i].freqH=10; + } else if (chan[i].freq>=0x80000) { + chan[i].freqH=9; + } else if (chan[i].freq>=0x40000) { + chan[i].freqH=8; + } else if (chan[i].freq>=0x20000) { + chan[i].freqH=7; + } else if (chan[i].freq>=0x10000) { + chan[i].freqH=6; + } else if (chan[i].freq>=0x8000) { + chan[i].freqH=5; + } else if (chan[i].freq>=0x4000) { + chan[i].freqH=4; + } else if (chan[i].freq>=0x2000) { + chan[i].freqH=3; + } else if (chan[i].freq>=0x1000) { + chan[i].freqH=2; + } else if (chan[i].freq>=0x800) { + chan[i].freqH=1; + } else { + chan[i].freqH=0; + } + chan[i].freqL=(chan[i].freq>>chan[i].freqH)&0x3ff; + chan[i].freqH=8^chan[i].freqH; + ctrl|=(chan[i].active?0x80:0)|(chan[i].damp?0x40:0)|(isMuted[i]?8:(chan[i].pan&0xf)); + unsigned int waveNum=chan[i].sample; + if (ramSize<=0x200000) { + waveNum=MIN(waveNum,0x7f)|0x180; + } + if (chan[i].keyOn) { + rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl&~0x80); // force keyoff first + rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1)); + rWrite(PCM_ADDR_WAVE_L+(i-pcmChanOffs),waveNum&0xff); + if (!chan[i].std.vol.had) { + chan[i].outVol=chan[i].vol; + } + chan[i].writeCtrl=true; + chan[i].keyOn=false; + } + if (chan[i].keyOff) { + chan[i].writeCtrl=true; + chan[i].keyOff=false; + } + if (chan[i].freqChanged) { + rWrite(PCM_ADDR_WAVE_H_FN_L+(i-pcmChanOffs),((chan[i].freqL&0x7f)<<1)|((waveNum>>8)&1)); + rWrite(PCM_ADDR_FN_H_PR_OCT+(i-pcmChanOffs),((chan[i].freqH&0xf)<<4)|(chan[i].pseudoReverb?0x08:0x00)|((chan[i].freqL>>7)&0x7)); + chan[i].freqChanged=false; + } + if (chan[i].writeCtrl) { + rWrite(PCM_ADDR_KEY_DAMP_LFORST_CH_PAN+(i-pcmChanOffs),ctrl); + chan[i].writeCtrl=false; + } + } } else { int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2; chan[i].std.next(); @@ -1341,6 +1426,11 @@ int DivPlatformOPL::toFreq(int freq) { void DivPlatformOPL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; + if (ch>=pcmChanOffs) { + chan[ch].freqChanged=true; + chan[ch].writeCtrl=true; + return; + } if (ch==adpcmChan) { immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol)); return; @@ -1494,7 +1584,31 @@ int DivPlatformOPL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>=pcmChanOffs) { // OPL4 PCM - + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); + chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].sample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; + c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + } + if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { + chan[c.chan].sample=-1; + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].macroInit(ins); + if (!chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + break; } else if (c.chan==adpcmChan) { // ADPCM DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255; @@ -1619,6 +1733,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; + if (c.chan>=pcmChanOffs) { + chan[c.chan].sample=-1; + chan[c.chan].macroInit(NULL); + } break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].keyOff=true; @@ -1639,6 +1757,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; } + if (c.chan>=pcmChanOffs) { // OPL4 PCM + immWrite(PCM_ADDR_TL+(c.chan-pcmChanOffs),((0x7f-chan[c.chan].outVol)<<1)|(chan[c.chan].levelDirect?1:0)); + break; + } if (c.chan==adpcmChan) { // ADPCM-B immWrite(18,(isMuted[adpcmChan]?0:chan[adpcmChan].outVol)); break; @@ -2275,6 +2397,7 @@ DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { } int DivPlatformOPL::mapVelocity(int ch, float vel) { + if (ch>=pcmChanOffs) return vel*127.0; if (ch==adpcmChan) return vel*255.0; // -0.75dB per step // -6: 64: 8 @@ -2389,8 +2512,9 @@ void DivPlatformOPL::reset() { for (int i=0; i=pcmChanOffs)?0x7f:0x3f; + chan[i].outVol=(i>=pcmChanOffs)?0x7f:0x3f; + chan[i].pan=(i>=pcmChanOffs)?0:3; } if (adpcmChan>=0) { @@ -2746,8 +2870,9 @@ void DivPlatformOPL::renderSamples(int sysID) { if (pcmChanOffs>=0) { // OPL4 PCM size_t memPos=((ramSize<=0x200000)?0x200600:0x1800); + const int maxSample=(ramSize<=0x200000)?127:511; int sampleCount=parent->song.sampleLen; - if (sampleCount>511) sampleCount=511; + if (sampleCount>maxSample) sampleCount=maxSample; for (int i=0; isong.sample[i]; if (!s->renderOn[0][sysID]) { @@ -2795,8 +2920,8 @@ void DivPlatformOPL::renderSamples(int sysID) { DivSample* s=parent->song.sample[i]; unsigned int insAddr=(i*12)+((ramSize<=0x200000)?0x200000:0); unsigned char bitDepth; - int loop=CLAMP(s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT),0,0xffff); - int endPos=CLAMP(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT),1,0x10000); + int endPos=CLAMP(s->loopEnd,1,0x10000); + int loop=s->isLoopable()?CLAMP(s->loopStart,0,endPos-1):(endPos-1); switch (s->depth) { default: case DIV_SAMPLE_DEPTH_8BIT: @@ -2811,13 +2936,13 @@ void DivPlatformOPL::renderSamples(int sysID) { pcmMem[2+insAddr]=(sampleOffPCM[i])&0xff; pcmMem[3+insAddr]=(loop>>8)&0xff; pcmMem[4+insAddr]=(loop)&0xff; - pcmMem[5+insAddr]=(endPos>>8)&0xff; - pcmMem[6+insAddr]=(endPos)&0xff; + pcmMem[5+insAddr]=((~(endPos-1))>>8)&0xff; + pcmMem[6+insAddr]=(~(endPos-1))&0xff; // TODO: how to fill in rest of instrument table? pcmMem[7+insAddr]=0; // LFO, VIB - pcmMem[8+insAddr]=0; // AR, D1R + pcmMem[8+insAddr]=(0xf << 4) | (0xf << 0); // AR, D1R pcmMem[9+insAddr]=0; // DL, D2R - pcmMem[10+insAddr]=0; // RC, RR + pcmMem[10+insAddr]=(0xf << 4) | (0xf << 0); // RC, RR pcmMem[11+insAddr]=0; // AM } if (ramSize<=0x200000) { @@ -2825,7 +2950,6 @@ void DivPlatformOPL::renderSamples(int sysID) { } memCompo.used=pcmMemLen; - memCompo.capacity=getSampleMemCapacity(0); } else if (adpcmChan>=0) { // ADPCM size_t memPos=0; for (int i=0; isong.sampleLen; i++) { @@ -2857,8 +2981,8 @@ void DivPlatformOPL::renderSamples(int sysID) { adpcmBMemLen=memPos+256; memCompo.used=adpcmBMemLen; - memCompo.capacity=getSampleMemCapacity(0); } + memCompo.capacity=getSampleMemCapacity(0); } int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 90951b824..1394764e7 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -45,10 +45,11 @@ class DivPlatformOPL: public DivDispatch { protected: struct Channel: public SharedChannel { DivInstrumentFM state; - unsigned char freqH, freqL; + unsigned int freqH, freqL; int sample, fixedFreq; - bool furnacePCM, fourOp, hardReset; - unsigned char pan; + bool furnacePCM, fourOp, hardReset, writeCtrl; + bool levelDirect, damp, pseudoReverb; + int pan; int macroVolMul; Channel(): SharedChannel(0), @@ -59,6 +60,10 @@ class DivPlatformOPL: public DivDispatch { furnacePCM(false), fourOp(false), hardReset(false), + writeCtrl(false), + levelDirect(true), + damp(false), + pseudoReverb(false), pan(3), macroVolMul(64) { state.ops=2; @@ -138,6 +143,7 @@ class DivPlatformOPL: public DivDispatch { int octave(int freq); int toFreq(int freq); double NOTE_ADPCMB(int note); + double NOTE_OPL4(int ch, int note); void commitState(int ch, DivInstrument* ins); friend void putDispatchChip(void*,int);