command stream preset instruments/volumes

This commit is contained in:
tildearrow 2025-11-10 18:27:45 -05:00
parent d3ce1a0b84
commit 92ad27f87e
5 changed files with 200 additions and 38 deletions

View file

@ -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

View file

@ -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; i<fileChans; i++) {

View file

@ -83,10 +83,12 @@ class DivCSPlayer {
SafeReader stream;
DivCSChannelState chan[DIV_MAX_CHANS];
unsigned char fastDelays[16];
unsigned char fastCmds[16];
unsigned char fastIns[6];
unsigned char fastVols[6];
unsigned char fastCmds[4];
unsigned char arpSpeed;
unsigned int fileChans;
unsigned int curTick, fastDelaysOff, fastCmdsOff, deltaCyclePos;
unsigned int curTick, fastDelaysOff, fastInsOff, fastVolsOff, fastCmdsOff, deltaCyclePos;
bool longPointers;
bool bigEndian;
@ -98,6 +100,8 @@ class DivCSPlayer {
DivCSChannelState* getChanState(int ch);
unsigned int getFileChans();
unsigned char* getFastDelays();
unsigned char* getFastIns();
unsigned char* getFastVols();
unsigned char* getFastCmds();
unsigned int getCurTick();
void cleanup();

View file

@ -256,13 +256,14 @@ int DivCS::getInsLength(unsigned char ins, unsigned char ext, unsigned char* spe
case 0xc8: // vol slide
case 0xc9: // porta
return 3;
// speed dial commands
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
return 1;
// speed dial commands
case 0xec: case 0xed: case 0xee: case 0xef:
if (speedDial==NULL) return 0;
return 1+getCmdLength(speedDial[ins&15]);
return 1+getCmdLength(speedDial[ins&3]);
case 0xd0: // opt
return 4;
case 0xd7: // cmd
@ -1257,12 +1258,18 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, DivCSOptions options
int loopRow=curSubSong->ts.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<size_t> 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]<insPopularity[i]) {
sortCand=i;
}
}
}
if (sortCand==-1) break;
sortedInsPopularity[sortPos]=insPopularity[sortCand];
sortedIns[sortPos]=sortCand;
insPopularity[sortCand]=0;
sortPos++;
}
// set preset instruments
for (int h=0; h<chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
for (size_t i=0; i<chanStream[h]->size(); 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]<volPopularity[i]) {
sortCand=i;
}
}
}
if (sortCand==-1) break;
sortedVolPopularity[sortPos]=volPopularity[sortCand];
sortedVol[sortPos]=sortCand;
volPopularity[sortCand]=0;
sortPos++;
}
// set preset volumes
for (int h=0; h<chans; h++) {
unsigned char* buf=chanStream[h]->getFinalBuf();
for (size_t i=0; i<chanStream[h]->size(); 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; i<chanStream[h]->size(); 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]);
}

View file

@ -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,(cmd<DIV_CMD_MAX)?cmdName[cmd]:"INVALID");
String ret=fmt::sprintf("qcmd%d %s",buf[addr]-0xec,(cmd<DIV_CMD_MAX)?cmdName[cmd]:"INVALID");
for (int i=0; i<cmdLen; i++) {
ret+=fmt::sprintf(", %.2x",buf[addr+1+i]);
}
@ -325,7 +328,7 @@ void FurnaceGUI::drawCSPlayer() {
if (state->trace[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]);
}