diff --git a/papers/format.md b/papers/format.md index 734f5b2c8..5d227f13e 100644 --- a/papers/format.md +++ b/papers/format.md @@ -25,6 +25,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 47: Furnace 0.5.6pre1 +- 46: Furnace 0.5.5 - 45: Furnace 0.5.5pre3 - 44: Furnace 0.5.5pre2 - 43: Furnace 0.5.5pre1 @@ -161,7 +163,9 @@ size | description 1 | compatible arpeggio (>=45) or reserved 1 | note off resets slides (>=45) or reserved 1 | target resets slides (>=45) or reserved - 10 | reserved + 1 | arpeggio inhibits portamento (>=47) or reserved + 1 | wack algorithm macro (>=47) or reserved + 8 | 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 4365d1d88..bd28d833c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1551,6 +1551,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.compatibleArpeggio=true; ds.noteOffResetsSlides=true; ds.targetResetsSlides=true; + ds.arpNonPorta=false; + ds.algMacroBehavior=false; // Neo Geo detune if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT) { @@ -2093,6 +2095,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.noteOffResetsSlides=true; ds.targetResetsSlides=true; } + if (ds.version<46) { + ds.arpNonPorta=true; + ds.algMacroBehavior=true; + } else { + ds.arpNonPorta=false; + ds.algMacroBehavior=false; + } reader.readS(); // reserved int infoSeek=reader.readI(); @@ -2200,7 +2209,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<10; i++) reader.readC(); + if (ds.version>=47) { + ds.arpNonPorta=reader.readC(); + } else { + reader.readC(); + } + if (ds.version>=47) { + ds.algMacroBehavior=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<8; i++) reader.readC(); } else { for (int i=0; i<20; i++) reader.readC(); } @@ -2581,7 +2600,9 @@ SafeWriter* DivEngine::saveFur() { w->writeC(song.compatibleArpeggio); w->writeC(song.noteOffResetsSlides); w->writeC(song.targetResetsSlides); - for (int i=0; i<10; i++) { + w->writeC(song.arpNonPorta); + w->writeC(song.algMacroBehavior); + for (int i=0; i<8; i++) { w->writeC(0); } diff --git a/src/engine/engine.h b/src/engine/engine.h index 76913d7c6..312f36595 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -11,8 +11,8 @@ #include #include -#define DIV_VERSION "0.5.5" -#define DIV_ENGINE_VERSION 46 +#define DIV_VERSION "0.5.6pre1" +#define DIV_ENGINE_VERSION 47 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 366bcaff4..8422e467d 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -276,7 +276,7 @@ void DivPlatformArcade::tick() { } else { rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7)); } - for (int j=0; j<4; j++) { + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isMuted[i]) { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 3f5640ad6..e90fa921c 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -201,7 +201,7 @@ void DivPlatformGenesis::tick() { if (chan[i].std.hadAlg) { chan[i].state.alg=chan[i].std.alg; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - for (int j=0; j<4; j++) { + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isMuted[i]) { diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 93f0af3d0..cbecc4aa6 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -183,7 +183,7 @@ void DivPlatformYM2610::tick() { if (chan[i].std.hadAlg) { chan[i].state.alg=chan[i].std.alg; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - for (int j=0; j<4; j++) { + if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isMuted[i]) { diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2456e4167..041ebf731 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -565,7 +565,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; chan[i].inPorta=false; - dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); + if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?0x60:255; chan[i].portaSpeed=effectVal; @@ -574,7 +574,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].stopOnOff=false; chan[i].scheduledSlideReset=false; chan[i].inPorta=false; - dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); } break; case 0x02: // ramp down @@ -582,7 +582,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].portaNote=-1; chan[i].portaSpeed=-1; chan[i].inPorta=false; - dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); + if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); } else { chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60; chan[i].portaSpeed=effectVal; @@ -591,7 +591,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].stopOnOff=false; chan[i].scheduledSlideReset=false; chan[i].inPorta=false; - dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); } break; case 0x03: // portamento diff --git a/src/engine/song.h b/src/engine/song.h index 1a467a196..9977e48ed 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -224,6 +224,8 @@ struct DivSong { bool compatibleArpeggio; bool noteOffResetsSlides; bool targetResetsSlides; + bool arpNonPorta; + bool algMacroBehavior; DivOrders orders; std::vector ins; @@ -279,7 +281,9 @@ struct DivSong { legacyVolumeSlides(false), compatibleArpeggio(false), noteOffResetsSlides(true), - targetResetsSlides(true) { + targetResetsSlides(true), + arpNonPorta(false), + algMacroBehavior(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 98bae99d6..d7db90ae9 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3997,6 +3997,19 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("select to not reset channels on loop."); } + + ImGui::Separator(); + + ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions."); + + ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } + ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } } ImGui::End(); }