tracking: read the ledger's version, not just write it; clear owned on any block; channel-correct prune recovery

This commit is contained in:
2026-07-30 20:11:05 -04:00
parent 7f70d94228
commit 45b87dc2ff
27 changed files with 432 additions and 168 deletions
+4 -4
View File
@@ -455,13 +455,13 @@ void RunRecaptureFromSource(ReaSamplerSession& session)
// One batched undo point around the in-place mutation; index-only ext-state,
// nothing placed on the timeline.
Undo_BeginBlock2(nullptr);
// Before the index outcome is even known: the render above already wrote the file,
// and makeUniqueTag guarantees a NEW path, so this is a second record carrying the
// same sampleId as the original. The superseded file becomes an orphan for prune.
session.recordCreated(updated, tracking::OriginKind::Recapture);
const bool changed = session.book().updateSampleInPlace(sampleId, updated);
if (changed)
{
// Record the regenerated file's birth; the superseded file becomes an orphan
// for prune to reclaim. A re-capture onto the same path is a dedup no-op, so
// the original birth record — not this one — stays authoritative.
session.recordCreated(updated, tracking::OriginKind::Recapture);
// Regenerating the same id's audio is exactly why instances need the generation
// bump — they'd otherwise keep playing stale audio until reload. Bumped inside
// the undo block so undo rolls back the generation with the rest of the blob.
@@ -250,9 +250,6 @@ CaptureResult captureAndIndexOne(ReaSamplerSession& session,
// AddResult tells a fresh add from a hash-dedup collapse, so the assign path (S8) can
// target the sample actually in the bank (the existing entry on a collapse).
const model::AddResult addResult = session.bank().add(res.sample);
// Record the birth at the same point the Sample is added, regardless of the index
// AddResult — even a hash-collapse still WROTE a file the tool owns, and the ledger
// dedups a repeat path itself (prune reconciles ledger vs index later).
session.recordCreated(res.sample, tracking::OriginKind::Capture);
// Resolve the LANDED bank-index id into res.sample.id for the S8 assign path: the new
+19 -7
View File
@@ -31,8 +31,6 @@ void CommitRealtimeResult(ReaSamplerSession& session, const CaptureResult& res)
return;
}
session.bank().add(res.sample);
// Record the birth regardless of the index AddResult — even a hash-collapse still
// wrote a file the tool owns; the ledger dedups a repeat path itself.
session.recordCreated(res.sample, tracking::OriginKind::Capture);
// A capture add changes what a live instance could play, so bump the generation
// before persisting to refresh instances.
@@ -56,14 +54,28 @@ void DriveRealtimeCapture(ReaSamplerSession& session)
{
RealtimeTickResult r = g_rtBackend.abort(*g_rtCapture);
// Log without persisting — we restored into the original project but must
// not persist into the now-active foreign one. A Failed abort surfaces
// not persist into the now-active foreign one: the ledger and bank we would
// have to write belong to the project we just left. A Failed abort surfaces
// abort()'s own message, distinguishing a clean tab-switch abort from the
// closed-project case (nothing restored because the pointers were already
// freed).
if (r.status == RealtimeTickStatus::Done)
ShowConsoleMsg("ReaSampler realtime capture: project switched mid-record -- "
"captured audio restored into the original project; not "
"persisted to avoid crossing projects.\n");
//
// The abort already moved the recorded WAV into the ORIGINAL project's bank
// folder, so it survives here untracked — deleting a user's just-recorded
// audio is the destructive direction and is not this shell's call, so the
// path is NAMED instead and the operator decides (docs/TODO.md).
if (r.status == RealtimeTickStatus::Done && r.result.status == CaptureStatus::Ok)
ShowConsoleMsg(("ReaSampler realtime capture: project switched mid-record -- "
"captured audio restored into the original project; not "
"persisted to avoid crossing projects. The recorded file was "
"left in the original project's bank folder as '" +
r.result.sample.relativePath +
"', untracked -- reopen that project and re-import it, or "
"delete it by hand.\n").c_str());
else if (r.status == RealtimeTickStatus::Done)
ShowConsoleMsg(("ReaSampler realtime capture: project switched mid-record -- "
"the capture was aborted and produced no usable file: " +
r.result.message + "\n").c_str());
else
ShowConsoleMsg(("ReaSampler realtime capture: project changed mid-record -- " +
r.result.message + "\n").c_str());