diff --git a/papers/format.md b/papers/format.md index fc954ba05..d61e94d43 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 72: Furnace dev72 - 71: Furnace dev71 - 70: Furnace dev70 - 69: Furnace dev69 @@ -242,7 +243,9 @@ size | description 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 + 1 | buggy portamento after slide (>=72) or reserved + 1 | new ins affects envelope (Game Boy) (>=72) or reserved + 26 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 52d400ca0..24c3e1352 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -42,8 +42,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev71" -#define DIV_ENGINE_VERSION 71 +#define DIV_VERSION "dev72" +#define DIV_ENGINE_VERSION 72 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 4697d338a..eca105a39 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -148,6 +148,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.noSlidesOnFirstTick=false; ds.rowResetsArpPos=false; ds.ignoreJumpAtEnd=true; + ds.buggyPortaAfterSlide=true; + ds.gbInsAffectsEnvelope=true; // 1.1 compat flags if (ds.version>24) { @@ -833,6 +835,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.rowResetsArpPos=false; ds.ignoreJumpAtEnd=true; } + if (ds.version<72) { + ds.buggyPortaAfterSlide=true; + ds.gbInsAffectsEnvelope=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1080,15 +1086,22 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { // extended compat flags ds.brokenSpeedSel=reader.readC(); if (ds.version>=71) { - song.noSlidesOnFirstTick=reader.readC(); - song.rowResetsArpPos=reader.readC(); - song.ignoreJumpAtEnd=reader.readC(); + ds.noSlidesOnFirstTick=reader.readC(); + ds.rowResetsArpPos=reader.readC(); + ds.ignoreJumpAtEnd=reader.readC(); } else { reader.readC(); reader.readC(); reader.readC(); } - for (int i=0; i<28; i++) { + if (ds.version>=72) { + ds.buggyPortaAfterSlide=reader.readC(); + ds.gbInsAffectsEnvelope=reader.readC(); + } else { + reader.readC(); + reader.readC(); + } + for (int i=0; i<26; i++) { reader.readC(); } } @@ -1939,7 +1952,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.noSlidesOnFirstTick); w->writeC(song.rowResetsArpPos); w->writeC(song.ignoreJumpAtEnd); - for (int i=0; i<28; i++) { + w->writeC(song.buggyPortaAfterSlide); + w->writeC(song.gbInsAffectsEnvelope); + for (int i=0; i<26; i++) { w->writeC(0); } diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index d25cc2118..d1d580c84 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -288,8 +288,11 @@ int DivPlatformGB::dispatch(DivCommand c) { if (chan[c.chan].ins!=c.value || c.value2==1) { chan[c.chan].ins=c.value; if (c.chan!=2) { - chan[c.chan].vol=parent->getIns(chan[c.chan].ins)->gb.envVol; - // TODO: also change envelope values + DivInstrument* ins=parent->getIns(chan[c.chan].ins); + chan[c.chan].vol=ins->gb.envVol; + if (parent->song.gbInsAffectsEnvelope) { + rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3)); + } } } break; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 8042e2cde..71fd58ddd 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -882,7 +882,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { - if (chan[i].note==chan[i].oldNote && !chan[i].inPorta) { + if (chan[i].note==chan[i].oldNote && !chan[i].inPorta && song.buggyPortaAfterSlide) { chan[i].portaNote=chan[i].note; chan[i].portaSpeed=-1; } else { diff --git a/src/engine/song.h b/src/engine/song.h index 19a89cfa4..9142636e3 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -305,6 +305,8 @@ struct DivSong { bool noSlidesOnFirstTick; bool rowResetsArpPos; bool ignoreJumpAtEnd; + bool buggyPortaAfterSlide; + bool gbInsAffectsEnvelope; DivOrders orders; std::vector ins; @@ -381,7 +383,9 @@ struct DivSong { brokenSpeedSel(false), noSlidesOnFirstTick(false), rowResetsArpPos(false), - ignoreJumpAtEnd(false) { + ignoreJumpAtEnd(false), + buggyPortaAfterSlide(false), + gbInsAffectsEnvelope(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index ca0538ea6..91aa34c7e 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -97,6 +97,14 @@ void FurnaceGUI::drawCompatFlags() { 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::Checkbox("Buggy portamento after pitch slide",&e->song.buggyPortaAfterSlide); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding."); + } + ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {