dev71 - more compatibility flags for .mod

This commit is contained in:
tildearrow 2022-03-23 23:56:59 -05:00
parent ece34990e5
commit bd36a4ffdc
7 changed files with 89 additions and 31 deletions

View file

@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res
the format versions are: the format versions are:
- 71: Furnace dev71
- 70: Furnace dev70 - 70: Furnace dev70
- 69: Furnace dev69 - 69: Furnace dev69
- 68: Furnace dev68 - 68: Furnace dev68
@ -238,7 +239,10 @@ size | description
| this is 2.0f for modules before 59 | this is 2.0f for modules before 59
--- | **extended compatibility flags** (>=70) --- | **extended compatibility flags** (>=70)
1 | broken speed selection 1 | broken speed selection
31 | reserved 1 | no slides on first tick (>=71) or reserved
1 | next row reset arp pos (>=71) or reserved
1 | ignore jump at end (>=71) or reserved
28 | reserved
``` ```
# instrument # instrument

View file

@ -144,7 +144,7 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) {
effectVal=pat[k]->data[j][5+(l<<1)]; effectVal=pat[k]->data[j][5+(l<<1)];
if (effectVal<0) effectVal=0; if (effectVal<0) effectVal=0;
if (pat[k]->data[j][4+(l<<1)]==0x0d) { if (pat[k]->data[j][4+(l<<1)]==0x0d) {
if (nextOrder==-1 && i<song.ordersLen-1) { if (nextOrder==-1 && (i<song.ordersLen-1 || !song.ignoreJumpAtEnd)) {
nextOrder=i+1; nextOrder=i+1;
nextRow=effectVal; nextRow=effectVal;
} }
@ -957,6 +957,7 @@ void DivEngine::reset() {
extValuePresent=0; extValuePresent=0;
speed1=song.speed1; speed1=song.speed1;
speed2=song.speed2; speed2=song.speed2;
firstTick=false;
nextSpeed=speed1; nextSpeed=speed1;
divider=60; divider=60;
if (song.customTempo) { if (song.customTempo) {

View file

@ -42,8 +42,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev70" #define DIV_VERSION "dev71"
#define DIV_ENGINE_VERSION 70 #define DIV_ENGINE_VERSION 71
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
@ -190,6 +190,7 @@ class DivEngine {
bool forceMono; bool forceMono;
bool cmdStreamEnabled; bool cmdStreamEnabled;
bool softLocked; bool softLocked;
bool firstTick;
int softLockCount; int softLockCount;
int ticks, curRow, curOrder, remainingLoops, nextSpeed; int ticks, curRow, curOrder, remainingLoops, nextSpeed;
double divider; double divider;
@ -682,7 +683,8 @@ class DivEngine {
halted(false), halted(false),
forceMono(false), forceMono(false),
cmdStreamEnabled(false), cmdStreamEnabled(false),
softLocked(0), softLocked(false),
firstTick(false),
softLockCount(0), softLockCount(0),
ticks(0), ticks(0),
curRow(0), curRow(0),

View file

@ -145,6 +145,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.newInsTriggersInPorta=true; ds.newInsTriggersInPorta=true;
ds.arp0Reset=true; ds.arp0Reset=true;
ds.brokenSpeedSel=true; ds.brokenSpeedSel=true;
ds.noSlidesOnFirstTick=false;
ds.rowResetsArpPos=false;
ds.ignoreJumpAtEnd=true;
// 1.1 compat flags // 1.1 compat flags
if (ds.version>24) { if (ds.version>24) {
@ -825,6 +828,11 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<69) { if (ds.version<69) {
ds.arp0Reset=false; ds.arp0Reset=false;
} }
if (ds.version<71) {
ds.noSlidesOnFirstTick=false;
ds.rowResetsArpPos=false;
ds.ignoreJumpAtEnd=true;
}
ds.isDMF=false; ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
@ -1071,7 +1079,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version>=70) { if (ds.version>=70) {
// extended compat flags // extended compat flags
ds.brokenSpeedSel=reader.readC(); ds.brokenSpeedSel=reader.readC();
for (int i=0; i<31; i++) { if (ds.version>=71) {
song.noSlidesOnFirstTick=reader.readC();
song.rowResetsArpPos=reader.readC();
song.ignoreJumpAtEnd=reader.readC();
} else {
reader.readC();
reader.readC();
reader.readC();
}
for (int i=0; i<28; i++) {
reader.readC(); reader.readC();
} }
} }
@ -1282,6 +1299,9 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
DivSong ds; DivSong ds;
ds.tuning=436.0; ds.tuning=436.0;
ds.version=DIV_VERSION_MOD; ds.version=DIV_VERSION_MOD;
ds.noSlidesOnFirstTick=true;
ds.rowResetsArpPos=true;
ds.ignoreJumpAtEnd=false;
int insCount=31; int insCount=31;
bool bypassLimits=false; bool bypassLimits=false;
@ -1916,7 +1936,10 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
// extended compat flags // extended compat flags
w->writeC(song.brokenSpeedSel); w->writeC(song.brokenSpeedSel);
for (int i=0; i<31; i++) { w->writeC(song.noSlidesOnFirstTick);
w->writeC(song.rowResetsArpPos);
w->writeC(song.ignoreJumpAtEnd);
for (int i=0; i<28; i++) {
w->writeC(0); w->writeC(0);
} }

View file

@ -821,7 +821,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
} }
break; break;
case 0x0d: // next order case 0x0d: // next order
if (changeOrd<0 && curOrder<(song.ordersLen-1)) { if (changeOrd<0 && (curOrder<(song.ordersLen-1) || !song.ignoreJumpAtEnd)) {
changeOrd=-2; changeOrd=-2;
changePos=effectVal; changePos=effectVal;
} }
@ -1229,6 +1229,7 @@ void DivEngine::nextRow() {
} }
if (haltOn==DIV_HALT_ROW) halted=true; if (haltOn==DIV_HALT_ROW) halted=true;
firstTick=true;
} }
bool DivEngine::nextTick(bool noAccum) { bool DivEngine::nextTick(bool noAccum) {
@ -1281,23 +1282,25 @@ bool DivEngine::nextTick(bool noAccum) {
keyHit[i]=true; keyHit[i]=true;
} }
} }
if (chan[i].volSpeed!=0) { if (!song.noSlidesOnFirstTick || !firstTick) {
chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); if (chan[i].volSpeed!=0) {
chan[i].volume+=chan[i].volSpeed; chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8);
if (chan[i].volume>chan[i].volMax) { chan[i].volume+=chan[i].volSpeed;
chan[i].volume=chan[i].volMax; if (chan[i].volume>chan[i].volMax) {
chan[i].volSpeed=0; chan[i].volume=chan[i].volMax;
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); chan[i].volSpeed=0;
} else if (chan[i].volume<0) { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
chan[i].volSpeed=0; } else if (chan[i].volume<0) {
if (song.legacyVolumeSlides) { chan[i].volSpeed=0;
chan[i].volume=chan[i].volMax+1; if (song.legacyVolumeSlides) {
chan[i].volume=chan[i].volMax+1;
} else {
chan[i].volume=0;
}
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} else { } else {
chan[i].volume=0; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} }
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} else {
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
} }
} }
if (chan[i].vibratoDepth>0) { if (chan[i].vibratoDepth>0) {
@ -1315,13 +1318,15 @@ bool DivEngine::nextTick(bool noAccum) {
break; break;
} }
} }
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { if (!song.noSlidesOnFirstTick || !firstTick) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
chan[i].portaSpeed=0; if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
chan[i].oldNote=chan[i].note; chan[i].portaSpeed=0;
chan[i].note=chan[i].portaNote; chan[i].oldNote=chan[i].note;
chan[i].inPorta=false; chan[i].note=chan[i].portaNote;
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); chan[i].inPorta=false;
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
}
} }
} }
if (chan[i].cut>0) { if (chan[i].cut>0) {
@ -1354,6 +1359,9 @@ bool DivEngine::nextTick(bool noAccum) {
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
chan[i].resetArp=false; chan[i].resetArp=false;
} }
if (song.rowResetsArpPos && firstTick) {
chan[i].arpStage=-1;
}
if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) { if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) {
if (--chan[i].arpTicks<1) { if (--chan[i].arpTicks<1) {
chan[i].arpTicks=song.arpLen; chan[i].arpTicks=song.arpLen;
@ -1377,6 +1385,8 @@ bool DivEngine::nextTick(bool noAccum) {
} }
} }
firstTick=false;
// system tick // system tick
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->tick(); for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->tick();

