diff --git a/papers/format.md b/papers/format.md index 882c5d8c9..b04deac74 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: +- 65: Furnace dev65 - 64: Furnace dev64 - 63: Furnace dev63 - 62: Furnace dev62 @@ -205,7 +206,8 @@ size | description 1 | stop portamento on note off (>=62) or reserved 1 | continuous vibrato (>=62) or reserved 1 | broken DAC mode (>=64) or reserved - 3 | reserved + 1 | one tick cut (>=65) or reserved + 2 | reserved 4?? | pointers to instruments 4?? | pointers to wavetables 4?? | pointers to samples diff --git a/src/engine/engine.h b/src/engine/engine.h index 29d5a7795..fc70c8730 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -37,8 +37,8 @@ warnings+=(String("\n")+x); \ } -#define DIV_VERSION "dev64" -#define DIV_ENGINE_VERSION 64 +#define DIV_VERSION "dev65" +#define DIV_ENGINE_VERSION 65 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 9a927fdd2..3c1a9d2a1 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -141,6 +141,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.brokenShortcutSlides=false; ds.ignoreDuplicateSlides=true; ds.brokenDACMode=true; + ds.oneTickCut=false; // 1.1 compat flags if (ds.version>24) { @@ -803,6 +804,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<64) { ds.brokenDACMode=false; } + if (ds.version<65) { + ds.oneTickCut=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -984,7 +988,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<3; i++) reader.readC(); + if (ds.version>=65) { + ds.oneTickCut=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<2; i++) reader.readC(); } else { for (int i=0; i<20; i++) reader.readC(); } @@ -1427,7 +1436,8 @@ SafeWriter* DivEngine::saveFur() { w->writeC(song.stopPortaOnNoteOff); w->writeC(song.continuousVibrato); w->writeC(song.brokenDACMode); - for (int i=0; i<3; i++) { + w->writeC(song.oneTickCut); + for (int i=0; i<2; i++) { w->writeC(0); } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 7bee3c411..4626635b0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1067,7 +1067,24 @@ void DivEngine::nextRow() { if (pat->data[curRow][0]!=100 && pat->data[curRow][0]!=101 && pat->data[curRow][0]!=102) { if (!chan[i].legato) { dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks)); - //chan[i].cut=ticks; + + if (song.oneTickCut) { + bool doPrepareCut=true; + + for (int j=0; jdata[curRow][4+(j<<1)]==0x03) { + doPrepareCut=false; + break; + } + if (pat->data[curRow][4+(j<<1)]==0xea) { + if (pat->data[curRow][5+(j<<1)]>0) { + doPrepareCut=false; + break; + } + } + } + if (doPrepareCut) chan[i].cut=ticks; + } } } } diff --git a/src/engine/song.h b/src/engine/song.h index 44b027320..5747aa511 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -296,6 +296,7 @@ struct DivSong { bool stopPortaOnNoteOff; bool continuousVibrato; bool brokenDACMode; + bool oneTickCut; DivOrders orders; std::vector ins; @@ -361,7 +362,8 @@ struct DivSong { ignoreDuplicateSlides(false), stopPortaOnNoteOff(false), continuousVibrato(false), - brokenDACMode(false) { + brokenDACMode(false), + oneTickCut(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 a1f176fa1..88c945bdd 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2069,7 +2069,7 @@ void FurnaceGUI::drawCompatFlags() { } if (!compatFlagsOpen) return; if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) { - ImGui::TextWrapped("these flags are stored in the song when saving in .fur format, and are automatically enabled when saving in .dmf format."); + ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility."); ImGui::Checkbox("Limit slide range",&e->song.limitSlides); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); @@ -2119,6 +2119,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing."); } + ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {