Make an FX reclaim step one whole track's chain: drop the resume cursor, yield only between tracks
This commit is contained in:
@@ -7,32 +7,9 @@ namespace reasampler::view {
|
||||
|
||||
namespace {
|
||||
|
||||
// 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& resumeAfterFxGuid,
|
||||
int resumeAfterSlot) {
|
||||
if (resumeAfterFxGuid.empty() && resumeAfterSlot < 0) return ops;
|
||||
for (std::size_t i = 0; i < ops.size(); ++i) {
|
||||
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());
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// Slot keying, deliberately: the live chain IS the target set, so its positions
|
||||
// are current by construction. Identity keying would drop an FX REAPER reports no
|
||||
// GUID for, and reclaim has to reach those too — the fxGuid rides along only so
|
||||
// the resume point has something to match.
|
||||
// GUID for, and reclaim has to reach those too.
|
||||
std::vector<FxOfflineOp> offlineTargets(const std::string& trackGuid,
|
||||
const std::vector<std::string>& liveFxGuids) {
|
||||
std::vector<FxOfflineOp> ops;
|
||||
@@ -70,14 +47,13 @@ std::vector<FxTrackWork> desiredFxState(const ViewModeModel& model,
|
||||
if (liveTrackGuids.count(guid) == 0) continue; // dead track — nothing to write
|
||||
|
||||
if (!model.leafBelongsToMode(guid, targetMode)) {
|
||||
offline.push_back(FxTrackWork{guid, FxWorkKind::Offline, {}, {}});
|
||||
offline.push_back(FxTrackWork{guid, FxWorkKind::Offline, {}});
|
||||
continue;
|
||||
}
|
||||
// Active with the FX half already released: the chain is back online and
|
||||
// the record is only waiting on its flags. Nothing owed either way.
|
||||
if (!snap.fxHeld) continue;
|
||||
online.push_back(
|
||||
FxTrackWork{guid, FxWorkKind::Online, fxRestoreTargets(model, guid), {}});
|
||||
online.push_back(FxTrackWork{guid, FxWorkKind::Online, fxRestoreTargets(model, guid)});
|
||||
}
|
||||
|
||||
online.reserve(online.size() + offline.size());
|
||||
@@ -87,14 +63,9 @@ std::vector<FxTrackWork> desiredFxState(const ViewModeModel& model,
|
||||
|
||||
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,
|
||||
work.resumeAfterSlot),
|
||||
liveFxGuids);
|
||||
}
|
||||
return resolveFxRestore(
|
||||
remainingAfter(work.targets, work.resumeAfterFxGuid, work.resumeAfterSlot), liveFxGuids);
|
||||
if (work.kind == FxWorkKind::Offline)
|
||||
return resolveFxRestore(offlineTargets(work.trackGuid, liveFxGuids), liveFxGuids);
|
||||
return resolveFxRestore(work.targets, liveFxGuids);
|
||||
}
|
||||
|
||||
} // namespace reasampler::view
|
||||
|
||||
+10
-26
@@ -19,9 +19,13 @@ enum class FxWorkKind {
|
||||
Offline, // reclaim: every live FX offline
|
||||
};
|
||||
|
||||
// One track's whole FX move. Track-granular so a drain may stop between tracks,
|
||||
// and resumable WITHIN a track by FX identity: a slot cursor would silently skip
|
||||
// or repeat a plugin if the chain were edited between two ticks.
|
||||
// One track's whole FX move, and the ATOMIC unit of work: a drain may stop
|
||||
// between items, never inside one, so an item is either untouched or complete
|
||||
// and no partial state survives a tick boundary. Do not reintroduce a
|
||||
// within-track resume cursor — carrying one across ticks meant reconciling it
|
||||
// against a chain the user may have edited in between, and the case an FX with
|
||||
// no reported identity forced (a cursor that reads as "nothing done yet" and
|
||||
// re-walks the item forever) was a live non-termination hazard.
|
||||
struct FxTrackWork {
|
||||
std::string trackGuid;
|
||||
FxWorkKind kind = FxWorkKind::Offline;
|
||||
@@ -32,28 +36,8 @@ 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 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 &&
|
||||
resumeAfterSlot == o.resumeAfterSlot;
|
||||
return trackGuid == o.trackGuid && kind == o.kind && targets == o.targets;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,8 +66,8 @@ std::vector<FxOfflineOp> fxRestoreTargets(const ViewModeModel& model,
|
||||
|
||||
// Resolves one work item against the chain as it stands, where `liveFxGuids[i]`
|
||||
// is the identity of the FX at slot `i` right now (empty when REAPER reported
|
||||
// none). Honors the resume point, then matches by identity: an identity that is
|
||||
// not live is DROPPED, never re-pointed at a slot.
|
||||
// none) — the item's WHOLE move, matched by identity: an identity that is not
|
||||
// live is DROPPED, never re-pointed at a slot.
|
||||
FxRestoreResolution resolveFxWork(const FxTrackWork& work,
|
||||
const std::vector<std::string>& liveFxGuids);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user