add a command stream dump option

This commit is contained in:
tildearrow 2022-08-04 00:51:47 -05:00
parent a0d10aa60b
commit d54d853ff8
6 changed files with 182 additions and 3 deletions

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dispatch.h"
#define _USE_MATH_DEFINES
#include "engine.h"
#include "instrument.h"
@ -234,6 +235,111 @@ double DivEngine::benchmarkSeek() {
return tAvg;
}
SafeWriter* DivEngine::saveCommand(bool binary) {
logI("implement! %d",binary);
stop();
repeatPattern=false;
setOrder(0);
BUSY_BEGIN_SOFT;
// determine loop point
int loopOrder=0;
int loopRow=0;
int loopEnd=0;
walkSong(loopOrder,loopRow,loopEnd);
logI("loop point: %d %d",loopOrder,loopRow);
SafeWriter* w=new SafeWriter;
w->init();
// write header
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");
// play the song ourselves
bool done=false;
playSub(false);
w->writeText("[Stream]\n");
int tick=0;
bool oldCmdStreamEnabled=cmdStreamEnabled;
cmdStreamEnabled=true;
double curDivider=divider;
while (!done) {
if (nextTick(false,true) || !playing) {
done=true;
}
// get command stream
bool wroteTick=false;
if (curDivider!=divider) {
curDivider=divider;
if (!wroteTick) {
wroteTick=true;
w->writeText(fmt::sprintf(">> TICK %d\n",tick));
}
w->writeText(fmt::sprintf(">> SET_RATE %f\n",curDivider));
}
for (DivCommand& i: cmdStream) {
switch (i.cmd) {
// strip away hinted/useless commands
case DIV_ALWAYS_SET_VOLUME:
break;
case DIV_CMD_GET_VOLUME:
break;
case DIV_CMD_VOLUME:
break;
case DIV_CMD_NOTE_PORTA:
break;
case DIV_CMD_LEGATO:
break;
case DIV_CMD_PITCH:
break;
default:
if (!wroteTick) {
wroteTick=true;
w->writeText(fmt::sprintf(">> TICK %d\n",tick));
}
w->writeText(fmt::sprintf(" %d: %s %d %d\n",i.chan,cmdName[i.cmd],i.value,i.value2));
break;
}
}
cmdStream.clear();
tick++;
}
cmdStreamEnabled=oldCmdStreamEnabled;
if (!playing) {
w->writeText(">> END\n");
} else {
w->writeText(">> LOOP 0\n");
}
remainingLoops=-1;
playing=false;
freelance=false;
extValuePresent=false;
BUSY_END;
return w;
}
void _runExportThread(DivEngine* caller) {
caller->runExportThread();
}

View file

@ -280,6 +280,8 @@ enum DivChanTypes {
DIV_CH_OP=5
};
extern const char* cmdName[];
class DivEngine {
DivDispatchContainer disCont[32];
TAAudio* output;

View file

@ -120,6 +120,9 @@ int SafeWriter::writeWString(WString val, bool pascal) {
return 2+val.size()*2;
}
}
int SafeWriter::writeText(String val) {
return write(val.c_str(),val.size());
}
void SafeWriter::init() {
if (operative) return;

View file

@ -57,6 +57,7 @@ class SafeWriter {
int writeD_BE(double val);
int writeWString(WString val, bool pascal);
int writeString(String val, bool pascal);
int writeText(String val);
void init();
SafeReader* toReader();

View file

@ -1401,6 +1401,17 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale
);
break;
case GUI_FILE_EXPORT_CMDSTREAM:
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Command Stream",
{"text file", "*.txt",
"binary file", "*.bin"},
"text file{.txt},binary file{.bin}",
workingDirROMExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_ROM:
showError("Coming soon!");
break;
@ -2947,6 +2958,19 @@ bool FurnaceGUI::loop() {
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("export command stream...")) {
ImGui::Text(
"this option exports a text or binary file which\n"
"contains a dump of the internal command stream\n"
"produced when playing the song.\n\n"
"technical/development use only!"
);
if (ImGui::Button("export")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::BeginMenu("add system...")) {
for (int j=0; availableSystems[j]; j++) {
@ -3258,9 +3282,12 @@ bool FurnaceGUI::loop() {
workingDirAudioExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_VGM:
case GUI_FILE_EXPORT_ROM:
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_ROM:
case GUI_FILE_EXPORT_CMDSTREAM:
workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_LOAD_MAIN_FONT:
case GUI_FILE_LOAD_PAT_FONT:
workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR;
@ -3325,6 +3352,11 @@ bool FurnaceGUI::loop() {
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
checkExtension(".vgm");
}
if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM) {
// we can't tell whether the user chose .txt or .bin in the system file picker
const char* fallbackExt=(settings.sysFileDialog || ImGuiFileDialog::Instance()->GetCurrentFilter()=="text file")?".txt":".bin";
checkExtensionDual(".txt",".bin",fallbackExt);
}
if (curFileDialog==GUI_FILE_EXPORT_COLORS) {
checkExtension(".cfgc");
}
@ -3506,6 +3538,35 @@ bool FurnaceGUI::loop() {
case GUI_FILE_EXPORT_ROM:
showError("Coming soon!");
break;
case GUI_FILE_EXPORT_CMDSTREAM: {
String lowerCase=fileName;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
bool isBinary=true;
if ((lowerCase.size()<4 || lowerCase.rfind(".txt")!=lowerCase.size()-4)) {
isBinary=false;
}
SafeWriter* w=e->saveCommand(isBinary);
if (w!=NULL) {
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f);
} else {
showError("could not open file!");
}
w->finish();
delete w;
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
} else {
showError(fmt::sprintf("could not write command stream! (%s)",e->getLastError()));
}
break;
}
case GUI_FILE_LOAD_MAIN_FONT:
settings.mainFontPath=copyOfName;
break;
@ -4099,6 +4160,7 @@ bool FurnaceGUI::init() {
workingDirSample=e->getConfString("lastDirSample",workingDir);
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
workingDirFont=e->getConfString("lastDirFont",workingDir);
workingDirColors=e->getConfString("lastDirColors",workingDir);
workingDirKeybinds=e->getConfString("lastDirKeybinds",workingDir);
@ -4339,6 +4401,7 @@ bool FurnaceGUI::finish() {
e->setConf("lastDirSample",workingDirSample);
e->setConf("lastDirAudioExport",workingDirAudioExport);
e->setConf("lastDirVGMExport",workingDirVGMExport);
e->setConf("lastDirROMExport",workingDirROMExport);
e->setConf("lastDirFont",workingDirFont);
e->setConf("lastDirColors",workingDirColors);
e->setConf("lastDirKeybinds",workingDirKeybinds);

View file

@ -266,6 +266,7 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_EXPORT_AUDIO_PER_SYS,
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
GUI_FILE_EXPORT_VGM,
GUI_FILE_EXPORT_CMDSTREAM,
GUI_FILE_EXPORT_ROM,
GUI_FILE_LOAD_MAIN_FONT,
GUI_FILE_LOAD_PAT_FONT,
@ -948,11 +949,14 @@ class FurnaceGUI {
bool updateSampleTex;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout, workingDirROM, workingDirTest;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
String workingDirVGMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
String workingDirLayout, workingDirROM, workingDirTest;
String mmlString[32];
String mmlStringW;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
bool displayPendingIns, pendingInsSingle;
bool willExport[32];