fix(bank_panel): drive new-content baseline re-arm from persist's load signal
The detector re-armed its GuidBaseline on a pointer compare (proj != lastProject), a weaker signal than persist's GUID-primary identity. On a load onto a recycled ReaProject* the baseline never reset, so the just-loaded project's pre-existing tracks diffed against the previous project and were mass-tagged into the active mode — opening a Design-saved project mis-tagged its Arrange tracks. Re-arm now rides persist's authoritative load signal via bankPanelNotifyProjectLoaded().
This commit is contained in:
+43
-15
@@ -227,15 +227,26 @@ struct PanelState {
|
||||
//
|
||||
// Each timer tick diffs the live track+item GUID set against the previous tick to
|
||||
// auto-tag content created SINCE the last tick into the then-active mode. The
|
||||
// baseline carries the first-poll-after-open guard so pre-existing content is never
|
||||
// mass-tagged (it stays Arrange). `lastProject` detects a project switch so the
|
||||
// baseline re-arms per project (a switch never diffs across two projects). Both live
|
||||
// for the extension's lifetime alongside the session, independent of panel open/close
|
||||
// — detection must run whether or not the dock is visible (content is created in the
|
||||
// arrange, not the panel).
|
||||
// baseline carries the first-poll-after-open guard (GuidBaseline self-arms on its
|
||||
// first observe()) so pre-existing content is never mass-tagged (it stays Arrange).
|
||||
//
|
||||
// Project-load re-arm is driven by persist's AUTHORITATIVE load lifecycle, NOT by a
|
||||
// pointer compare here. main.cpp calls bankPanelNotifyProjectLoaded() on the exact
|
||||
// tick persist restores a project's membership + active mode (the same tick it
|
||||
// reapplies the active mode); that sets reloadPending so the NEXT detect tick this
|
||||
// same tick re-baselines against the fully-loaded set and reports nothing new. This
|
||||
// replaces the former `proj != lastProject` re-arm, which used a WEAKER signal than
|
||||
// persist (pointer-only vs persist's GUID-primary identity) and so missed a load onto
|
||||
// a RECYCLED ReaProject* address — the just-loaded project's pre-existing tracks then
|
||||
// diffed against the previous project's stale baseline and were mass-tagged into the
|
||||
// active mode (the reload-mis-tag bug). Coordinating with persist's signal makes the
|
||||
// two identity checks agree by construction.
|
||||
//
|
||||
// Lives for the extension's lifetime alongside the session, independent of panel
|
||||
// open/close — detection must run whether or not the dock is visible (content is
|
||||
// created in the arrange, not the panel).
|
||||
GuidBaseline contentBaseline;
|
||||
ReaProject* lastProject = nullptr;
|
||||
bool sawProject = false; // false until the first detect tick sees a project
|
||||
bool reloadPending = false; // set by bankPanelNotifyProjectLoaded; drained next detect tick
|
||||
};
|
||||
|
||||
PanelState g_panel;
|
||||
@@ -714,14 +725,19 @@ void detectNewContent() {
|
||||
|
||||
ReaProject* proj = EnumProjects(-1, nullptr, 0);
|
||||
|
||||
// Project switch (or first ever tick) re-arms the first-poll guard so we never diff
|
||||
// across two projects. GUID-address recycling is bounded here: a missed reset can at
|
||||
// worst re-baseline against the wrong project for one tick; the identity-of-record
|
||||
// (persist's minted project GUID) governs the bank/model reload, not this detector.
|
||||
if (!g_panel.sawProject || proj != g_panel.lastProject) {
|
||||
// A project (re)load re-arms the first-poll guard so we never diff across two
|
||||
// projects. The signal is persist's — main.cpp calls bankPanelNotifyProjectLoaded()
|
||||
// on the tick persist restores the project's membership + active mode, which sets
|
||||
// reloadPending. Draining it here re-baselines against the fully-loaded set (that
|
||||
// same tick's reapply-active-mode enumerated those tracks, so they are present),
|
||||
// and the observe() below returns nothing new — pre-existing untagged tracks stay
|
||||
// Arrange. GuidBaseline self-arms on its first observe() for the very first tick, so
|
||||
// no separate first-tick handling is needed here. Using persist's GUID-primary load
|
||||
// signal (not a local pointer compare) is what fixes the reload-mis-tag: the two
|
||||
// identity checks can no longer diverge on a recycled ReaProject* address.
|
||||
if (g_panel.reloadPending) {
|
||||
g_panel.contentBaseline.reset();
|
||||
g_panel.lastProject = proj;
|
||||
g_panel.sawProject = true;
|
||||
g_panel.reloadPending = false;
|
||||
}
|
||||
|
||||
std::set<std::string> live;
|
||||
@@ -1141,6 +1157,18 @@ std::vector<std::string> bankPanelSelectedSampleIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
void bankPanelNotifyProjectLoaded() {
|
||||
// Persist restored a project's membership + active mode this tick (main.cpp calls
|
||||
// this from the same consumeLoadSignal() branch that reapplies the active mode).
|
||||
// Arm the new-content detector to re-baseline on its next tick so the just-loaded
|
||||
// project's pre-existing content is treated as the baseline (nothing new) rather
|
||||
// than diffed against the previous project and mass-tagged into the active mode.
|
||||
// A flag (not an inline reset) because detectNewContent owns the baseline and runs
|
||||
// later in the SAME OnTimer tick — it drains this and re-baselines against the live
|
||||
// set in one place, keeping the reset and the observe() adjacent and ordered.
|
||||
g_panel.reloadPending = true;
|
||||
}
|
||||
|
||||
void bankPanelRefresh() {
|
||||
// New-content auto-tag detection runs EVERY tick regardless of panel open/close:
|
||||
// tracks/items are created in the arrange view, not the panel, so detection must
|
||||
|
||||
Reference in New Issue
Block a user