diff --git a/papers/format.md b/papers/format.md index c4d9d8a6..cfbe256a 100644 --- a/papers/format.md +++ b/papers/format.md @@ -25,6 +25,9 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 50: Furnace 0.5.7pre2 +- 49: Furnace 0.5.7pre1 +- 48: Furnace 0.5.6 - 47: Furnace 0.5.6pre1 - 46: Furnace 0.5.5 - 45: Furnace 0.5.5pre3 @@ -166,7 +169,8 @@ size | description 1 | arpeggio inhibits portamento (>=47) or reserved 1 | wack algorithm macro (>=47) or reserved 1 | broken shortcut slides (>=49) or reserved - 8 | reserved + 1 | ignore duplicate slides (>=50) or reserved + 6 | reserved 4?? | pointers to instruments 4?? | pointers to wavetables 4?? | pointers to samples diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 86a5be35..6d2a3132 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1565,6 +1565,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.arpNonPorta=false; ds.algMacroBehavior=false; ds.brokenShortcutSlides=false; + ds.ignoreDuplicateSlides=true; // Neo Geo detune if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT) { @@ -2117,6 +2118,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<49) { ds.brokenShortcutSlides=true; } + if (ds.version<50) { + ds.ignoreDuplicateSlides=false; + } reader.readS(); // reserved int infoSeek=reader.readI(); @@ -2239,7 +2243,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<7; i++) reader.readC(); + if (ds.version>=50) { + ds.ignoreDuplicateSlides=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<6; i++) reader.readC(); } else { for (int i=0; i<20; i++) reader.readC(); } @@ -2623,7 +2632,8 @@ SafeWriter* DivEngine::saveFur() { w->writeC(song.arpNonPorta); w->writeC(song.algMacroBehavior); w->writeC(song.brokenShortcutSlides); - for (int i=0; i<7; i++) { + w->writeC(song.ignoreDuplicateSlides); + for (int i=0; i<6; i++) { w->writeC(0); } diff --git a/src/engine/engine.h b/src/engine/engine.h index a1d4b0d3..44a05ca0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -30,8 +30,8 @@ #include #include -#define DIV_VERSION "0.5.7pre1" -#define DIV_ENGINE_VERSION 49 +#define DIV_VERSION "0.5.7pre2" +#define DIV_ENGINE_VERSION 50 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 7d27edf2..63d3ab69 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -552,6 +552,8 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].retrigSpeed=0; + short lastSlide=-1; + // effects for (int j=0; jdata[whatRow][4+(j<<1)]; @@ -583,6 +585,8 @@ void DivEngine::processRow(int i, bool afterDelay) { dispatchCmd(DivCommand(DIV_CMD_PANNING,i,effectVal)); break; case 0x01: // ramp up + if (song.ignoreDuplicateSlides && lastSlide==0x01) break; + lastSlide=0x01; if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; @@ -600,6 +604,8 @@ void DivEngine::processRow(int i, bool afterDelay) { } break; case 0x02: // ramp down + if (song.ignoreDuplicateSlides && lastSlide==0x02) break; + lastSlide=0x02; if (effectVal==0) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; diff --git a/src/engine/song.h b/src/engine/song.h index de57a78f..0607c351 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -246,6 +246,7 @@ struct DivSong { bool arpNonPorta; bool algMacroBehavior; bool brokenShortcutSlides; + bool ignoreDuplicateSlides; DivOrders orders; std::vector ins; @@ -304,7 +305,8 @@ struct DivSong { targetResetsSlides(true), arpNonPorta(false), algMacroBehavior(false), - brokenShortcutSlides(false) { + brokenShortcutSlides(false), + ignoreDuplicateSlides(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 715f3237..e5c91202 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1810,6 +1810,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target."); } + ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {