From f990dee0c1f5527614a47bc0d0840e71b206dadd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 29 Oct 2025 17:56:25 -0500 Subject: [PATCH] DivSongTimestamps, part 7 calculate timestamps on every change that is likely to require recalculation (inserting/altering/removing song control/speed effects, changing song speed, changing orders and so on) --- src/engine/playback.cpp | 4 ---- src/gui/commandPalette.cpp | 1 + src/gui/editing.cpp | 44 +++++++++++++++++++++++++++----------- src/gui/findReplace.cpp | 1 + src/gui/gui.cpp | 25 ++++++++++++++++++---- src/gui/gui.h | 1 + src/gui/orders.cpp | 4 ---- src/gui/speed.cpp | 12 +++++++++++ src/gui/sysManager.cpp | 2 ++ 9 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index d964a94f5..4b6e6f0ed 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -2572,10 +2572,6 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (stepPlay!=1) { if (!noAccum) { double dt=divider*tickMult; - // TODO: is this responsible for timing differences when skipping? - if (skipping) { - dt*=(double)virtualTempoN/(double)MAX(1,virtualTempoD); - } totalTicksR++; // despite the name, totalTicks is in microseconds... totalTicks+=1000000/dt; diff --git a/src/gui/commandPalette.cpp b/src/gui/commandPalette.cpp index f0586fe5f..f55673db8 100644 --- a/src/gui/commandPalette.cpp +++ b/src/gui/commandPalette.cpp @@ -381,6 +381,7 @@ void FurnaceGUI::drawPalette() { showError("cannot add chip! ("+e->getLastError()+")"); } else { MARK_MODIFIED; + recalcTimestamps=true; } ImGui::CloseCurrentPopup(); if (e->song.autoSystem) { diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 5a5af9db7..9df50c377 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -129,7 +129,6 @@ void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { bool doPush=false; - bool shallWalk=false; UndoStep s; s.type=action; s.oldCursor=undoCursor; @@ -184,6 +183,7 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { if (!s.ord.empty()) { doPush=true; } + recalcTimestamps=true; break; case GUI_UNDO_PATTERN_EDIT: case GUI_UNDO_PATTERN_DELETE: @@ -227,13 +227,29 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->newData[j][k],p->newData[j][k])); if (k>=DIV_PAT_FX(0)) { - if (op->newData[j][k&(~1)]==0x0b || - p->newData[j][k&(~1)]==0x0b || - op->newData[j][k&(~1)]==0x0d || - p->newData[j][k&(~1)]==0x0d || - op->newData[j][k&(~1)]==0xff || - p->newData[j][k&(~1)]==0xff) { - shallWalk=true; + int fxCol=(k&1)?k:(k-1); + if (op->newData[j][fxCol]==0x09 || + op->newData[j][fxCol]==0x0b || + op->newData[j][fxCol]==0x0d || + op->newData[j][fxCol]==0x0f || + op->newData[j][fxCol]==0xc0 || + op->newData[j][fxCol]==0xc1 || + op->newData[j][fxCol]==0xc2 || + op->newData[j][fxCol]==0xc3 || + op->newData[j][fxCol]==0xf0 || + op->newData[j][fxCol]==0xff || + p->newData[j][fxCol]==0x09 || + p->newData[j][fxCol]==0x0b || + p->newData[j][fxCol]==0x0d || + p->newData[j][fxCol]==0x0f || + p->newData[j][fxCol]==0xc0 || + p->newData[j][fxCol]==0xc1 || + p->newData[j][fxCol]==0xc2 || + p->newData[j][fxCol]==0xc3 || + p->newData[j][fxCol]==0xf0 || + p->newData[j][fxCol]==0xff) { + logV("recalcTimestamps due to speed effect."); + recalcTimestamps=true; } } @@ -258,9 +274,6 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) { redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); } - if (shallWalk) { - e->calcSongTimestamps(); - } // garbage collection for (std::pair i: oldPatMap) { @@ -1798,6 +1811,7 @@ void FurnaceGUI::doCollapseSong(int divider) { redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); } + recalcTimestamps=true; if (e->isPlaying()) e->play(); } @@ -1874,6 +1888,7 @@ void FurnaceGUI::doExpandSong(int multiplier) { redoHist.clear(); if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); } + recalcTimestamps=true; if (e->isPlaying()) e->play(); } @@ -2065,6 +2080,7 @@ void FurnaceGUI::moveSelected(int x, int y) { // replace cursor=selStart; doPaste(GUI_PASTE_MODE_OVERFLOW,0,false,c); + recalcTimestamps=true; makeUndo(GUI_UNDO_PATTERN_DRAG,UndoRegion(firstOrder,0,0,lastOrder,e->getTotalChannelCount()-1,e->curSubSong->patLen-1)); } @@ -2119,10 +2135,11 @@ void FurnaceGUI::doUndo() { } } } - e->calcSongTimestamps(); break; } + recalcTimestamps=true; + bool shallReplay=false; for (UndoOtherData& i: us.other) { switch (i.target) { @@ -2197,10 +2214,11 @@ void FurnaceGUI::doRedo() { } } } - e->calcSongTimestamps(); break; } + recalcTimestamps=true; + bool shallReplay=false; for (UndoOtherData& i: us.other) { switch (i.target) { diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index 2c595993d..d48567534 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -459,6 +459,7 @@ void FurnaceGUI::doReplace() { if (!curQueryResults.empty()) { MARK_MODIFIED; } + recalcTimestamps=true; if (!us.pat.empty()) { undoHist.push_back(us); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a175c85c0..bc4444a9b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1278,7 +1278,6 @@ void FurnaceGUI::play(int row) { chanOscChan[i].pitch=0.0f; } memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float)); - e->calcSongTimestamps(); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); if (wasFollowing) { followPattern=true; @@ -1309,7 +1308,6 @@ void FurnaceGUI::setOrder(unsigned char order, bool forced) { void FurnaceGUI::stop() { bool wasPlaying=e->isPlaying(); - e->calcSongTimestamps(); e->stop(); curNibble=false; orderNibble=false; @@ -1375,7 +1373,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { } } - logV("chan %d, %d:%d %d/%d",ch,ord,y,tick,speed); + logV("noteInput: chan %d, %d:%d %d/%d",ch,ord,y,tick,speed); DivPattern* pat=e->curPat[ch].getPattern(e->curOrders->ord[ch][ord],true); bool removeIns=false; @@ -1428,6 +1426,8 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { int ord=curOrder; int y=cursor.y; + logV("valueInput: chan %d, %d:%d",ch,ord,y); + if (e->isPlaying() && !e->isStepping() && followPattern) { e->getPlayPos(ord,y); } @@ -1538,7 +1538,6 @@ void FurnaceGUI::orderInput(int num) { } } } - e->calcSongTimestamps(); makeUndo(GUI_UNDO_CHANGE_ORDER); } } @@ -4644,6 +4643,7 @@ bool FurnaceGUI::loop() { showError(fmt::sprintf(_("cannot add chip! (%s)"),e->getLastError())); } else { MARK_MODIFIED; + recalcTimestamps=true; } ImGui::CloseCurrentPopup(); if (e->song.autoSystem) { @@ -4673,6 +4673,7 @@ bool FurnaceGUI::loop() { if (picked!=DIV_SYSTEM_NULL) { if (e->changeSystem(i,picked,preserveChanPos)) { MARK_MODIFIED; + recalcTimestamps=true; if (e->song.autoSystem) { autoDetectSystem(); } @@ -4697,6 +4698,7 @@ bool FurnaceGUI::loop() { showError(fmt::sprintf(_("cannot remove chip! (%s)"),e->getLastError())); } else { MARK_MODIFIED; + recalcTimestamps=true; } if (e->song.autoSystem) { autoDetectSystem(); @@ -6496,6 +6498,7 @@ bool FurnaceGUI::loop() { selStart.order=0; selEnd.order=0; MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Current subsong"))) { @@ -6509,6 +6512,7 @@ bool FurnaceGUI::loop() { selStart.order=0; selEnd.order=0; MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Orders"))) { @@ -6523,6 +6527,7 @@ bool FurnaceGUI::loop() { selStart.order=0; selEnd.order=0; MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Pattern"))) { @@ -6534,6 +6539,7 @@ bool FurnaceGUI::loop() { } }); MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Instruments"))) { @@ -6577,6 +6583,7 @@ bool FurnaceGUI::loop() { e->curSubSong->rearrangePatterns(); }); MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Remove unused patterns"))) { @@ -6585,6 +6592,7 @@ bool FurnaceGUI::loop() { e->curSubSong->removeUnusedPatterns(); }); MARK_MODIFIED; + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } if (ImGui::Button(_("Remove unused instruments"))) { @@ -6638,6 +6646,7 @@ bool FurnaceGUI::loop() { selStart=cursor; selEnd=cursor; curOrder=0; + recalcTimestamps=true; MARK_MODIFIED; } ImGui::CloseCurrentPopup(); @@ -6656,6 +6665,7 @@ bool FurnaceGUI::loop() { MARK_MODIFIED; } updateROMExportAvail(); + recalcTimestamps=true; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); @@ -7368,6 +7378,12 @@ bool FurnaceGUI::loop() { } } + if (recalcTimestamps) { + logV("need to recalc timestamps..."); + e->calcSongTimestamps(); + recalcTimestamps=false; + } + sampleMapWaitingInput=(curWindow==GUI_WINDOW_INS_EDIT && sampleMapFocused); curWindowThreadSafe=curWindow; @@ -8582,6 +8598,7 @@ FurnaceGUI::FurnaceGUI(): noteInputPoly(true), notifyWaveChange(false), notifySampleChange(false), + recalcTimestamps(false), wantScrollListIns(false), wantScrollListWave(false), wantScrollListSample(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 60cc32da9..826909d71 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1714,6 +1714,7 @@ class FurnaceGUI { bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed; bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange, notifySampleChange; + bool recalcTimestamps; bool wantScrollListIns, wantScrollListWave, wantScrollListSample; bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool displayPendingSamples, replacePendingSample; diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 2cd8acb7a..406d0237c 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -392,7 +392,6 @@ void FurnaceGUI::drawOrders() { if (e->curOrders->ord[j][i]<(unsigned char)(DIV_MAX_PATTERNS-1)) e->curOrders->ord[j][i]++; } }); - e->calcSongTimestamps(); makeUndo(GUI_UNDO_CHANGE_ORDER); } else { orderCursor=j; @@ -400,7 +399,6 @@ void FurnaceGUI::drawOrders() { } } else { setOrder(i); - e->calcSongTimestamps(); if (orderEditMode!=0) { orderCursor=j; curNibble=false; @@ -441,7 +439,6 @@ void FurnaceGUI::drawOrders() { if (e->curOrders->ord[j][i]>0) e->curOrders->ord[j][i]--; } }); - e->calcSongTimestamps(); makeUndo(GUI_UNDO_CHANGE_ORDER); } else { orderCursor=j; @@ -449,7 +446,6 @@ void FurnaceGUI::drawOrders() { } } else { setOrder(i); - e->calcSongTimestamps(); if (orderEditMode!=0) { orderCursor=j; curNibble=false; diff --git a/src/gui/speed.cpp b/src/gui/speed.cpp index 02e04481a..c680b506f 100644 --- a/src/gui/speed.cpp +++ b/src/gui/speed.cpp @@ -58,6 +58,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (setHz<1) setHz=1; if (setHz>999) setHz=999; e->setSongRate(setHz); + recalcTimestamps=true; } if (tempoView) { ImGui::SameLine(); @@ -82,6 +83,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { e->curSubSong->speeds.len=1; }); if (e->isPlaying()) play(); + recalcTimestamps=true; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(_("click for one speed")); @@ -94,6 +96,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { e->curSubSong->speeds.val[3]=e->curSubSong->speeds.val[1]; }); if (e->isPlaying()) play(); + recalcTimestamps=true; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(_("click for groove pattern")); @@ -105,6 +108,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { e->curSubSong->speeds.val[1]=e->curSubSong->speeds.val[0]; }); if (e->isPlaying()) play(); + recalcTimestamps=true; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(_("click for two (alternating) speeds")); @@ -138,6 +142,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { e->curSubSong->speeds.val[i]=intVersion[i]; } }); + recalcTimestamps=true; if (e->isPlaying()) play(); MARK_MODIFIED; } @@ -151,6 +156,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->curSubSong->speeds.val[0],&_ONE,&_THREE)) { MARK_MODIFIED if (e->curSubSong->speeds.val[0]<1) e->curSubSong->speeds.val[0]=1; if (e->isPlaying()) play(); + recalcTimestamps=true; } if (e->curSubSong->speeds.len>1) { ImGui::SameLine(); @@ -158,6 +164,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->curSubSong->speeds.val[1],&_ONE,&_THREE)) { MARK_MODIFIED if (e->curSubSong->speeds.val[1]<1) e->curSubSong->speeds.val[1]=1; if (e->isPlaying()) play(); + recalcTimestamps=true; } } } @@ -172,6 +179,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1; if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255; e->virtualTempoChanged(); + recalcTimestamps=true; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(_("Numerator")); @@ -182,6 +190,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1; if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255; e->virtualTempoChanged(); + recalcTimestamps=true; } if (ImGui::IsItemHovered()) { ImGui::SetTooltip(_("Denominator (set to base tempo)")); @@ -198,6 +207,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (realTB<1) realTB=1; if (realTB>16) realTB=16; e->curSubSong->timeBase=realTB-1; + recalcTimestamps=true; } ImGui::SameLine(); ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD)); @@ -237,6 +247,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (patLen<1) patLen=1; if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS; e->curSubSong->patLen=patLen; + recalcTimestamps=true; } ImGui::TableNextRow(); @@ -253,6 +264,7 @@ void FurnaceGUI::drawSpeed(bool asChild) { if (curOrder>=ordLen) { setOrder(ordLen-1); } + recalcTimestamps=true; } ImGui::EndTable(); diff --git a/src/gui/sysManager.cpp b/src/gui/sysManager.cpp index 9f9de0318..4ebb1f888 100644 --- a/src/gui/sysManager.cpp +++ b/src/gui/sysManager.cpp @@ -110,6 +110,7 @@ void FurnaceGUI::drawSysManager() { if (picked!=DIV_SYSTEM_NULL) { if (e->changeSystem(i,picked,preserveChanPos)) { MARK_MODIFIED; + recalcTimestamps=true; if (e->song.autoSystem) { autoDetectSystem(); } @@ -179,6 +180,7 @@ void FurnaceGUI::drawSysManager() { showError(fmt::sprintf(_("cannot add chip! (%s)"),e->getLastError())); } else { MARK_MODIFIED; + recalcTimestamps=true; } if (e->song.autoSystem) { autoDetectSystem();