Fix five Wave-1 pure-layer FX/park review findings: resume-point stall, stale doc comments, implicit mode read, keying reset
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
#pragma once
|
||||
// A comparable fingerprint of what an apply put into the project, so a reconcile
|
||||
// can ask "does the model differ from what we applied?" rather than "did an undo
|
||||
// happen?". The second question is not the same one: an undo that touched
|
||||
// nothing we drive still answers yes, and repairing on it re-enables state the
|
||||
// user disabled by hand.
|
||||
// can ask "did our applied output drift from what a restore would drive it back
|
||||
// to?" rather than "did an undo happen?". The second question is not the same
|
||||
// one: an undo that touched nothing we drive still answers yes, and repairing on
|
||||
// it re-enables state the user disabled by hand. Narrower still than "did the
|
||||
// model change?": membership, lane ownership and the solo cache are excluded by
|
||||
// design (see AppliedState below), so an edit to any of those changes what a
|
||||
// future apply must drive while leaving this fingerprint bit-identical. Not a
|
||||
// sole reapply trigger for anything membership can affect.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
+20
-11
@@ -7,15 +7,22 @@ namespace reasampler::view {
|
||||
|
||||
namespace {
|
||||
|
||||
// Everything up to and including the first op naming `resumeAfter` is already
|
||||
// done. Not found ⇒ nothing dropped: the identity left the chain between ticks,
|
||||
// and re-walking a compare-before-write list beats trusting a cursor that no
|
||||
// longer points at anything.
|
||||
// Everything up to and including the resume point is already done. An empty
|
||||
// `resumeAfterFxGuid` is ambiguous by itself — "nothing done yet" and "the last
|
||||
// completed op had no identity" look identical — so an empty guid falls back to
|
||||
// matching by slot instead; `resumeAfterSlot < 0` is what actually means
|
||||
// "nothing done yet". Not found ⇒ nothing dropped: the resume point left the
|
||||
// chain between ticks, and re-walking a compare-before-write list beats
|
||||
// trusting a cursor that no longer points at anything.
|
||||
std::vector<FxOfflineOp> remainingAfter(const std::vector<FxOfflineOp>& ops,
|
||||
const std::string& resumeAfter) {
|
||||
if (resumeAfter.empty()) return ops;
|
||||
const std::string& resumeAfterFxGuid,
|
||||
int resumeAfterSlot) {
|
||||
if (resumeAfterFxGuid.empty() && resumeAfterSlot < 0) return ops;
|
||||
for (std::size_t i = 0; i < ops.size(); ++i) {
|
||||
if (ops[i].fxGuid == resumeAfter)
|
||||
const bool isResumePoint = resumeAfterFxGuid.empty()
|
||||
? ops[i].slot == resumeAfterSlot
|
||||
: ops[i].fxGuid == resumeAfterFxGuid;
|
||||
if (isResumePoint)
|
||||
return std::vector<FxOfflineOp>(ops.begin() + static_cast<std::ptrdiff_t>(i) + 1,
|
||||
ops.end());
|
||||
}
|
||||
@@ -54,15 +61,15 @@ std::vector<FxOfflineOp> fxRestoreTargets(const ViewModeModel& model,
|
||||
}
|
||||
|
||||
std::vector<FxTrackWork> desiredFxState(const ViewModeModel& model,
|
||||
const std::string& targetMode,
|
||||
const std::set<std::string>& liveTrackGuids) {
|
||||
std::vector<FxTrackWork> online;
|
||||
std::vector<FxTrackWork> offline;
|
||||
|
||||
const std::string& activeMode = model.activeModeId();
|
||||
for (const auto& [guid, snap] : model.snapshots()) { // std::map ⇒ GUID order
|
||||
if (liveTrackGuids.count(guid) == 0) continue; // dead track — nothing to write
|
||||
|
||||
if (!model.leafBelongsToMode(guid, activeMode)) {
|
||||
if (!model.leafBelongsToMode(guid, targetMode)) {
|
||||
offline.push_back(FxTrackWork{guid, FxWorkKind::Offline, {}, {}});
|
||||
continue;
|
||||
}
|
||||
@@ -82,10 +89,12 @@ FxRestoreResolution resolveFxWork(const FxTrackWork& work,
|
||||
const std::vector<std::string>& liveFxGuids) {
|
||||
if (work.kind == FxWorkKind::Offline) {
|
||||
return resolveFxRestore(
|
||||
remainingAfter(offlineTargets(work.trackGuid, liveFxGuids), work.resumeAfterFxGuid),
|
||||
remainingAfter(offlineTargets(work.trackGuid, liveFxGuids), work.resumeAfterFxGuid,
|
||||
work.resumeAfterSlot),
|
||||
liveFxGuids);
|
||||
}
|
||||
return resolveFxRestore(remainingAfter(work.targets, work.resumeAfterFxGuid), liveFxGuids);
|
||||
return resolveFxRestore(
|
||||
remainingAfter(work.targets, work.resumeAfterFxGuid, work.resumeAfterSlot), liveFxGuids);
|
||||
}
|
||||
|
||||
} // namespace reasampler::view
|
||||
|
||||
@@ -32,14 +32,28 @@ struct FxTrackWork {
|
||||
// read it, so resolveFxWork synthesises the targets from what it is handed.
|
||||
std::vector<FxOfflineOp> targets;
|
||||
|
||||
// Identity of the last FX this item's drain completed; empty ⇒ none yet. An
|
||||
// optimisation only: every write is compare-before-write, so an identity that
|
||||
// has since left the chain safely restarts the item rather than stranding it.
|
||||
// Identity of the last FX this item's drain completed; empty means EITHER
|
||||
// nothing has completed yet OR the completed FX itself carried no identity
|
||||
// (REAPER reports no GUID for it, or it came from a Slot-keyed record) — see
|
||||
// resumeAfterSlot below, which disambiguates the two. This is load-bearing
|
||||
// for forward progress, not an optimisation: without the disambiguation, an
|
||||
// unidentified FX would look like "none yet" and the drain would restart at
|
||||
// the top of the list every tick, forever. Compare-before-write makes every
|
||||
// write idempotent, so an identity that has since left the chain safely
|
||||
// restarts the item rather than stranding it.
|
||||
std::string resumeAfterFxGuid;
|
||||
|
||||
// Position of the last completed op, consulted only when resumeAfterFxGuid
|
||||
// is empty, to tell "resumed past an unidentified FX at this slot" apart
|
||||
// from "nothing done yet" (-1). Every FxOfflineOp carries its slot
|
||||
// regardless of keying, so this is available for both Online and Offline
|
||||
// work.
|
||||
int resumeAfterSlot = -1;
|
||||
|
||||
bool operator==(const FxTrackWork& o) const {
|
||||
return trackGuid == o.trackGuid && kind == o.kind && targets == o.targets &&
|
||||
resumeAfterFxGuid == o.resumeAfterFxGuid;
|
||||
resumeAfterFxGuid == o.resumeAfterFxGuid &&
|
||||
resumeAfterSlot == o.resumeAfterSlot;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,7 +65,13 @@ struct FxTrackWork {
|
||||
// opportunistic reclaim that may be deferred indefinitely without losing
|
||||
// anything. Within a kind, GUID order — deterministic, so a drain's position in
|
||||
// the list survives a re-derivation.
|
||||
//
|
||||
// `targetMode` is explicit, mirroring planToggle, rather than read from
|
||||
// `model.activeModeId()`: applyMode sets the active mode LAST, after its park
|
||||
// and restore loops, so a caller reading it from inside or before that block
|
||||
// would classify every track against the OUTGOING mode instead.
|
||||
std::vector<FxTrackWork> desiredFxState(const ViewModeModel& model,
|
||||
const std::string& targetMode,
|
||||
const std::set<std::string>& liveTrackGuids);
|
||||
|
||||
// One track's Online targets from its park record's FX half; empty when the
|
||||
|
||||
@@ -278,7 +278,9 @@ void ViewModeModel::releaseSnapshotFx(const std::string& guid) {
|
||||
if (it == snapshots_.end()) return;
|
||||
it->second.fxHeld = false;
|
||||
it->second.fxOffline.clear();
|
||||
it->second.fxKeying = FxKeying::Identity;
|
||||
// fxKeying is left as captured: the record's own keying, not a claim about
|
||||
// one it never had. Harmless today (fxOffline is empty right above), but
|
||||
// whoever reads fxKeying after this point should see the truth.
|
||||
eraseIfSpent(snapshots_, it);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user