diff --git a/papers/format.md b/papers/format.md index dc052d038..7c113b53a 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: +- 62: Furnace dev62 - 61: Furnace dev61 - 60: Furnace dev60 - 59: Furnace dev59 @@ -197,7 +198,9 @@ size | description 1 | wack algorithm macro (>=47) or reserved 1 | broken shortcut slides (>=49) or reserved 1 | ignore duplicate slides (>=50) or reserved - 6 | reserved + 1 | stop portamento on note off (>=62) or reserved + 1 | continuous vibrato (>=62) or reserved + 4 | 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 02870178a..921d41569 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -37,8 +37,8 @@ warnings+=(String("\n")+x); \ } -#define DIV_VERSION "dev61" -#define DIV_ENGINE_VERSION 61 +#define DIV_VERSION "dev62" +#define DIV_ENGINE_VERSION 62 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 859a99fe2..8b5cfad5d 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -796,6 +796,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<50) { ds.ignoreDuplicateSlides=false; } + if (ds.version<62) { + ds.stopPortaOnNoteOff=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -965,7 +968,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<6; i++) reader.readC(); + if (ds.version>=62) { + ds.stopPortaOnNoteOff=reader.readC(); + ds.continuousVibrato=reader.readC(); + } else { + reader.readC(); + reader.readC(); + } + for (int i=0; i<4; i++) reader.readC(); } else { for (int i=0; i<20; i++) reader.readC(); } @@ -1404,7 +1414,9 @@ SafeWriter* DivEngine::saveFur() { w->writeC(song.algMacroBehavior); w->writeC(song.brokenShortcutSlides); w->writeC(song.ignoreDuplicateSlides); - for (int i=0; i<6; i++) { + w->writeC(song.stopPortaOnNoteOff); + w->writeC(song.continuousVibrato); + for (int i=0; i<4; i++) { w->writeC(0); } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index fac7728e8..23cb54e1c 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -728,7 +728,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } chan[i].portaStop=true; if (chan[i].keyOn) chan[i].doNote=false; - chan[i].stopOnOff=false; // what?! + chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,1)); lastSlide=0x1337; // i hate this so much @@ -778,7 +778,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaSpeed=(effectVal>>4)*4; chan[i].portaStop=true; chan[i].nowYouCanStop=false; - chan[i].stopOnOff=false; // what?! + chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; if ((effectVal&15)!=0) { chan[i].inPorta=true; @@ -794,7 +794,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaSpeed=(effectVal>>4)*4; chan[i].portaStop=true; chan[i].nowYouCanStop=false; - chan[i].stopOnOff=false; // what?! + chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?! chan[i].scheduledSlideReset=false; if ((effectVal&15)!=0) { chan[i].inPorta=true; @@ -854,7 +854,9 @@ void DivEngine::processRow(int i, bool afterDelay) { } if (chan[i].doNote) { - chan[i].vibratoPos=0; + if (!song.continuousVibrato) { + chan[i].vibratoPos=0; + } dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); if (chan[i].legato) { dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); diff --git a/src/engine/song.h b/src/engine/song.h index b200068e7..fff1065fe 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -276,6 +276,8 @@ struct DivSong { bool algMacroBehavior; bool brokenShortcutSlides; bool ignoreDuplicateSlides; + bool stopPortaOnNoteOff; + bool continuousVibrato; DivOrders orders; std::vector ins; @@ -338,7 +340,9 @@ struct DivSong { arpNonPorta(false), algMacroBehavior(false), brokenShortcutSlides(false), - ignoreDuplicateSlides(false) { + ignoreDuplicateSlides(false), + stopPortaOnNoteOff(false), + continuousVibrato(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 ef0e0d86f..58e95003f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2054,6 +2054,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); } + ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { @@ -2091,6 +2095,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("behavior changed in 0.5.7"); } + ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End();