From cf2c63caf75d3c64eb03929d22cf286895e8bc16 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 9 Jul 2023 17:41:24 -0500 Subject: [PATCH] YM2612: implement 9xxx, part 2 VGM export --- src/engine/engine.h | 2 +- src/engine/platform/genesis.cpp | 1 + src/engine/vgmOps.cpp | 107 +++++++++++++++++++++++++++----- 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index fb207885..1db10e53 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -489,7 +489,7 @@ class DivEngine { void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); - void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, size_t bankOffset, bool directStream); + void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream); // returns true if end of song. bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 36284160..fcd96972 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -932,6 +932,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { if (c.chan<5) c.chan=5; chan[c.chan].dacPos=c.value; chan[c.chan].setPos=true; + if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dacPos); break; case DIV_CMD_LEGATO: { if (c.chan==csmChan) { diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 97c26c56..c7fd9316 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -24,7 +24,7 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; -void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, size_t bankOffset, bool directStream) { +void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream) { unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; @@ -610,15 +610,35 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write pendingFreq[streamID]=write.val; } else { DivSample* sample=song.sample[write.val]; - w->writeC(0x95); - w->writeC(streamID); - w->writeS(write.val); // sample number - w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags + int pos=sampleOff8[write.val&0xff]+setPos[streamID]; + int len=(int)sampleLen8[write.val&0xff]-setPos[streamID]; + + if (len<0) len=0; + + if (setPos[streamID]!=0) { + if (len<=0) { + w->writeC(0x94); + w->writeC(streamID); + } else { + w->writeC(0x93); + w->writeC(streamID); + w->writeI(pos); + w->writeC(1|((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())?0x80:0)|(sampleDir[streamID]?0x10:0)); // flags + w->writeI(len); + } + } else { + w->writeC(0x95); + w->writeC(streamID); + w->writeS(write.val); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags + } + if (sample->isLoopable() && !sampleDir[streamID]) { - loopTimer[streamID]=sample->length8; + loopTimer[streamID]=len; loopSample[streamID]=write.val; } playingSample[streamID]=write.val; + setPos[streamID]=0; } } break; @@ -632,16 +652,36 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write loopFreq[streamID]=realFreq; if (pendingFreq[streamID]!=-1) { DivSample* sample=song.sample[pendingFreq[streamID]]; - w->writeC(0x95); - w->writeC(streamID); - w->writeS(pendingFreq[streamID]); // sample number - w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags + int pos=sampleOff8[pendingFreq[streamID]&0xff]+setPos[streamID]; + int len=(int)sampleLen8[pendingFreq[streamID]&0xff]-setPos[streamID]; + + if (len<0) len=0; + + if (setPos[streamID]!=0) { + if (len<=0) { + w->writeC(0x94); + w->writeC(streamID); + } else { + w->writeC(0x93); + w->writeC(streamID); + w->writeI(pos); + w->writeC(1|((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())?0x80:0)|(sampleDir[streamID]?0x10:0)); // flags + w->writeI(len); + } + } else { + w->writeC(0x95); + w->writeC(streamID); + w->writeS(pendingFreq[streamID]); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags + } + if (sample->isLoopable() && !sampleDir[streamID]) { - loopTimer[streamID]=sample->length8; + loopTimer[streamID]=len; loopSample[streamID]=pendingFreq[streamID]; } playingSample[streamID]=pendingFreq[streamID]; pendingFreq[streamID]=-1; + setPos[streamID]=0; } break; } @@ -655,6 +695,41 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write case 3: // set sample direction sampleDir[streamID]=write.val; break; + case 5: // set sample pos + setPos[streamID]=write.val; + + if (playingSample[streamID]!=-1 && pendingFreq[streamID]==-1) { + // play the sample again + DivSample* sample=song.sample[playingSample[streamID]]; + int pos=sampleOff8[playingSample[streamID]&0xff]+setPos[streamID]; + int len=(int)sampleLen8[playingSample[streamID]&0xff]-setPos[streamID]; + + if (len<0) len=0; + + if (setPos[streamID]!=0) { + if (len<=0) { + w->writeC(0x94); + w->writeC(streamID); + } else { + w->writeC(0x93); + w->writeC(streamID); + w->writeI(pos); + w->writeC(1|((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())?0x80:0)|(sampleDir[streamID]?0x10:0)); // flags + w->writeI(len); + } + } else { + w->writeC(0x95); + w->writeC(streamID); + w->writeS(playingSample[streamID]); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0 && sample->isLoopable())|(sampleDir[streamID]?0x10:0)); // flags + } + + if (sample->isLoopable() && !sampleDir[streamID]) { + loopTimer[streamID]=len; + loopSample[streamID]=playingSample[streamID]; + } + } + break; } } return; @@ -1067,6 +1142,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p int songTick=0; unsigned int sampleOff8[256]; + unsigned int sampleLen8[256]; unsigned int sampleOffSegaPCM[256]; SafeWriter* w=new SafeWriter; @@ -1087,6 +1163,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p bool sampleDir[DIV_MAX_CHANS]; int pendingFreq[DIV_MAX_CHANS]; int playingSample[DIV_MAX_CHANS]; + int setPos[DIV_MAX_CHANS]; std::vector chipVol; std::vector delayedWrites[DIV_MAX_CHIPS]; std::vector> sortedWrites; @@ -1106,6 +1183,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p loopSample[i]=-1; pendingFreq[i]=-1; playingSample[i]=-1; + setPos[i]=0; sampleDir[i]=false; } @@ -1363,7 +1441,6 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p willExport[i]=true; CHIP_VOL(6,1.0); CHIP_VOL(0x86,1.7); - writeDACSamples=true; } else if (!(hasOPN&0x40000000)) { isSecond[i]=true; willExport[i]=true; @@ -1842,6 +1919,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p // initialize sample offsets memset(sampleOff8,0,256*sizeof(unsigned int)); + memset(sampleLen8,0,256*sizeof(unsigned int)); memset(sampleOffSegaPCM,0,256*sizeof(unsigned int)); // write samples @@ -1850,6 +1928,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p DivSample* sample=song.sample[i]; logI("setting seek to %d",sampleSeek); sampleOff8[i]=sampleSeek; + sampleLen8[i]=sample->length8; sampleSeek+=sample->length8; } @@ -2241,7 +2320,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); for (DivRegWrite& j: writes) { - performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,bankOffset[i],directStream); + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i],directStream); writeCount++; } writes.clear(); @@ -2281,7 +2360,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p lastOne=i.second.time; } // write write - performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,bankOffset[i.first],directStream); + performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream); // handle global Furnace commands writeCount++;