diff --git a/doc/2-interface/export.md b/doc/2-interface/export.md index c2e520b5d..11b627d8a 100644 --- a/doc/2-interface/export.md +++ b/doc/2-interface/export.md @@ -76,10 +76,9 @@ click on **Begin Export** to... you know. ## export command stream -this option exports a text or binary file which contains a dump of the internal command stream produced when playing the song. +this option exports a binary file which contains a dump of the internal command stream produced when playing the song. it's not really useful, unless you're a developer and want to use a command stream dump for some reason (e.g. writing a hardware sound driver). -- **export (binary)**: exports in Furnace's own command stream format (FCS). see `export-tech.md` in `papers/` for details. -- **export (text)**: exports the command stream as a text file. only useful for analysis, really. +- **export**: exports in Furnace's own command stream format (FCS). see `export-tech.md` in `papers/` for details. diff --git a/doc/8-advanced/command-line.md b/doc/8-advanced/command-line.md index cc5d4f0df..3a84575e1 100644 --- a/doc/8-advanced/command-line.md +++ b/doc/8-advanced/command-line.md @@ -83,7 +83,6 @@ the following parameters may be used: - `-cmdout path`: output command stream dump to `path`. - you must provide a file, otherwise Furnace will quit. -- `-binary`: set command stream output format to binary. ## COMMAND LINE INTERFACE diff --git a/src/engine/cmdStreamOps.cpp b/src/engine/cmdStreamOps.cpp index ce8990646..e328cba38 100644 --- a/src/engine/cmdStreamOps.cpp +++ b/src/engine/cmdStreamOps.cpp @@ -21,26 +21,19 @@ #include "../ta-log.h" #define WRITE_TICK(x) \ - if (binary) { \ - if (!wroteTick[x]) { \ - wroteTick[x]=true; \ - if (tick-lastTick[x]>255) { \ - chanStream[x]->writeC(0xfc); \ - chanStream[x]->writeS(tick-lastTick[x]); \ - } else if (tick-lastTick[x]>1) { \ - delayPopularity[tick-lastTick[x]]++; \ - chanStream[x]->writeC(0xfd); \ - chanStream[x]->writeC(tick-lastTick[x]); \ - } else if (tick-lastTick[x]>0) { \ - chanStream[x]->writeC(0xfe); \ - } \ - lastTick[x]=tick; \ - } \ - } else { \ - if (!wroteTickGlobal) { \ - wroteTickGlobal=true; \ - w->writeText(fmt::sprintf(">> TICK %d\n",tick)); \ + if (!wroteTick[x]) { \ + wroteTick[x]=true; \ + if (tick-lastTick[x]>255) { \ + chanStream[x]->writeC(0xfc); \ + chanStream[x]->writeS(tick-lastTick[x]); \ + } else if (tick-lastTick[x]>1) { \ + delayPopularity[tick-lastTick[x]]++; \ + chanStream[x]->writeC(0xfd); \ + chanStream[x]->writeC(tick-lastTick[x]); \ + } else if (tick-lastTick[x]>0) { \ + chanStream[x]->writeC(0xfe); \ } \ + lastTick[x]=tick; \ } void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { @@ -205,7 +198,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { } } -SafeWriter* DivEngine::saveCommand(bool binary) { +SafeWriter* DivEngine::saveCommand() { stop(); repeatPattern=false; shallStop=false; @@ -243,49 +236,23 @@ SafeWriter* DivEngine::saveCommand(bool binary) { w->init(); // write header - if (binary) { - w->write("FCS",4); - w->writeI(chans); - // offsets - for (int i=0; iinit(); - w->writeI(0); - } - // preset delays and speed dial - for (int i=0; i<32; i++) { - w->writeC(0); - } - } else { - w->writeText("# Furnace Command Stream\n\n"); - - w->writeText("[Information]\n"); - w->writeText(fmt::sprintf("name: %s\n",song.name)); - w->writeText(fmt::sprintf("author: %s\n",song.author)); - w->writeText(fmt::sprintf("category: %s\n",song.category)); - w->writeText(fmt::sprintf("system: %s\n",song.systemName)); - - w->writeText("\n"); - - w->writeText("[SubSongInformation]\n"); - w->writeText(fmt::sprintf("name: %s\n",curSubSong->name)); - w->writeText(fmt::sprintf("tickRate: %f\n",curSubSong->hz)); - - w->writeText("\n"); - - w->writeText("[SysDefinition]\n"); - // TODO - - w->writeText("\n"); + w->write("FCS",4); + w->writeI(chans); + // offsets + for (int i=0; iinit(); + w->writeI(0); + } + // preset delays and speed dial + for (int i=0; i<32; i++) { + w->writeC(0); } // play the song ourselves bool done=false; playSub(false); - if (!binary) { - w->writeText("[Stream]\n"); - } int tick=0; bool oldCmdStreamEnabled=cmdStreamEnabled; cmdStreamEnabled=true; @@ -305,12 +272,8 @@ SafeWriter* DivEngine::saveCommand(bool binary) { if (curDivider!=divider) { curDivider=divider; WRITE_TICK(0); - if (binary) { - chanStream[0]->writeC(0xfb); - chanStream[0]->writeI((int)(curDivider*65536)); - } else { - w->writeText(fmt::sprintf(">> SET_RATE %f\n",curDivider)); - } + chanStream[0]->writeC(0xfb); + chanStream[0]->writeI((int)(curDivider*65536)); } for (DivCommand& i: cmdStream) { switch (i.cmd) { @@ -329,12 +292,8 @@ SafeWriter* DivEngine::saveCommand(bool binary) { break; default: WRITE_TICK(i.chan); - if (binary) { - cmdPopularity[i.cmd]++; - writePackedCommandValues(chanStream[i.chan],i); - } else { - w->writeText(fmt::sprintf(" %d: %s %d %d\n",i.chan,cmdName[i.cmd],i.value,i.value2)); - } + cmdPopularity[i.cmd]++; + writePackedCommandValues(chanStream[i.chan],i); break; } } @@ -348,192 +307,184 @@ SafeWriter* DivEngine::saveCommand(bool binary) { } cmdStreamEnabled=oldCmdStreamEnabled; - if (binary) { - int sortCand=-1; - int sortPos=0; - while (sortPos<16) { - sortCand=-1; - for (int i=DIV_CMD_SAMPLE_MODE; i<256; i++) { - if (cmdPopularity[i]) { - if (sortCand==-1) { - sortCand=i; - } else if (cmdPopularity[sortCand]writeC(0xff); - // optimize stream - SafeWriter* oldStream=chanStream[i]; - SafeReader* reader=oldStream->toReader(); - chanStream[i]=new SafeWriter; - chanStream[i]->init(); + sortedCmdPopularity[sortPos]=cmdPopularity[sortCand]; + sortedCmd[sortPos]=sortCand; + cmdPopularity[sortCand]=0; + sortPos++; + } - while (1) { - try { - unsigned char next=reader->readC(); - switch (next) { - case 0xb8: // instrument - case 0xc0: // pre porta - case 0xc3: // vibrato range - case 0xc4: // vibrato shape - case 0xc5: // pitch - case 0xc7: // volume - case 0xca: // legato - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - break; - case 0xbe: // panning - case 0xc2: // vibrato - case 0xc6: // arpeggio - case 0xc8: // vol slide - case 0xc9: // porta - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - break; - case 0xf0: { // full command (pre) - unsigned char cmd=reader->readC(); - bool foundShort=false; - for (int j=0; j<16; j++) { - if (sortedCmd[j]==cmd) { - chanStream[i]->writeC(0xd0+j); - foundShort=true; - break; - } - } - if (!foundShort) { - chanStream[i]->writeC(0xf7); // full command - chanStream[i]->writeC(cmd); - } - - unsigned char cmdLen=reader->readC(); - logD("cmdLen: %d",cmdLen); - for (unsigned char j=0; jreadC(); - chanStream[i]->writeC(next); - } - break; - } - case 0xfb: // tick rate - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - next=reader->readC(); - chanStream[i]->writeC(next); - break; - case 0xfc: { // 16-bit wait - unsigned short delay=reader->readS(); - bool foundShort=false; - for (int j=0; j<16; j++) { - if (sortedDelay[j]==delay) { - chanStream[i]->writeC(0xe0+j); - foundShort=true; - break; - } - } - if (!foundShort) { - chanStream[i]->writeC(next); - chanStream[i]->writeS(delay); - } - break; - } - case 0xfd: { // 8-bit wait - unsigned char delay=reader->readC(); - bool foundShort=false; - for (int j=0; j<16; j++) { - if (sortedDelay[j]==delay) { - chanStream[i]->writeC(0xe0+j); - foundShort=true; - break; - } - } - if (!foundShort) { - chanStream[i]->writeC(next); - chanStream[i]->writeC(delay); - } - break; - } - default: - chanStream[i]->writeC(next); - break; - } - } catch (EndOfFileException& e) { - break; + sortCand=-1; + sortPos=0; + while (sortPos<16) { + sortCand=-1; + for (int i=0; i<256; i++) { + if (delayPopularity[i]) { + if (sortCand==-1) { + sortCand=i; + } else if (delayPopularity[sortCand]finish(); - delete oldStream; + sortedDelayPopularity[sortPos]=delayPopularity[sortCand]; + sortedDelay[sortPos]=sortCand; + delayPopularity[sortCand]=0; + sortPos++; + } + + for (int i=0; iwriteC(0xff); + // optimize stream + SafeWriter* oldStream=chanStream[i]; + SafeReader* reader=oldStream->toReader(); + chanStream[i]=new SafeWriter; + chanStream[i]->init(); + + while (1) { + try { + unsigned char next=reader->readC(); + switch (next) { + case 0xb8: // instrument + case 0xc0: // pre porta + case 0xc3: // vibrato range + case 0xc4: // vibrato shape + case 0xc5: // pitch + case 0xc7: // volume + case 0xca: // legato + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + break; + case 0xbe: // panning + case 0xc2: // vibrato + case 0xc6: // arpeggio + case 0xc8: // vol slide + case 0xc9: // porta + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + break; + case 0xf0: { // full command (pre) + unsigned char cmd=reader->readC(); + bool foundShort=false; + for (int j=0; j<16; j++) { + if (sortedCmd[j]==cmd) { + chanStream[i]->writeC(0xd0+j); + foundShort=true; + break; + } + } + if (!foundShort) { + chanStream[i]->writeC(0xf7); // full command + chanStream[i]->writeC(cmd); + } + + unsigned char cmdLen=reader->readC(); + logD("cmdLen: %d",cmdLen); + for (unsigned char j=0; jreadC(); + chanStream[i]->writeC(next); + } + break; + } + case 0xfb: // tick rate + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + next=reader->readC(); + chanStream[i]->writeC(next); + break; + case 0xfc: { // 16-bit wait + unsigned short delay=reader->readS(); + bool foundShort=false; + for (int j=0; j<16; j++) { + if (sortedDelay[j]==delay) { + chanStream[i]->writeC(0xe0+j); + foundShort=true; + break; + } + } + if (!foundShort) { + chanStream[i]->writeC(next); + chanStream[i]->writeS(delay); + } + break; + } + case 0xfd: { // 8-bit wait + unsigned char delay=reader->readC(); + bool foundShort=false; + for (int j=0; j<16; j++) { + if (sortedDelay[j]==delay) { + chanStream[i]->writeC(0xe0+j); + foundShort=true; + break; + } + } + if (!foundShort) { + chanStream[i]->writeC(next); + chanStream[i]->writeC(delay); + } + break; + } + default: + chanStream[i]->writeC(next); + break; + } + } catch (EndOfFileException& e) { + break; + } } - for (int i=0; itell(); - logI("- %d: off %x size %ld",i,chanStreamOff[i],chanStream[i]->size()); - w->write(chanStream[i]->getFinalBuf(),chanStream[i]->size()); - chanStream[i]->finish(); - delete chanStream[i]; - } + oldStream->finish(); + delete oldStream; + } - w->seek(8,SEEK_SET); - for (int i=0; iwriteI(chanStreamOff[i]); - } + for (int i=0; itell(); + logI("- %d: off %x size %ld",i,chanStreamOff[i],chanStream[i]->size()); + w->write(chanStream[i]->getFinalBuf(),chanStream[i]->size()); + chanStream[i]->finish(); + delete chanStream[i]; + } - logD("delay popularity:"); - for (int i=0; i<16; i++) { - w->writeC(sortedDelay[i]); - if (sortedDelayPopularity[i]) logD("- %d: %d",sortedDelay[i],sortedDelayPopularity[i]); - } + w->seek(8,SEEK_SET); + for (int i=0; iwriteI(chanStreamOff[i]); + } - logD("command popularity:"); - for (int i=0; i<16; i++) { - w->writeC(sortedCmd[i]); - if (sortedCmdPopularity[i]) logD("- %s: %d",cmdName[sortedCmd[i]],sortedCmdPopularity[i]); - } - } else { - if (!playing) { - w->writeText(">> END\n"); - } else { - w->writeText(">> LOOP 0\n"); - } + logD("delay popularity:"); + for (int i=0; i<16; i++) { + w->writeC(sortedDelay[i]); + if (sortedDelayPopularity[i]) logD("- %d: %d",sortedDelay[i],sortedDelayPopularity[i]); + } + + logD("command popularity:"); + for (int i=0; i<16; i++) { + w->writeC(sortedCmd[i]); + if (sortedCmdPopularity[i]) logD("- %s: %d",cmdName[sortedCmd[i]],sortedCmdPopularity[i]); } remainingLoops=-1; diff --git a/src/engine/engine.h b/src/engine/engine.h index 049cce820..e5088d620 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -655,7 +655,7 @@ class DivEngine { // dump to ZSM. SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true); // dump command stream. - SafeWriter* saveCommand(bool binary=false); + SafeWriter* saveCommand(); // export to text SafeWriter* saveText(bool separatePatterns=true); // export to an audio file diff --git a/src/gui/csPlayer.cpp b/src/gui/csPlayer.cpp index d8f079c07..7402fa5d7 100644 --- a/src/gui/csPlayer.cpp +++ b/src/gui/csPlayer.cpp @@ -44,7 +44,7 @@ void FurnaceGUI::drawCSPlayer() { } ImGui::SameLine(); if (ImGui::Button("Burn Current Song")) { - SafeWriter* w=e->saveCommand(true); + SafeWriter* w=e->saveCommand(); if (w!=NULL) { if (!e->playStream(w->getFinalBuf(),w->size())) { showError(e->getLastError()); diff --git a/src/gui/exportOptions.cpp b/src/gui/exportOptions.cpp index 16dd7eeda..65bbefbaa 100644 --- a/src/gui/exportOptions.cpp +++ b/src/gui/exportOptions.cpp @@ -242,15 +242,10 @@ void FurnaceGUI::drawExportCommand(bool onWindow) { ); if (onWindow) { ImGui::Separator(); - if (ImGui::Button("Cancel",ImVec2(133.3f*dpiScale,0))) ImGui::CloseCurrentPopup(); + if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); ImGui::SameLine(); } - if (ImGui::Button("Export (binary)",ImVec2(133.3f*dpiScale,0))) { - openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if (ImGui::Button("Export (text)",ImVec2(133.3f*dpiScale,0))) { + if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) { openFileDialog(GUI_FILE_EXPORT_CMDSTREAM); ImGui::CloseCurrentPopup(); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5e0532db1..1d28fdf3f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1894,15 +1894,6 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { ); break; case GUI_FILE_EXPORT_CMDSTREAM: - if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir(); - hasOpened=fileDialog->openSave( - "Export Command Stream", - {"text file", "*.txt"}, - workingDirROMExport, - dpiScale - ); - break; - case GUI_FILE_EXPORT_CMDSTREAM_BINARY: if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir(); hasOpened=fileDialog->openSave( "Export Command Stream", @@ -4829,7 +4820,6 @@ bool FurnaceGUI::loop() { case GUI_FILE_EXPORT_ROM: case GUI_FILE_EXPORT_TEXT: case GUI_FILE_EXPORT_CMDSTREAM: - case GUI_FILE_EXPORT_CMDSTREAM_BINARY: workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR; break; case GUI_FILE_LOAD_MAIN_FONT: @@ -4919,10 +4909,10 @@ bool FurnaceGUI::loop() { if (curFileDialog==GUI_FILE_EXPORT_ZSM) { checkExtension(".zsm"); } - if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM || curFileDialog==GUI_FILE_EXPORT_TEXT) { + if (curFileDialog==GUI_FILE_EXPORT_TEXT) { checkExtension(".txt"); } - if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY) { + if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM) { checkExtension(".bin"); } if (curFileDialog==GUI_FILE_EXPORT_COLORS) { @@ -5316,11 +5306,8 @@ bool FurnaceGUI::loop() { } break; } - case GUI_FILE_EXPORT_CMDSTREAM: - case GUI_FILE_EXPORT_CMDSTREAM_BINARY: { - bool isBinary=(curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY); - - SafeWriter* w=e->saveCommand(isBinary); + case GUI_FILE_EXPORT_CMDSTREAM: { + SafeWriter* w=e->saveCommand(); if (w!=NULL) { FILE* f=ps_fopen(copyOfName.c_str(),"wb"); if (f!=NULL) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 3b1a2b552..3bda6b320 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -537,7 +537,6 @@ enum FurnaceGUIFileDialogs { GUI_FILE_EXPORT_VGM, GUI_FILE_EXPORT_ZSM, GUI_FILE_EXPORT_CMDSTREAM, - GUI_FILE_EXPORT_CMDSTREAM_BINARY, GUI_FILE_EXPORT_TEXT, GUI_FILE_EXPORT_ROM, GUI_FILE_LOAD_MAIN_FONT, diff --git a/src/main.cpp b/src/main.cpp index 3b23068e6..0dcbcfa5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,7 +72,6 @@ bool consoleMode=true; #endif bool displayEngineFailError=false; -bool cmdOutBinary=false; bool vgmOutDirect=false; bool safeMode=false; @@ -153,11 +152,6 @@ TAParamResult pSafeModeAudio(String val) { #endif } -TAParamResult pBinary(String val) { - cmdOutBinary=true; - return TA_PARAM_SUCCESS; -} - TAParamResult pDirect(String val) { vgmOutDirect=true; return TA_PARAM_SUCCESS; @@ -373,7 +367,6 @@ void initParams() { params.push_back(TAParam("D","direct",false,pDirect,"","set VGM export direct stream mode")); params.push_back(TAParam("Z","zsmout",true,pZSMOut,"","output .zsm data for Commander X16 Zsound")); params.push_back(TAParam("C","cmdout",true,pCmdOut,"","output command stream")); - params.push_back(TAParam("b","binary",false,pBinary,"","set command stream output format to binary")); params.push_back(TAParam("L","loglevel",true,pLogLevel,"debug|info|warning|error","set the log level (info by default)")); params.push_back(TAParam("v","view",true,pView,"pattern|commands|nothing","set visualization (nothing by default)")); params.push_back(TAParam("i","info",false,pInfo,"","get info about a song")); @@ -660,7 +653,7 @@ int main(int argc, char** argv) { if (outName!="" || vgmOutName!="" || cmdOutName!="") { if (cmdOutName!="") { - SafeWriter* w=e.saveCommand(cmdOutBinary); + SafeWriter* w=e.saveCommand(); if (w!=NULL) { FILE* f=fopen(cmdOutName.c_str(),"wb"); if (f!=NULL) {