Split FX out of the park plan: flag-only plans, derived online-first FX work list, two-half record release, applied-state fingerprint

This commit is contained in:
2026-08-06 16:31:59 -04:00
parent 3eae942625
commit 2423225f0c
14 changed files with 897 additions and 164 deletions
+38 -18
View File
@@ -196,6 +196,11 @@ struct FolderTree {
// parking — restore's source of truth. Ints, not bools, so a snapshot
// faithfully round-trips whatever REAPER reported (defensive against
// non-0/1 values).
//
// The two halves are released INDEPENDENTLY. The flag half is spent the moment a
// restore writes it back; the FX half has to outlive that, because it is the only
// record of which plugins the USER had offline before the park — release it with
// the flag half and a later online blanket-enables them.
struct TrackSnapshot {
int showInTcp = 0; // B_SHOWINTCP prior value
int showInMixer = 0; // B_SHOWINMIXER prior value
@@ -208,15 +213,19 @@ struct TrackSnapshot {
std::vector<FxOfflineState> fxOffline;
FxKeying fxKeying = FxKeying::Identity;
bool flagsHeld = true; // false ⇒ a restore already wrote the flags back
bool fxHeld = true; // false ⇒ the chain is back online; nothing left to restore
bool operator==(const TrackSnapshot& o) const {
return showInTcp == o.showInTcp && showInMixer == o.showInMixer &&
mainSend == o.mainSend && fxEnable == o.fxEnable &&
fxOffline == o.fxOffline && fxKeying == o.fxKeying;
fxOffline == o.fxOffline && fxKeying == o.fxKeying &&
flagsHeld == o.flagsHeld && fxHeld == o.fxHeld;
}
};
// Which scalar flag a TrackFlagOp drives. FX-offline is carried separately (it
// is per-slot, variable length) — see TrackParkPlan::fxOffline.
// Which scalar flag a TrackFlagOp drives. FX-offline is not one of these and is
// not planned beside them — it is derived from view state (core/view/fx_state).
enum class Flag {
ShowInTcp, // B_SHOWINTCP
ShowInMixer, // B_SHOWINMIXER
@@ -249,14 +258,12 @@ struct LanePlayOp {
}
};
// The complete set of operations to park one inactive leaf, or restore one
// leaf. Park uses fixed zeros; restore uses a snapshot's values. Park's
// fxOffline ops are slot-keyed (every live slot goes offline); restore's carry
// the snapshot's keying and are resolved against the live chain by
// resolveFxRestore before any write.
// One leaf's FLAG-DOMAIN plan: park writes fixed zeros, restore writes the
// snapshot's captured values. Per-FX offline is deliberately absent — it is a
// derived function of view state (core/view/fx_state), not a peer of the flags,
// and carrying it here only ever produced an always-empty park-side vector.
struct TrackPlan {
std::vector<TrackFlagOp> flags;
std::vector<FxOfflineOp> fxOffline;
std::vector<TrackFlagOp> flags;
};
// The plan for a toggle to a target mode. Parents and show-both leaves never
@@ -295,11 +302,27 @@ public:
bool setActiveMode(const std::string& modeId);
// The shell calls store before it parks a track, so restore survives a save.
// Both halves are held from here; each is released by the act that spends it.
void storeSnapshot(const std::string& guid, const TrackSnapshot& snap);
void clearSnapshot(const std::string& guid);
void clearSnapshot(const std::string& guid); // both halves at once
const TrackSnapshot* snapshot(const std::string& guid) const;
const std::map<std::string, TrackSnapshot>& snapshots() const { return snapshots_; }
// A held half is one nothing has spent yet. `snapshot()` returning non-null is
// NOT that question: a record outlives its flag half, so a caller that reads
// the flag values off a record without asking here will drive stale ones.
bool holdsSnapshotFlags(const std::string& guid) const;
bool holdsSnapshotFx(const std::string& guid) const;
// Spent by the restore that writes the captured flags back. Zeroes them, so a
// record that no longer claims them cannot be read for them either.
void releaseSnapshotFlags(const std::string& guid);
// Spent only once the chain is ACTUALLY back online — not by the flag restore.
// Releasing both together destroys the only record of which plugins the user
// had offline before the park, and a later online then blanket-enables them.
void releaseSnapshotFx(const std::string& guid);
// Drops every snapshot whose GUID is NOT in `liveGuids`, and prunes the solo
// cache the same way (see SoloCache::reconcile). Returns the count of
// SNAPSHOTS removed — the solo cache's own count is available from it directly.
@@ -330,10 +353,7 @@ public:
// restored from it. Parents and show-both leaves are never parked.
// Untagged leaves are Arrange members and park/restore accordingly. Tree
// membership is the enumeration source, so stale membership GUIDs absent
// from the tree are ignored.
//
// Park plans here carry an empty fxOffline vector — the D2 shell expands
// per-FX offline writes via TrackFX_GetCount (not available to the pure model).
// from the tree are ignored. Flag domain only; FX comes from desiredFxState.
TogglePlan planToggle(const FolderTree& tree, const std::string& targetMode) const;
// Managed lanes only, from the ownership index — the set a toggle may
@@ -357,11 +377,11 @@ private:
view::SoloCache soloCache_; // modeId -> guid -> raw I_SOLO
};
// Fixed-zero park plan for one leaf, offlining `fxCount` slots.
TrackPlan makeParkPlan(const std::string& guid, int fxCount);
// Fixed-zero park plan for one leaf.
TrackPlan makeParkPlan(const std::string& guid);
// Restore plan for one leaf from its snapshot — every flag to its captured
// value, never a default.
// value, never a default. Callers must have checked holdsSnapshotFlags.
TrackPlan makeRestorePlan(const std::string& guid, const TrackSnapshot& snap);
// Auto-tag decision: new content takes the active mode at creation; the