From 3512591fd18b7fd86fc9db2b192faf1263236a4d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Mar 2024 14:56:55 -0500 Subject: [PATCH] add effects to change virtual tempo --- doc/3-pattern/effects.md | 3 +++ src/engine/engine.cpp | 23 ++++++++++++++++++++++- src/engine/engine.h | 10 ++++++++++ src/engine/playback.cpp | 16 +++++++++++----- src/engine/vgmOps.cpp | 2 +- src/gui/gui.cpp | 2 +- src/gui/speed.cpp | 2 ++ 7 files changed, 50 insertions(+), 8 deletions(-) diff --git a/doc/3-pattern/effects.md b/doc/3-pattern/effects.md index 67ca51ad5..e87858431 100644 --- a/doc/3-pattern/effects.md +++ b/doc/3-pattern/effects.md @@ -70,6 +70,9 @@ not all chips support these effects. - `xxx` may be from `000` to `3FF`. - `F0xx`: **Set BPM.** changes tick rate according to beats per minute. range is `01` to `FF`. - --- +- `FDxx`: **Set virtual tempo numerator.** sets the virtual tempo's numerator to the effect value. +- `FExx`: **Set virtual tempo denominator.** sets the virtual tempo's denominator to the effect value. + - --- - `0Bxx`: **Jump to order.** `x` is the order to play after the current row. - this marks the end of a loop with order `x` as the loop start. - `0Dxx`: **Jump to next pattern.** skips the current row and remainder of current order. `x` is the row at which to start playing the next pattern. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ab8b22a99..c34a20e47 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -141,6 +141,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul return "FAxx: Fast volume slide (0y: down; x0: up)"; case 0xfc: return "FCxx: Note release"; + case 0xfd: + return "FDxx: Set virtual tempo numerator"; + case 0xfe: + return "FExx: Set virtual tempo denominator"; case 0xff: return "FFxx: Stop song"; default: @@ -1683,7 +1687,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { runMidiTime(cycles); } if (oldOrder!=curOrder) break; - if (ticks-((tempoAccum+curSubSong->virtualTempoN)/MAX(1,curSubSong->virtualTempoD))<1 && curRow>=goalRow) break; + if (ticks-((tempoAccum+virtualTempoN)/MAX(1,virtualTempoD))<1 && curRow>=goalRow) break; } for (int i=0; isetSkipRegisterWrites(false); if (goal>0 || goalRow>0) { @@ -2121,6 +2125,8 @@ void DivEngine::reset() { extValue=0; extValuePresent=0; speeds=curSubSong->speeds; + virtualTempoN=curSubSong->virtualTempoN; + virtualTempoD=curSubSong->virtualTempoD; firstTick=false; shallStop=false; shallStopSched=false; @@ -2368,6 +2374,21 @@ float DivEngine::getCurHz() { return divider; } +short DivEngine::getVirtualTempoN() { + return virtualTempoN; +} + +short DivEngine::getVirtualTempoD() { + return virtualTempoD; +} + +void DivEngine::virtualTempoChanged() { + BUSY_BEGIN; + virtualTempoN=curSubSong->virtualTempoN; + virtualTempoD=curSubSong->virtualTempoD; + BUSY_END; +} + int DivEngine::getTotalSeconds() { return totalSeconds; } diff --git a/src/engine/engine.h b/src/engine/engine.h index bc4d9926a..982361f23 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -442,6 +442,7 @@ class DivEngine { int curMidiTimePiece, curMidiTimeCode; unsigned char extValue, pendingMetroTick; DivGroovePattern speeds; + short virtualTempoN, virtualTempoD; short tempoAccum; DivStatusView view; DivHaltPositions haltOn; @@ -891,6 +892,13 @@ class DivEngine { // get current Hz float getCurHz(); + // get virtual tempo + short getVirtualTempoN(); + short getVirtualTempoD(); + + // tell engine about virtual tempo changes + void virtualTempoChanged(); + // get time int getTotalTicks(); // 1/1000000th of a second int getTotalSeconds(); @@ -1334,6 +1342,8 @@ class DivEngine { curMidiTimeCode(0), extValue(0), pendingMetroTick(0), + virtualTempoN(150), + virtualTempoD(150), tempoAccum(0), view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 9f704414f..49341ce14 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -487,6 +487,12 @@ void DivEngine::processRow(int i, bool afterDelay) { if (effectVal>0) speeds.val[0]=effectVal; } break; + case 0xfd: // virtual tempo num + if (effectVal>0) virtualTempoN=effectVal; + break; + case 0xfe: // virtual tempo den + if (effectVal>0) virtualTempoD=effectVal; + break; case 0x0b: // change order if (changeOrd==-1 || song.jumpTreatment==0) { changeOrd=effectVal; @@ -1442,9 +1448,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { subticks=tickMult; if (stepPlay!=1) { - tempoAccum+=(skipping && curSubSong->virtualTempoNvirtualTempoD)?curSubSong->virtualTempoD:curSubSong->virtualTempoN; - while (tempoAccum>=curSubSong->virtualTempoD) { - tempoAccum-=curSubSong->virtualTempoD; + tempoAccum+=(skipping && virtualTempoN=virtualTempoD) { + tempoAccum-=virtualTempoD; if (--ticks<=0) { ret=endOfSong; if (shallStopSched) { @@ -1693,7 +1699,7 @@ void DivEngine::runMidiClock(int totalCycles) { if (hl<=0.0) hl=4.0; double timeBase=curSubSong->timeBase+1; double speedSum=0; - double vD=curSubSong->virtualTempoD; + double vD=virtualTempoD; for (int i=0; ivirtualTempoN/vD; + double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)virtualTempoN/vD; if (bpm<1.0) bpm=1.0; int increment=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm); diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 2d85601f8..a8a1fab74 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -2424,7 +2424,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p while (!done) { if (loopPos==-1) { if (loopOrder==curOrder && loopRow==curRow) { - if ((ticks-((tempoAccum+curSubSong->virtualTempoN)/curSubSong->virtualTempoD))<=0) { + if ((ticks-((tempoAccum+virtualTempoN)/virtualTempoD))<=0) { writeLoop=true; } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9d95d1595..d5fe08159 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4446,7 +4446,7 @@ bool FurnaceGUI::loop() { info="| Groove"; } - info+=fmt::sprintf(" @ %gHz (%g BPM) ",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); + info+=fmt::sprintf(" @ %gHz (%g BPM) ",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->getVirtualTempoN(),e->getVirtualTempoD())); if (settings.orderRowsBase) { info+=fmt::sprintf("| Order %.2X/%.2X ",playOrder,e->curSubSong->ordersLen-1); diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp index 66760d44f..df0265741 100644 --- a/src/gui/speed.cpp +++ b/src/gui/speed.cpp @@ -171,6 +171,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_TEN)) { MARK_MODIFIED if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; + e->virtualTempoChanged(); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Numerator"); @@ -180,6 +181,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_TEN)) { MARK_MODIFIED if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; + e->virtualTempoChanged(); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Denominator (set to base tempo)");