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)
This commit is contained in:
tildearrow 2025-10-29 17:56:25 -05:00
parent 3b93c4e0ec
commit f990dee0c1
9 changed files with 69 additions and 25 deletions

View file

@ -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;

View file

@ -381,6 +381,7 @@ void FurnaceGUI::drawPalette() {
showError("cannot add chip! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
recalcTimestamps=true;
}
ImGui::CloseCurrentPopup();
if (e->song.autoSystem) {

View file

@ -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<unsigned short,DivPattern*> 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) {

View file

@ -459,6 +459,7 @@ void FurnaceGUI::doReplace() {
if (!curQueryResults.empty()) {
MARK_MODIFIED;
}
recalcTimestamps=true;
if (!us.pat.empty()) {
undoHist.push_back(us);

View file

@ -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),

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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();