From 21994a7eee091069cbc2ebdebc04d5dfe2775cbb Mon Sep 17 00:00:00 2001 From: Dalia Date: Sun, 17 Nov 2024 12:45:39 -0700 Subject: [PATCH] per-channel arpeggio speeds E0xx works on a per-channel basis for new songs. old behaviour can be toggled with a compatibility flag --- src/engine/cmdStream.cpp | 9 +++++++-- src/engine/cmdStream.h | 3 ++- src/engine/engine.h | 3 ++- src/engine/fileOps/dmf.cpp | 1 + src/engine/fileOps/fur.cpp | 3 +++ src/engine/playback.cpp | 10 +++++++--- src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 8 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/engine/cmdStream.cpp b/src/engine/cmdStream.cpp index f1b267869a..9508994c16 100644 --- a/src/engine/cmdStream.cpp +++ b/src/engine/cmdStream.cpp @@ -352,7 +352,11 @@ bool DivCSPlayer::tick() { chan[i].arp=(((unsigned char)arg0)<<4)|(arg1&15); break; case DIV_CMD_HINT_ARP_TIME: - arpSpeed=arg0; + if (e->song.arpSpeedGlobal) { + arpSpeed=arg0; + } else { + chan[i].arpLen=arg0; + } break; default: // dispatch it e->dispatchCmd(DivCommand((DivDispatchCmds)command,i,arg0,arg1)); @@ -422,7 +426,7 @@ bool DivCSPlayer::tick() { } chan[i].arpStage++; if (chan[i].arpStage>=3) chan[i].arpStage=0; - chan[i].arpTicks=arpSpeed; + chan[i].arpTicks=(e->song.arpSpeedGlobal)?arpSpeed:chan[i].arpLen; } chan[i].arpTicks--; } @@ -460,6 +464,7 @@ bool DivCSPlayer::init() { for (int i=0; igetTotalChannelCount(); i++) { chan[i].volMax=(e->getDispatch(e->dispatchOfChan[i])->dispatch(DivCommand(DIV_CMD_GET_VOLMAX,e->dispatchChanOfChan[i]))<<8)|0xff; chan[i].volume=chan[i].volMax; + chan[i].arpLen=1; } for (int i=0; i<64; i++) { diff --git a/src/engine/cmdStream.h b/src/engine/cmdStream.h index 2d8e6e97ef..ad645b5f34 100644 --- a/src/engine/cmdStream.h +++ b/src/engine/cmdStream.h @@ -37,7 +37,7 @@ struct DivCSChannelState { int volume, volMax, volSpeed, volSpeedTarget; int vibratoDepth, vibratoRate, vibratoPos; int portaTarget, portaSpeed; - unsigned char arp, arpStage, arpTicks; + unsigned char arp, arpStage, arpTicks, arpLen; unsigned int callStack[8]; unsigned char callStackPos; @@ -65,6 +65,7 @@ struct DivCSChannelState { arp(0), arpStage(0), arpTicks(0), + arpLen(1), callStackPos(0), tracePos(0) { for (int i=0; i24) { diff --git a/src/engine/fileOps/fur.cpp b/src/engine/fileOps/fur.cpp index bcee8e9ba9..bedbb6fffd 100644 --- a/src/engine/fileOps/fur.cpp +++ b/src/engine/fileOps/fur.cpp @@ -864,6 +864,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { if (ds.version<200) { ds.oldSampleOffset=true; } + if (ds.version<224) { + ds.arpSpeedGlobal=true; + } ds.isDMF=false; reader.readS(); // reserved diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0c8f4fdbf4..181c13b769 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1020,8 +1020,12 @@ void DivEngine::processRow(int i, bool afterDelay) { break; case 0xe0: // arp speed if (effectVal>0) { - curSubSong->arpLen=effectVal; - dispatchCmd(DivCommand(DIV_CMD_HINT_ARP_TIME,i,curSubSong->arpLen)); + if (song.arpSpeedGlobal) { + curSubSong->arpLen=effectVal; + } else { + chan[i].arpLen=effectVal; + } + dispatchCmd(DivCommand(DIV_CMD_HINT_ARP_TIME,i,effectVal)); } break; case 0xe1: // portamento up @@ -1933,7 +1937,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) { if (--chan[i].arpTicks<1) { - chan[i].arpTicks=curSubSong->arpLen; + chan[i].arpTicks=song.arpSpeedGlobal?curSubSong->arpLen:chan[i].arpLen; chan[i].arpStage++; if (chan[i].arpStage>2) chan[i].arpStage=0; switch (chan[i].arpStage) { diff --git a/src/engine/song.h b/src/engine/song.h index ef745d0cd9..c91e739eb1 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -349,6 +349,7 @@ struct DivSong { bool ceilVolumeScaling; bool oldAlwaysSetVolume; bool oldSampleOffset; + bool arpSpeedGlobal; std::vector ins; std::vector wave; @@ -478,7 +479,8 @@ struct DivSong { resetArpPhaseOnNewNote(false), ceilVolumeScaling(false), oldAlwaysSetVolume(false), - oldSampleOffset(false) { + oldSampleOffset(false), + arpSpeedGlobal(false) { for (int i=0; isong.arpSpeedGlobal); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(_("when enabled, E0xx will affect all channels simultaneously.")); + } ImGui::EndTabItem(); } if (ImGui::BeginTabItem(_(".mod import"))) {