diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 6963dfb8..ffd9e247 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -324,7 +324,7 @@ class DivDispatch { * @param rate stream rate (e.g. 44100 for VGM). * @param len number of samples. */ - virtual void fillStream(std::vector& stream, int rate, size_t len); + virtual void fillStream(std::vector& stream, int sRate, size_t len); /** * send a command to this dispatch. diff --git a/src/engine/engine.h b/src/engine/engine.h index e524f0a4..f4ed7cf5 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -431,7 +431,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); + void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, 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); @@ -515,7 +515,7 @@ class DivEngine { // specify system to build ROM for. SafeWriter* buildROM(int sys); // dump to VGM. - SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false); + SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false); // dump to ZSM. SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true); // dump command stream. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index cdff34f3..8b3239f0 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -22,7 +22,7 @@ void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) { } -void DivDispatch::fillStream(std::vector& stream, int rate, size_t len) { +void DivDispatch::fillStream(std::vector& stream, int sRate, size_t len) { } void DivDispatch::tick(bool sysTick) { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 63ff23ee..67f58a67 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1553,7 +1553,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef( - "OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0, false, 1U<=0xffff0000) { // Furnace special command + if (write.addr>=0xffff0000 && !directStream) { // Furnace special command unsigned char streamID=streamOff+((write.addr&0xff00)>>8); logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID); switch (write.addr&0xff) { @@ -843,7 +843,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \ } -SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints) { +SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool patternHints, bool directStream) { if (version<0x150) { lastError="VGM version is too low"; return NULL; @@ -947,6 +947,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p int loopSample[DIV_MAX_CHANS]; bool sampleDir[DIV_MAX_CHANS]; std::vector chipVol; + std::vector delayedWrites[32]; for (int i=0; ilength8; } - if (writeDACSamples) for (int i=0; iwriteC(0x67); w->writeC(0x66); @@ -1596,7 +1597,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } } - if (writeNESSamples) for (int i=0; iwriteC(0x67); w->writeC(0x66); @@ -1607,7 +1608,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } } - if (writePCESamples) for (int i=0; iwriteC(0x67); w->writeC(0x66); @@ -1785,87 +1786,89 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p // initialize streams int streamID=0; - for (int i=0; iwriteC(0x90); - w->writeC(streamID); - w->writeC(0x02); - w->writeC(0); // port - w->writeC(0x2a); // DAC - - w->writeC(0x91); - w->writeC(streamID); - w->writeC(0); - w->writeC(1); - w->writeC(0); - - w->writeC(0x92); - w->writeC(streamID); - w->writeI(32000); // default - streamID++; - break; - case DIV_SYSTEM_NES: - w->writeC(0x90); - w->writeC(streamID); - w->writeC(20); - w->writeC(0); // port - w->writeC(0x11); // DAC - - w->writeC(0x91); - w->writeC(streamID); - w->writeC(7); - w->writeC(1); - w->writeC(0); - - w->writeC(0x92); - w->writeC(streamID); - w->writeI(32000); // default - streamID++; - break; - case DIV_SYSTEM_PCE: - for (int j=0; j<6; j++) { + if (!directStream) { + for (int i=0; iwriteC(0x90); w->writeC(streamID); - w->writeC(27); - w->writeC(j); // port - w->writeC(0x06); // select+DAC + w->writeC(0x02); + w->writeC(0); // port + w->writeC(0x2a); // DAC w->writeC(0x91); w->writeC(streamID); - w->writeC(5); + w->writeC(0); w->writeC(1); w->writeC(0); w->writeC(0x92); w->writeC(streamID); - w->writeI(16000); // default + w->writeI(32000); // default streamID++; - } - break; - case DIV_SYSTEM_SWAN: - w->writeC(0x90); - w->writeC(streamID); - w->writeC(isSecond[i]?0xa1:0x21); - w->writeC(0); // port - w->writeC(0x09); // DAC + break; + case DIV_SYSTEM_NES: + w->writeC(0x90); + w->writeC(streamID); + w->writeC(20); + w->writeC(0); // port + w->writeC(0x11); // DAC - w->writeC(0x91); - w->writeC(streamID); - w->writeC(0); - w->writeC(1); - w->writeC(0); + w->writeC(0x91); + w->writeC(streamID); + w->writeC(7); + w->writeC(1); + w->writeC(0); - w->writeC(0x92); - w->writeC(streamID); - w->writeI(24000); // default - streamID++; - break; - default: - break; + w->writeC(0x92); + w->writeC(streamID); + w->writeI(32000); // default + streamID++; + break; + case DIV_SYSTEM_PCE: + for (int j=0; j<6; j++) { + w->writeC(0x90); + w->writeC(streamID); + w->writeC(27); + w->writeC(j); // port + w->writeC(0x06); // select+DAC + + w->writeC(0x91); + w->writeC(streamID); + w->writeC(5); + w->writeC(1); + w->writeC(0); + + w->writeC(0x92); + w->writeC(streamID); + w->writeI(16000); // default + streamID++; + } + break; + case DIV_SYSTEM_SWAN: + w->writeC(0x90); + w->writeC(streamID); + w->writeC(isSecond[i]?0xa1:0x21); + w->writeC(0); // port + w->writeC(0x09); // DAC + + w->writeC(0x91); + w->writeC(streamID); + w->writeC(0); + w->writeC(1); + w->writeC(0); + + w->writeC(0x92); + w->writeC(streamID); + w->writeI(24000); // default + streamID++; + break; + default: + break; + } } } @@ -1894,10 +1897,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p break; } // stop all streams - for (int i=0; iwriteC(0x94); - w->writeC(i); - loopSample[i]=-1; + if (!directStream) { + for (int i=0; iwriteC(0x94); + w->writeC(i); + loopSample[i]=-1; + } } if (!playing) { @@ -1929,63 +1934,69 @@ 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]); + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],directStream); writeCount++; } writes.clear(); } // check whether we need to loop int totalWait=cycles>>MASTER_CLOCK_PREC; - for (int i=0; i=0) { - loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait; + if (directStream) { + for (int i=0; ifillStream(delayedWrites[i],44100,totalWait); } - } - bool haveNegatives=false; - for (int i=0; i=0) { - if (loopTimer[i]<0) { - haveNegatives=true; + } else { + for (int i=0; i=0) { + loopTimer[i]-=(loopFreq[i]/44100.0)*(double)totalWait; } } - } - while (haveNegatives) { - // finish all negatives - int nextToTouch=-1; + bool haveNegatives=false; for (int i=0; i=0) { if (loopTimer[i]<0) { - if (nextToTouch>=0) { - if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i; - } else { - nextToTouch=i; - } + haveNegatives=true; } } } - if (nextToTouch>=0) { - double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch]))); - if (waitTime>0) { - w->writeC(0x61); - w->writeS(waitTime); - logV("wait is: %f",waitTime); - totalWait-=waitTime; - tickCount+=waitTime; - } - if (loopSample[nextToTouch]getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { - w->writeC(0x93); - w->writeC(nextToTouch); - w->writeI(sampleOff8[loopSample[nextToTouch]]+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); - w->writeC(0x81); - w->writeI(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); + while (haveNegatives) { + // finish all negatives + int nextToTouch=-1; + for (int i=0; i=0) { + if (loopTimer[i]<0) { + if (nextToTouch>=0) { + if (loopTimer[nextToTouch]>loopTimer[i]) nextToTouch=i; + } else { + nextToTouch=i; + } + } } } - loopSample[nextToTouch]=-1; - } else { - haveNegatives=false; + if (nextToTouch>=0) { + double waitTime=totalWait+(loopTimer[nextToTouch]*(44100.0/MAX(1,loopFreq[nextToTouch]))); + if (waitTime>0) { + w->writeC(0x61); + w->writeS(waitTime); + logV("wait is: %f",waitTime); + totalWait-=waitTime; + tickCount+=waitTime; + } + if (loopSample[nextToTouch]getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)) { + w->writeC(0x93); + w->writeC(nextToTouch); + w->writeI(sampleOff8[loopSample[nextToTouch]]+sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); + w->writeC(0x81); + w->writeI(sample->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)); + } + } + loopSample[nextToTouch]=-1; + } else { + haveNegatives=false; + } } } // write wait diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 3a1931b6..a66567ed 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3390,6 +3390,14 @@ bool FurnaceGUI::loop() { "pattern indexes are ordered as they appear in the song." ); } + ImGui::Checkbox("direct stream mode",&vgmExportDirectStream); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "required for DualPCM and MSM6258 export.\n\n" + "allows for volume/direction changes when playing samples,\n" + "at the cost of a massive increase in file size." + ); + } ImGui::Text("chips to export:"); bool hasOneAtLeast=false; for (int i=0; isong.systemLen; i++) { @@ -4161,7 +4169,7 @@ bool FurnaceGUI::loop() { break; } case GUI_FILE_EXPORT_VGM: { - SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints); + SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints,vgmExportDirectStream); if (w!=NULL) { FILE* f=ps_fopen(copyOfName.c_str(),"wb"); if (f!=NULL) { @@ -5309,6 +5317,7 @@ FurnaceGUI::FurnaceGUI(): vgmExportLoop(true), zsmExportLoop(true), vgmExportPatternHints(false), + vgmExportDirectStream(false), portrait(false), mobileMenuOpen(false), wantCaptureKeyboard(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index dd1fa92f..fc47d6d8 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1035,6 +1035,7 @@ class FurnaceGUI { std::deque recentFile; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; + bool vgmExportDirectStream; bool portrait, mobileMenuOpen; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;