View file

@ -302,6 +302,9 @@ struct DivSong {
bool newInsTriggersInPorta; bool newInsTriggersInPorta;
bool arp0Reset; bool arp0Reset;
bool brokenSpeedSel; bool brokenSpeedSel;
bool noSlidesOnFirstTick;
bool rowResetsArpPos;
bool ignoreJumpAtEnd;
DivOrders orders; DivOrders orders;
std::vector<DivInstrument*> ins; std::vector<DivInstrument*> ins;
@ -375,7 +378,10 @@ struct DivSong {
oneTickCut(false), oneTickCut(false),
newInsTriggersInPorta(true), newInsTriggersInPorta(true),
arp0Reset(true), arp0Reset(true),
brokenSpeedSel(false) { brokenSpeedSel(false),
noSlidesOnFirstTick(false),
rowResetsArpPos(false),
ignoreJumpAtEnd(false) {
for (int i=0; i<32; i++) { for (int i=0; i<32; i++) {
system[i]=DIV_SYSTEM_NULL; system[i]=DIV_SYSTEM_NULL;
systemVol[i]=64; systemVol[i]=64;

View file

@ -85,6 +85,18 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("determines next speed based on whether the row is odd/even instead of alternating between speeds."); ImGui::SetTooltip("determines next speed based on whether the row is odd/even instead of alternating between speeds.");
} }
ImGui::Checkbox("Don't slide on the first tick of a row",&e->song.noSlidesOnFirstTick);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("simulates ProTracker's behavior of not applying volume/pitch slides on the first tick of a row.");
}
ImGui::Checkbox("Reset arpeggio position on row change",&e->song.rowResetsArpPos);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("simulates ProTracker's behavior of arpeggio being bound to the current tick of a row.");
}
ImGui::Checkbox("Ignore 0Dxx on the last order",&e->song.ignoreJumpAtEnd);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("if this is on, a jump to next row effect will not take place when it is on the last order of a song.");
}
ImGui::Text("Loop modality:"); ImGui::Text("Loop modality:");
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {