From 92ad27f87ef88e006e547b0a082482dc726cc13b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 10 Nov 2025 18:27:45 -0500 Subject: [PATCH] command stream preset instruments/volumes --- papers/export-tech.md | 26 +++++-- src/engine/cmdStream.cpp | 34 ++++++--- src/engine/cmdStream.h | 8 ++- src/engine/cmdStreamOps.cpp | 139 ++++++++++++++++++++++++++++++++---- src/gui/csPlayer.cpp | 31 +++++--- 5 files changed, 200 insertions(+), 38 deletions(-) diff --git a/papers/export-tech.md b/papers/export-tech.md index 309d8e6bb..21a2bbff9 100644 --- a/papers/export-tech.md +++ b/papers/export-tech.md @@ -27,8 +27,12 @@ size | description 1 | reserved 1?? | preset delays | - 16 values + 1?? | preset instruments + | - 6 values + 1?? | preset volumes + | - 6 values 1?? | speed dial commands - | - 16 values + | - 4 values ??? | pointers to channel data | - pointers are short (2-byte) or long (4-byte), set in flags 1?? | maximum stack size per channel @@ -115,10 +119,22 @@ hex | description de | wait one tick df | stop ----|------------------------------------ - e0 | speed dial command 0 - e1 | speed dial command 1 - .. | ... - ef | speed dial command 15 + e0 | preset instrument 0 + e1 | preset instrument 1 + e2 | preset instrument 2 + e3 | preset instrument 3 + e4 | preset instrument 4 + e5 | preset instrument 5 + e6 | preset volume 0 + e7 | preset volume 1 + e8 | preset volume 2 + e9 | preset volume 3 + ea | preset volume 4 + eb | preset volume 5 + ec | speed dial command 0 + ed | speed dial command 1 + ee | speed dial command 2 + ef | speed dial command 3 ----|------------------------------------ f0 | preset delay 0 f1 | preset delay 1 diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index f2650aa59..568f5ee0c 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -59,6 +59,14 @@ unsigned char* DivCSPlayer::getFastDelays() { return fastDelays; } +unsigned char* DivCSPlayer::getFastIns() { + return fastIns; +} + +unsigned char* DivCSPlayer::getFastVols() { + return fastVols; +} + unsigned char* DivCSPlayer::getFastCmds() { return fastCmds; } @@ -110,9 +118,6 @@ bool DivCSPlayer::tick() { e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,(int)next-60)); chan[i].note=(int)next-60; chan[i].vibratoPos=0; - } else if (next>=0xe0 && next<=0xef) { - command=fastCmds[next&15]; - bAccessTS[fastCmdsOff+(next&15)]=curTick; } else if (next>=0xf0) { // preset delay chan[i].waitTicks=fastDelays[next&15]; chan[i].lastWaitLen=chan[i].waitTicks; @@ -205,13 +210,18 @@ bool DivCSPlayer::tick() { e->dispatchCmd(DivCommand(DIV_CMD_PANNING,i,panL,panR)); break; } - case 0xe0: case 0xe1: case 0xe2: case 0xe3: - case 0xe4: case 0xe5: case 0xe6: case 0xe7: - case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: + e->dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,fastIns[next-0xe0])); + bAccessTS[fastInsOff+(next-0xe0)]=curTick; + break; + case 0xe6: case 0xe7: case 0xe8: case 0xe9: case 0xea: case 0xeb: + chan[i].volume=fastVols[next-0xe6]<<8; + bAccessTS[fastVolsOff+(next-0xe6)]=curTick; + sendVolume=true; + break; case 0xec: case 0xed: case 0xee: case 0xef: - // TODO: remove as it has no effect - command=fastCmds[next&15]; - + command=fastCmds[next&3]; + bAccessTS[fastCmdsOff+(next&3)]=curTick; break; case 0xd0: // placeholder stream.readC(); @@ -644,8 +654,12 @@ bool DivCSPlayer::init() { fastDelaysOff=stream.tell(); stream.read(fastDelays,16); + fastInsOff=stream.tell(); + stream.read(fastIns,6); + fastVolsOff=stream.tell(); + stream.read(fastVols,6); fastCmdsOff=stream.tell(); - stream.read(fastCmds,16); + stream.read(fastCmds,4); if (longPointers) { for (unsigned int i=0; its.loopStart.row; logI("loop point: %d %d",loopOrder,loopRow); + int insPopularity[256]; + int volPopularity[256]; int cmdPopularity[256]; int delayPopularity[256]; - int sortedCmdPopularity[16]; + int sortedInsPopularity[6]; + int sortedVolPopularity[6]; + int sortedCmdPopularity[4]; int sortedDelayPopularity[16]; - unsigned char sortedCmd[16]; + unsigned char sortedIns[6]; + unsigned char sortedVol[6]; + unsigned char sortedCmd[4]; unsigned char sortedDelay[16]; SafeWriter* globalStream; @@ -1272,14 +1279,20 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options std::vector tickPos[DIV_MAX_CHANS]; int loopTick=-1; + memset(insPopularity,0,256*sizeof(int)); + memset(volPopularity,0,256*sizeof(int)); memset(cmdPopularity,0,256*sizeof(int)); memset(delayPopularity,0,256*sizeof(int)); memset(chanStream,0,DIV_MAX_CHANS*sizeof(void*)); memset(chanStreamOff,0,DIV_MAX_CHANS*sizeof(unsigned int)); memset(chanStackSize,0,DIV_MAX_CHANS*sizeof(unsigned int)); - memset(sortedCmdPopularity,0,16*sizeof(int)); + memset(sortedInsPopularity,0,6*sizeof(int)); + memset(sortedVolPopularity,0,6*sizeof(int)); + memset(sortedCmdPopularity,0,4*sizeof(int)); memset(sortedDelayPopularity,0,16*sizeof(int)); - memset(sortedCmd,0,16); + memset(sortedIns,0,6); + memset(sortedVol,0,6); + memset(sortedCmd,0,4); memset(sortedDelay,0,16); SafeWriter* w=new SafeWriter; @@ -1386,6 +1399,11 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options case DIV_CMD_PRE_NOTE: break; default: + if (i.cmd==DIV_CMD_HINT_VOLUME) { + volPopularity[i.value&0xff]++; + } else if (i.cmd==DIV_CMD_INSTRUMENT) { + insPopularity[i.value&0xff]++; + } cmdPopularity[i.cmd]++; writeCommandValues(chanStream[i.chan],i,options.bigEndian); break; @@ -1450,12 +1468,97 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options extValuePresent=false; BUSY_END; - // PASS 1: optimize command calls + // PASS 1: optimize command calls and volume/instrument changes if (!options.noCmdCallOpt) { - // calculate command usage + /// 1. instruments + // calculate instrument usage int sortCand=-1; int sortPos=0; - while (sortPos<16) { + while (sortPos<6) { + sortCand=-1; + for (int i=0; i<256; i++) { + if (insPopularity[i]) { + if (sortCand==-1) { + sortCand=i; + } else if (insPopularity[sortCand]getFinalBuf(); + for (size_t i=0; isize(); i+=8) { + if (buf[i]==0xb8) { + // find whether this instrument is preset + for (int j=0; j<6; j++) { + if (buf[i+1]==sortedIns[j]) { + buf[i]=0xe0+j; + for (int k=i+1; k<(int)i+8; k++) { + buf[k]=0; + } + break; + } + } + } + } + } + + /// 2. volumes + // calculate volume usage + sortCand=-1; + sortPos=0; + while (sortPos<6) { + sortCand=-1; + for (int i=0; i<256; i++) { + if (volPopularity[i]) { + if (sortCand==-1) { + sortCand=i; + } else if (volPopularity[sortCand]getFinalBuf(); + for (size_t i=0; isize(); i+=8) { + if (buf[i]==0xc7) { + // find whether this volume is preset + for (int j=0; j<6; j++) { + if (buf[i+1]==sortedVol[j]) { + buf[i]=0xe6+j; + for (int k=i+1; k<(int)i+8; k++) { + buf[k]=0; + } + break; + } + } + } + } + } + + /// 3. commands + // calculate command usage + sortCand=-1; + sortPos=0; + while (sortPos<4) { sortCand=-1; for (int i=DIV_CMD_SAMPLE_MODE; i<256; i++) { if (cmdPopularity[i]) { @@ -1480,9 +1583,9 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options for (size_t i=0; isize(); i+=8) { if (buf[i]==0xd7) { // find whether this command is in speed dial - for (int j=0; j<16; j++) { + for (int j=0; j<4; j++) { if (buf[i+1]==sortedCmd[j]) { - buf[i]=0xe0+j; + buf[i]=0xec+j; // move everything to the left for (int k=i+2; k<(int)i+8; k++) { buf[k-1]=buf[k]; @@ -1795,8 +1898,20 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options if (sortedDelayPopularity[i]) logD("- %d: %d",sortedDelay[i],sortedDelayPopularity[i]); } + logD("instrument popularity:"); + for (int i=0; i<6; i++) { + w->writeC(sortedIns[i]); + logD("- $%.2x: %d",sortedIns[i],sortedInsPopularity[i]); + } + + logD("volume popularity:"); + for (int i=0; i<6; i++) { + w->writeC(sortedVol[i]); + logD("- $%.2x: %d",sortedVol[i],sortedVolPopularity[i]); + } + logD("command popularity:"); - for (int i=0; i<16; i++) { + for (int i=0; i<4; i++) { w->writeC(sortedCmd[i]); if (sortedCmdPopularity[i]) logD("- %s ($%.2x): %d",cmdName[sortedCmd[i]],sortedCmd[i],sortedCmdPopularity[i]); } diff --git a/src/gui/csPlayer.cpp b/src/gui/csPlayer.cpp index e438a4d60..4a4578be8 100644 --- a/src/gui/csPlayer.cpp +++ b/src/gui/csPlayer.cpp @@ -22,7 +22,7 @@ #include "imgui.h" #include "guiConst.h" -String disasmCmd(unsigned char* buf, size_t bufLen, unsigned int addr, unsigned char* speedDial) { +String disasmCmd(unsigned char* buf, size_t bufLen, unsigned int addr, unsigned char* fastIns, unsigned char* fastVols, unsigned char* fastCmds) { if (addr>=bufLen) return "???"; if (buf[addr]<0xb4) { @@ -104,14 +104,17 @@ String disasmCmd(unsigned char* buf, size_t bufLen, unsigned int addr, unsigned if (addr+2>=bufLen) return "???"; return fmt::sprintf("pan $%x, $%x",(int)buf[addr+1],(int)buf[addr+2]); break; - case 0xe0: case 0xe1: case 0xe2: case 0xe3: - case 0xe4: case 0xe5: case 0xe6: case 0xe7: - case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: + return fmt::sprintf("qins%d $%.2x",buf[addr]-0xe0,fastIns[buf[addr]-0xe0]); + break; + case 0xe6: case 0xe7: case 0xe8: case 0xe9: case 0xea: case 0xeb: + return fmt::sprintf("qvol%d $%.2x",buf[addr]-0xe6,fastVols[buf[addr]-0xe6]); + break; case 0xec: case 0xed: case 0xee: case 0xef: { - unsigned char cmd=speedDial[buf[addr]&15]; + unsigned char cmd=fastCmds[buf[addr]&3]; int cmdLen=DivCS::getCmdLength(cmd); if ((addr+cmdLen)>=bufLen) return "???"; - String ret=fmt::sprintf("qcmd%d %s",buf[addr]-0xd0,(cmdtrace[j]==0) { ImGui::TextUnformatted("..."); } else { - String dis=disasmCmd(buf,bufSize,state->trace[j],cs->getFastCmds()); + String dis=disasmCmd(buf,bufSize,state->trace[j],cs->getFastIns(),cs->getFastVols(),cs->getFastCmds()); ImGui::Text("%.4x: %s",state->trace[j],dis.c_str()); } } @@ -400,7 +403,7 @@ void FurnaceGUI::drawCSPlayer() { } ImGui::TableNextColumn(); - String dis=disasmCmd(i.data,8,0,cs->getFastCmds()); + String dis=disasmCmd(i.data,8,0,cs->getFastIns(),cs->getFastVols(),cs->getFastCmds()); ImGui::Text("%s",dis.c_str()); // jmp/ret separator @@ -599,8 +602,18 @@ void FurnaceGUI::drawCSPlayer() { ImGui::SameLine(); ImGui::Text("%d",cs->getFastDelays()[i]); } + ImGui::Text("preset instruments:"); + for (int i=0; i<6; i++) { + ImGui::SameLine(); + ImGui::Text("%d",cs->getFastIns()[i]); + } + ImGui::Text("preset volumes:"); + for (int i=0; i<6; i++) { + ImGui::SameLine(); + ImGui::Text("%d",cs->getFastVols()[i]); + } ImGui::Text("speed dial commands:"); - for (int i=0; i<16; i++) { + for (int i=0; i<4; i++) { ImGui::SameLine(); ImGui::Text("%d",cs->getFastCmds()[i]); }