From 310ad1391655b58c43ac443cc4567a918334eddf Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Fri, 23 Aug 2024 10:39:14 -0700 Subject: [PATCH 1/4] volume portamento (vol porta) on D3xx and D4xx (D4 is fast version, rate * 256) --- src/engine/cmdStream.cpp | 15 +++++++++- src/engine/cmdStream.h | 3 +- src/engine/cmdStreamOps.cpp | 5 ++++ src/engine/dispatch.h | 1 + src/engine/engine.cpp | 4 +++ src/engine/engine.h | 3 +- src/engine/playback.cpp | 55 +++++++++++++++++++++++++++++++++++-- src/gui/csPlayer.cpp | 4 +++ src/gui/debugWindow.cpp | 1 + src/gui/guiConst.cpp | 4 +-- src/gui/pattern.cpp | 1 + 11 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index d8c77030f..5f369b211 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -215,6 +215,10 @@ bool DivCSPlayer::tick() { case DIV_CMD_HINT_VOL_SLIDE: arg0=(short)stream.readS(); break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + arg0=(short)stream.readS(); + arg1=(short)stream.readS(); + break; case DIV_CMD_HINT_LEGATO: arg0=(unsigned char)stream.readC(); if (arg0==0xff) { @@ -321,6 +325,11 @@ bool DivCSPlayer::tick() { break; case DIV_CMD_HINT_VOL_SLIDE: chan[i].volSpeed=arg0; + chan[i].volSpeedTarget=-1; + break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + chan[i].volSpeed=arg0; + chan[i].volSpeedTarget=arg0==0 ? -1 : arg1; break; case DIV_CMD_HINT_PITCH: chan[i].pitch=arg0; @@ -356,13 +365,17 @@ bool DivCSPlayer::tick() { if (sendVolume || chan[i].volSpeed!=0) { chan[i].volume+=chan[i].volSpeed; + if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + } if (chan[i].volume<0) { chan[i].volume=0; } if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; } - e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 4704b41b9..33fcdd2cf 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -34,7 +34,7 @@ struct DivCSChannelState { int lastWaitLen; int note, pitch; - int volume, volMax, volSpeed; + int volume, volMax, volSpeed, volSpeedTarget; int vibratoDepth, vibratoRate, vibratoPos; int portaTarget, portaSpeed; unsigned char arp, arpStage, arpTicks; @@ -56,6 +56,7 @@ struct DivCSChannelState { volume(0x7f00), volMax(0), volSpeed(0), + volSpeedTarget(0), vibratoDepth(0), vibratoRate(0), vibratoPos(0), diff --git a/src/engine/cmdStreamOps.cpp b/src/engine/cmdStreamOps.cpp index 0710e650e..1f3d81ee2 100644 --- a/src/engine/cmdStreamOps.cpp +++ b/src/engine/cmdStreamOps.cpp @@ -59,6 +59,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { case DIV_CMD_HINT_VOLUME: case DIV_CMD_HINT_PORTA: case DIV_CMD_HINT_VOL_SLIDE: + case DIV_CMD_HINT_VOL_SLIDE_TARGET: case DIV_CMD_HINT_LEGATO: w->writeC((unsigned char)c.cmd+0xb4); break; @@ -100,6 +101,10 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { case DIV_CMD_HINT_VOL_SLIDE: w->writeS(c.value); break; + case DIV_CMD_HINT_VOL_SLIDE_TARGET: + w->writeS(c.value); + w->writeS(c.value2); + break; case DIV_CMD_SAMPLE_MODE: case DIV_CMD_SAMPLE_FREQ: case DIV_CMD_SAMPLE_BANK: diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index e077d8f4d..517adc662 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -67,6 +67,7 @@ enum DivDispatchCmds { DIV_CMD_HINT_ARPEGGIO, // (note1, note2) DIV_CMD_HINT_VOLUME, // (vol) DIV_CMD_HINT_VOL_SLIDE, // (amount, oneTick) + DIV_CMD_HINT_VOL_SLIDE_TARGET, // (amount, target) DIV_CMD_HINT_PORTA, // (target, speed) DIV_CMD_HINT_LEGATO, // (note) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fbbf0edc8..18189a29b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -98,6 +98,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul break; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return _("Cxxx: Set tick rate (hz)"); + case 0xd3: + return _("D3xx: Volume portamento"); + case 0xd4: + return _("D4xx: Volume portamento (fast)"); case 0xdc: return _("DCxx: Delayed mute"); case 0xe0: diff --git a/src/engine/engine.h b/src/engine/engine.h index 64d406964..e7788149e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -133,7 +133,7 @@ struct DivAudioExportOptions { struct DivChannelState { std::vector delayed; int note, oldNote, lastIns, pitch, portaSpeed, portaNote; - int volume, volSpeed, cut, volCut, legatoDelay, legatoTarget, rowDelay, volMax; + int volume, volSpeed, volSpeedTarget, cut, volCut, legatoDelay, legatoTarget, rowDelay, volMax; int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoShape, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; @@ -157,6 +157,7 @@ struct DivChannelState { portaNote(-1), volume(0x7f00), volSpeed(0), + volSpeedTarget(0), cut(-1), volCut(-1), legatoDelay(-1), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3b3234934..9c157073a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -67,6 +67,7 @@ const char* cmdName[]={ "HINT_ARPEGGIO", "HINT_VOLUME", "HINT_VOL_SLIDE", + "HINT_VOL_SLIDE_TARGET", "HINT_PORTA", "HINT_LEGATO", @@ -639,11 +640,26 @@ void DivEngine::processRow(int i, bool afterDelay) { } // volume + int volPortaTarget=-1; + bool useVolPorta = false; + for (int j=0; jdata[whatRow][4+(j<<1)]; + short effectVal=pat->data[whatRow][5+(j<<1)]; + if (effectVal==-1) effectVal=0; + if ((effect==0xd3||effect==0xd4) && effectVal!=0) { // vol porta + useVolPorta=true; + break; + } + } + if (pat->data[whatRow][3]!=-1) { - if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { + if (useVolPorta) { + volPortaTarget=pat->data[whatRow][3]<<8; + } else if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } + chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -828,6 +844,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x06: // vol slide + porta @@ -869,6 +886,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x07: // tremolo @@ -879,6 +897,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloRate=effectVal>>4; if (chan[i].tremoloDepth!=0) { chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; } else { dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -898,6 +917,7 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0x00: // arpeggio @@ -1077,6 +1097,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloDepth=0; chan[i].tremoloRate=0; chan[i].volSpeed=effectVal; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf4: // fine volume ramp down @@ -1084,6 +1105,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].tremoloDepth=0; chan[i].tremoloRate=0; chan[i].volSpeed=-effectVal; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; case 0xf5: // disable macro @@ -1097,12 +1119,14 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xf8: // single volume ramp up chan[i].volSpeed=0; // add compat flag? + chan[i].volSpeedTarget=-1; chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); break; case 0xf9: // single volume ramp down chan[i].volSpeed=0; // add compat flag? + chan[i].volSpeedTarget=-1; chan[i].volume=MAX(chan[i].volume-effectVal*256,0); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -1120,9 +1144,25 @@ void DivEngine::processRow(int i, bool afterDelay) { } else { chan[i].volSpeed=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - + case 0xd3: // volume portamento (vol porta) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; + case 0xd4: // volume portamento fast (vol porta fast) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; case 0xfc: // delayed note release if (song.delayBehavior==2 || effectValchan[i].volMax) { + if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); + } else if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); @@ -1604,6 +1652,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } else { chan[i].volume=0; } + chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); } else { diff --git a/src/gui/csPlayer.cpp b/src/gui/csPlayer.cpp index 6bbae43ce..7be1f4e1d 100644 --- a/src/gui/csPlayer.cpp +++ b/src/gui/csPlayer.cpp @@ -161,6 +161,8 @@ void FurnaceGUI::drawCSPlayer() { ImGui::TableNextColumn(); ImGui::Text(_("vols")); ImGui::TableNextColumn(); + ImGui::Text(_("volst")); + ImGui::TableNextColumn(); ImGui::Text(_("vib")); ImGui::TableNextColumn(); ImGui::Text(_("porta")); @@ -189,6 +191,8 @@ void FurnaceGUI::drawCSPlayer() { ImGui::TableNextColumn(); ImGui::Text("%+d",state->volSpeed); ImGui::TableNextColumn(); + ImGui::Text("%+d",state->volSpeedTarget); + ImGui::TableNextColumn(); ImGui::Text("%d/%d (%d)",state->vibratoDepth,state->vibratoRate,state->vibratoPos); ImGui::TableNextColumn(); ImGui::Text("-> %d (%d)",state->portaTarget,state->portaSpeed); diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index b08c60532..b28126fc5 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -145,6 +145,7 @@ void FurnaceGUI::drawDebug() { ImGui::Text("- portaNote = %d",ch->portaNote); ImGui::Text("- volume = %.4x",ch->volume); ImGui::Text("- volSpeed = %d",ch->volSpeed); + ImGui::Text("- volSpeedTarget = %d",ch->volSpeedTarget); ImGui::Text("- cut = %d",ch->cut); ImGui::Text("- rowDelay = %d",ch->rowDelay); ImGui::Text("- volMax = %.4x",ch->volMax); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 8fd662824..fbfdadc0c 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -473,8 +473,8 @@ const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_VOLUME, + GUI_COLOR_PATTERN_EFFECT_VOLUME, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, GUI_COLOR_PATTERN_EFFECT_INVALID, diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index a2568bd31..c32171161 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -1500,6 +1500,7 @@ void FurnaceGUI::drawPattern() { i.cmd==DIV_CMD_HINT_PORTA || i.cmd==DIV_CMD_HINT_LEGATO || i.cmd==DIV_CMD_HINT_VOL_SLIDE || + i.cmd==DIV_CMD_HINT_VOL_SLIDE_TARGET || i.cmd==DIV_CMD_HINT_ARPEGGIO || i.cmd==DIV_CMD_HINT_PITCH || i.cmd==DIV_CMD_HINT_VIBRATO || From ae4adececd48c21d8440c71f2e963eaf5c073437 Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Fri, 23 Aug 2024 11:16:22 -0700 Subject: [PATCH 2/4] do nothing if volume column empty --- src/engine/playback.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 9c157073a..e56489f05 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -641,21 +641,23 @@ void DivEngine::processRow(int i, bool afterDelay) { // volume int volPortaTarget=-1; - bool useVolPorta = false; + bool noApplyVolume=false; for (int j=0; jdata[whatRow][4+(j<<1)]; - short effectVal=pat->data[whatRow][5+(j<<1)]; - if (effectVal==-1) effectVal=0; - if ((effect==0xd3||effect==0xd4) && effectVal!=0) { // vol porta - useVolPorta=true; - break; + if (effect==0xd3 || effect==0xd4) { // vol porta + volPortaTarget=pat->data[whatRow][3]<<8; // can be -256 + + short effectVal=pat->data[whatRow][5+(j<<1)]; + if (effectVal==-1) effectVal=0; + effectVal&=255; + + noApplyVolume=effectVal>0; // "D3.." or "D300" shouldn't stop volume from applying + break; // technically you could have both D3 and D4... let's not care } } - if (pat->data[whatRow][3]!=-1) { - if (useVolPorta) { - volPortaTarget=pat->data[whatRow][3]<<8; - } else if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { + if (pat->data[whatRow][3]!=-1 && !noApplyVolume) { + if (!song.oldAlwaysSetVolume || disCont[dispatchOfChan[i]].dispatch->getLegacyAlwaysSetVolume() || (MIN(chan[i].volMax,chan[i].volume)>>8)!=pat->data[whatRow][3]) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } @@ -1147,22 +1149,24 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - case 0xd3: // volume portamento (vol porta) + case 0xd3: { // volume portamento (vol porta) // tremolo and vol slides are incompatible chan[i].tremoloDepth=0; chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; - case 0xd4: // volume portamento fast (vol porta fast) + } + case 0xd4: { // volume portamento fast (vol porta fast) // tremolo and vol slides are incompatible chan[i].tremoloDepth=0; chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); break; + } case 0xfc: // delayed note release if (song.delayBehavior==2 || effectVal Date: Fri, 23 Aug 2024 11:24:24 -0700 Subject: [PATCH 3/4] minor cleanup --- src/engine/cmdStream.h | 2 +- src/engine/engine.h | 2 +- src/engine/playback.cpp | 35 ++++++++++++++++------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 33fcdd2cf..2d8e6e97e 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -56,7 +56,7 @@ struct DivCSChannelState { volume(0x7f00), volMax(0), volSpeed(0), - volSpeedTarget(0), + volSpeedTarget(-1), vibratoDepth(0), vibratoRate(0), vibratoPos(0), diff --git a/src/engine/engine.h b/src/engine/engine.h index e7788149e..308c6f5cd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -157,7 +157,7 @@ struct DivChannelState { portaNote(-1), volume(0x7f00), volSpeed(0), - volSpeedTarget(0), + volSpeedTarget(-1), cut(-1), volCut(-1), legatoDelay(-1), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index e56489f05..2c7e97e0e 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -661,7 +661,6 @@ void DivEngine::processRow(int i, bool afterDelay) { if (pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0) { chan[i].midiAftertouch=true; } - chan[i].volume=pat->data[whatRow][3]<<8; dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); @@ -962,6 +961,22 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].cutType=0; } break; + case 0xd3: // volume portamento (vol porta) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; + case 0xd4: // volume portamento fast (vol porta fast) + // tremolo and vol slides are incompatible + chan[i].tremoloDepth=0; + chan[i].tremoloRate=0; + chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; + chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); + break; case 0xe0: // arp speed if (effectVal>0) { curSubSong->arpLen=effectVal; @@ -1149,24 +1164,6 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].volSpeedTarget=-1; dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed)); break; - case 0xd3: { // volume portamento (vol porta) - // tremolo and vol slides are incompatible - chan[i].tremoloDepth=0; - chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? effectVal : -effectVal; - chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); - break; - } - case 0xd4: { // volume portamento fast (vol porta fast) - // tremolo and vol slides are incompatible - chan[i].tremoloDepth=0; - chan[i].tremoloRate=0; - chan[i].volSpeed=volPortaTarget<0 ? 0 : volPortaTarget>chan[i].volume ? 256*effectVal : -256*effectVal; - chan[i].volSpeedTarget=chan[i].volSpeed==0 ? -1 : volPortaTarget; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE_TARGET,i,chan[i].volSpeed,chan[i].volSpeedTarget)); - break; - } case 0xfc: // delayed note release if (song.delayBehavior==2 || effectVal Date: Sat, 24 Aug 2024 00:59:03 -0700 Subject: [PATCH 4/4] update vol-porta-ending logic to be more readable --- src/engine/cmdStream.cpp | 20 ++++++++++++++++---- src/engine/playback.cpp | 29 +++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index 5f369b211..40542ef98 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -365,10 +365,22 @@ bool DivCSPlayer::tick() { if (sendVolume || chan[i].volSpeed!=0) { chan[i].volume+=chan[i].volSpeed; - if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { - chan[i].volume=chan[i].volSpeedTarget; - chan[i].volSpeed=0; - chan[i].volSpeedTarget=-1; + if (chan[i].volSpeedTarget!=-1) { + bool atTarget=false; + if (chan[i].volSpeed>0) { + atTarget=(chan[i].volume>=chan[i].volSpeedTarget); + } else if (chan[i].volSpeed<0) { + atTarget=(chan[i].volume<=chan[i].volSpeedTarget); + } else { + atTarget=true; + chan[i].volSpeedTarget=chan[i].volume; + } + + if (atTarget) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + } } if (chan[i].volume<0) { chan[i].volume=0; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2c7e97e0e..296183f8c 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1631,14 +1631,27 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (chan[i].volSpeed!=0) { chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); chan[i].volume+=chan[i].volSpeed; - if (chan[i].volSpeedTarget!=-1 && (chan[i].volume==chan[i].volSpeedTarget || (chan[i].volume>chan[i].volSpeedTarget)==(chan[i].volSpeed>0))) { - chan[i].volume=chan[i].volSpeedTarget; - chan[i].volSpeed=0; - chan[i].volSpeedTarget=-1; - dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); - dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); - dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); - } else if (chan[i].volume>chan[i].volMax) { + if (chan[i].volSpeedTarget!=-1) { + bool atTarget=false; + if (chan[i].volSpeed>0) { + atTarget=(chan[i].volume>=chan[i].volSpeedTarget); + } else if (chan[i].volSpeed<0) { + atTarget=(chan[i].volume<=chan[i].volSpeedTarget); + } else { + atTarget=true; + chan[i].volSpeedTarget=chan[i].volume; + } + + if (atTarget) { + chan[i].volume=chan[i].volSpeedTarget; + chan[i].volSpeed=0; + chan[i].volSpeedTarget=-1; + dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,0)); + } + } + if (chan[i].volume>chan[i].volMax) { chan[i].volume=chan[i].volMax; chan[i].volSpeed=0; chan[i].volSpeedTarget=-1;