add a command stream dump option
This commit is contained in:
parent
a0d10aa60b
commit
d54d853ff8
|
@ -17,6 +17,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "dispatch.h"
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "instrument.h"
|
#include "instrument.h"
|
||||||
|
@ -234,6 +235,111 @@ double DivEngine::benchmarkSeek() {
|
||||||
return tAvg;
|
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) {
|
void _runExportThread(DivEngine* caller) {
|
||||||
caller->runExportThread();
|
caller->runExportThread();
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,8 @@ enum DivChanTypes {
|
||||||
DIV_CH_OP=5
|
DIV_CH_OP=5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char* cmdName[];
|
||||||
|
|
||||||
class DivEngine {
|
class DivEngine {
|
||||||
DivDispatchContainer disCont[32];
|
DivDispatchContainer disCont[32];
|
||||||
TAAudio* output;
|
TAAudio* output;
|
||||||
|
|
|
@ -120,6 +120,9 @@ int SafeWriter::writeWString(WString val, bool pascal) {
|
||||||
return 2+val.size()*2;
|
return 2+val.size()*2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int SafeWriter::writeText(String val) {
|
||||||
|
return write(val.c_str(),val.size());
|
||||||
|
}
|
||||||
|
|
||||||
void SafeWriter::init() {
|
void SafeWriter::init() {
|
||||||
if (operative) return;
|
if (operative) return;
|
||||||
|
|
|
@ -57,6 +57,7 @@ class SafeWriter {
|
||||||
int writeD_BE(double val);
|
int writeD_BE(double val);
|
||||||
int writeWString(WString val, bool pascal);
|
int writeWString(WString val, bool pascal);
|
||||||
int writeString(String val, bool pascal);
|
int writeString(String val, bool pascal);
|
||||||
|
int writeText(String val);
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
SafeReader* toReader();
|
SafeReader* toReader();
|
||||||
|
|
|
@ -1401,6 +1401,17 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
dpiScale
|
dpiScale
|
||||||
);
|
);
|
||||||
break;
|
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:
|
case GUI_FILE_EXPORT_ROM:
|
||||||
showError("Coming soon!");
|
showError("Coming soon!");
|
||||||
break;
|
break;
|
||||||
|
@ -2947,6 +2958,19 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
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();
|
ImGui::Separator();
|
||||||
if (ImGui::BeginMenu("add system...")) {
|
if (ImGui::BeginMenu("add system...")) {
|
||||||
for (int j=0; availableSystems[j]; j++) {
|
for (int j=0; availableSystems[j]; j++) {
|
||||||
|
@ -3258,9 +3282,12 @@ bool FurnaceGUI::loop() {
|
||||||
workingDirAudioExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirAudioExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
break;
|
break;
|
||||||
case GUI_FILE_EXPORT_VGM:
|
case GUI_FILE_EXPORT_VGM:
|
||||||
case GUI_FILE_EXPORT_ROM:
|
|
||||||
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
break;
|
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_MAIN_FONT:
|
||||||
case GUI_FILE_LOAD_PAT_FONT:
|
case GUI_FILE_LOAD_PAT_FONT:
|
||||||
workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR;
|
||||||
|
@ -3325,6 +3352,11 @@ bool FurnaceGUI::loop() {
|
||||||
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
|
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
|
||||||
checkExtension(".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) {
|
if (curFileDialog==GUI_FILE_EXPORT_COLORS) {
|
||||||
checkExtension(".cfgc");
|
checkExtension(".cfgc");
|
||||||
}
|
}
|
||||||
|
@ -3506,6 +3538,35 @@ bool FurnaceGUI::loop() {
|
||||||
case GUI_FILE_EXPORT_ROM:
|
case GUI_FILE_EXPORT_ROM:
|
||||||
showError("Coming soon!");
|
showError("Coming soon!");
|
||||||
break;
|
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:
|
case GUI_FILE_LOAD_MAIN_FONT:
|
||||||
settings.mainFontPath=copyOfName;
|
settings.mainFontPath=copyOfName;
|
||||||
break;
|
break;
|
||||||
|
@ -4099,6 +4160,7 @@ bool FurnaceGUI::init() {
|
||||||
workingDirSample=e->getConfString("lastDirSample",workingDir);
|
workingDirSample=e->getConfString("lastDirSample",workingDir);
|
||||||
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
|
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
|
||||||
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
|
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
|
||||||
|
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
|
||||||
workingDirFont=e->getConfString("lastDirFont",workingDir);
|
workingDirFont=e->getConfString("lastDirFont",workingDir);
|
||||||
workingDirColors=e->getConfString("lastDirColors",workingDir);
|
workingDirColors=e->getConfString("lastDirColors",workingDir);
|
||||||
workingDirKeybinds=e->getConfString("lastDirKeybinds",workingDir);
|
workingDirKeybinds=e->getConfString("lastDirKeybinds",workingDir);
|
||||||
|
@ -4339,6 +4401,7 @@ bool FurnaceGUI::finish() {
|
||||||
e->setConf("lastDirSample",workingDirSample);
|
e->setConf("lastDirSample",workingDirSample);
|
||||||
e->setConf("lastDirAudioExport",workingDirAudioExport);
|
e->setConf("lastDirAudioExport",workingDirAudioExport);
|
||||||
e->setConf("lastDirVGMExport",workingDirVGMExport);
|
e->setConf("lastDirVGMExport",workingDirVGMExport);
|
||||||
|
e->setConf("lastDirROMExport",workingDirROMExport);
|
||||||
e->setConf("lastDirFont",workingDirFont);
|
e->setConf("lastDirFont",workingDirFont);
|
||||||
e->setConf("lastDirColors",workingDirColors);
|
e->setConf("lastDirColors",workingDirColors);
|
||||||
e->setConf("lastDirKeybinds",workingDirKeybinds);
|
e->setConf("lastDirKeybinds",workingDirKeybinds);
|
||||||
|
|
|
@ -266,6 +266,7 @@ enum FurnaceGUIFileDialogs {
|
||||||
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
||||||
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
||||||
GUI_FILE_EXPORT_VGM,
|
GUI_FILE_EXPORT_VGM,
|
||||||
|
GUI_FILE_EXPORT_CMDSTREAM,
|
||||||
GUI_FILE_EXPORT_ROM,
|
GUI_FILE_EXPORT_ROM,
|
||||||
GUI_FILE_LOAD_MAIN_FONT,
|
GUI_FILE_LOAD_MAIN_FONT,
|
||||||
GUI_FILE_LOAD_PAT_FONT,
|
GUI_FILE_LOAD_PAT_FONT,
|
||||||
|
@ -948,11 +949,14 @@ class FurnaceGUI {
|
||||||
bool updateSampleTex;
|
bool updateSampleTex;
|
||||||
|
|
||||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile;
|
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 mmlString[32];
|
||||||
String mmlStringW;
|
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 displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
|
||||||
bool displayPendingIns, pendingInsSingle;
|
bool displayPendingIns, pendingInsSingle;
|
||||||
bool willExport[32];
|
bool willExport[32];
|
||||||
|
|
Loading…
Reference in a new issue