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:
- 71: Furnace dev71
- 70: Furnace dev70
- 69: Furnace dev69
- 68: Furnace dev68
@ -238,7 +239,10 @@ size | description
| this is 2.0f for modules before 59
--- | **extended compatibility flags** (>=70)
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -85,6 +85,18 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
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:");
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {