From 92762e2b6c980d00ae36d9de4db4ba4636e78fe2 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Thu, 23 Jul 2026 16:42:42 -0400 Subject: [PATCH] Docs: reconcile to two-scope capture model (master removed) Update the CLAUDE.md FX-scope invariant to item+track only; note master removal in COMPLETED.md; drop master references from the tail spec and PLAN Milestone T; realtime now taps the selected track. --- CLAUDE.md | 2 +- COMPLETED.md | 3 ++- PLAN.md | 12 ++++++------ docs/product/capture-tail.md | 14 ++++++-------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cf696a0..f11d23e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,4 +85,4 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde - **Non-destructive:** capture never mutates source items or tracks; the realtime backend's temp track is created and removed cleanly, and source routing is restored. - **Exact bounds:** no rounding of the requested range; no added silence unless a tail is explicitly requested; channel count preserved (no silent stereo fold). - **Relative paths only** in the persisted `BankIndex`. -- **Capture FX scope:** item capture = item/take FX only; track capture = item + the track's own FX; master = full chain. For item/track, the out-of-scope chain (ancestors + master, plus the item's own track for item scope) is rendered with its FX, gain, and pan/width/pan-law/mode neutralized to unity — so captures are uncolored by, and independent of, the parent/folder/master chain. Range (time selection or razor) is orthogonal. +- **Capture FX scope:** two scopes only — item = item/take FX only; track = item FX + the selected track's own track FX. There is no master scope (to capture the master, render a track instead). For both scopes, the out-of-scope chain (ancestors + master track, plus the item's own track for item scope) has its FX, gain, and pan/width/pan-law/mode neutralized to unity — the master track is bypassed as out-of-scope chain, not captured as a scope. Range (time selection or razor) is orthogonal. diff --git a/COMPLETED.md b/COMPLETED.md index ab81d53..dcc69d0 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -281,5 +281,6 @@ auto-inserts into the arrange. **Superseded / reworked (post-landing):** - The four wet source-mode actions (master/tracks/items/razor) were replaced by **three FX-scope actions** — `capture item`, `capture track`, `capture master` — with range (razor-else-time-selection) inferred orthogonally. This fixed the defect where item captures were rendered through the parent FX chain. -- **FX-scope semantics:** item = item/take FX only; track = item FX + the selected track's own track FX; master = full chain. For item/track, the out-of-scope chain (ancestors + master, plus the item's own track for item scope) is neutralized during the render. +- **FX-scope semantics (initial rework):** item = item/take FX only; track = item FX + the selected track's own track FX; master = full chain. For item/track, the out-of-scope chain (ancestors + master, plus the item's own track for item scope) is neutralized during the render. +- **Master scope subsequently removed:** capture is now **two scopes — item and track only**. `CAPTURE_MASTER` and `CAPTURE_MASTER_REALTIME` are retired (to capture the master, render a track instead). The master track is still neutralized as out-of-scope chain for both item and track captures; it is a bypass target, not a capture scope. The realtime backend taps the selected track (track scope only; item realtime deferred). - **`FxBypassGuard` (RAII):** snapshot → neutralize (FX bypassed via `I_FXEN`; gain zeroed via `D_VOL`; pan/width/pan-law/mode set to unity via `D_PAN`/`D_WIDTH`/`D_PANLAW`/`I_PANMODE`) → render → restore. Non-destructive. This guard is the reusable mechanism M8 (realtime backend) and M10 (null-test / true dry) build on. diff --git a/PLAN.md b/PLAN.md index fccacd7..f47c8c6 100644 --- a/PLAN.md +++ b/PLAN.md @@ -142,12 +142,12 @@ then trims at the -72 dB decay point (± inherent realtime tolerance); realtime frame, rewrite the file truncated (new I/O the backend does not do today). ## Milestone T open questions -- **Auto as the shipped-action default?** Whether `CAPTURE_ITEM`/`CAPTURE_TRACK`/ - `CAPTURE_MASTER` (currently all TailMode::None) flip to Auto, gain a "…with tail" - variant, or take a modifier. Product call for Daniel; **leaning** paired variant / - toggle over silently changing the exact-bounds default. Not blocking T1 (the - request-level three-state contract is independent). (touches `render_settings.cpp` - action table + `actions`.) +- **Auto as the shipped-action default?** Whether `CAPTURE_ITEM`/`CAPTURE_TRACK` + (currently all TailMode::None) flip to Auto, gain a "…with tail" variant, or take + a modifier. Product call for Daniel; **leaning** paired variant / toggle over + silently changing the exact-bounds default. Not blocking T1 (the request-level + three-state contract is independent). (touches `render_settings.cpp` action table + + `actions`.) --- diff --git a/docs/product/capture-tail.md b/docs/product/capture-tail.md index 138f431..fd63534 100644 --- a/docs/product/capture-tail.md +++ b/docs/product/capture-tail.md @@ -173,7 +173,7 @@ as configurable — noted below — not to let a single request uncap it.) The request already has `renderTail: bool` + `tailMs: double`. The auto-trim mode adds a third state, so the wiring is a small enum, not a bool: -- **None** (default for null-test / verify captures, and the current three-scope +- **None** (default for null-test / verify captures, and the current two-scope action defaults): `RENDER_TAILFLAG = 0`, `RENDER_TAILMS = 0`, normalize = disable-all. Exact bounds. Byte-identical to today. - **Auto** (the new user-facing default for tail-on captures): tailFlag `1`, @@ -194,8 +194,8 @@ applies the returned values. ## The realtime path (parallel, follow-on) The M8 `RealtimeRecordBackend` (`src/capture_realtime.cpp`) does **not** drive -`RENDER_*` at all — it records the master send into a hidden temp track over a time -selection. So none of the offline tail machinery reaches it. It needs a **parallel** +`RENDER_*` at all — it taps the selected track's own output (post-fader, pre-parent) +into a hidden temp track over a time selection (track scope only). So none of the offline tail machinery reaches it. It needs a **parallel** tail path, and it is explicitly a **follow-on to the offline tail** (offline lands first; realtime tail is a later increment). @@ -260,7 +260,7 @@ held to bit-identical repeats. dry no-tail capture re-inserted at its source position must null against the source; a tail would extend the file past the source region and break the null. **The tail must be OFF for null-test and verification captures** — this is a hard - rule, not a default. (The three-scope capture *actions* may default to a tail once + rule, not a default. (The two capture *actions* may default to a tail once this ships; the *verify* action never does.) - **Determinism holds for offline.** The offline auto-trim path is deterministic: fixed threshold + fixed cap + a boundary-only trim = two identical requests @@ -270,8 +270,6 @@ held to bit-identical repeats. The tail render still goes through `FxBypassGuard` for track/item scopes (`render_settings.h §FxBypassPlan`, `CLAUDE.md §Capture FX scope`). This is exactly what we want: the tail is the *in-scope FX decay*. - - **Master scope:** whole chain in scope → the tail is the full master-chain - reverb/release decay. Correct. - **Track scope:** the track's own FX are in scope, ancestors/master bypassed → the tail is **the track's own reverb/delay decay**, not the parent bus's. A track with a reverb plugin captures that reverb's tail; a track feeding a folder reverb @@ -386,8 +384,8 @@ Neither is in scope now; both are single-constant seams so promotion is cheap. trailing edge, and the tail is appended after `ENDPOS`. But a range that itself ends in near-silence before a loud transient is the edge case to check the trim doesn't over-eat.) -- **Auto as the action default?** Should the three shipped capture actions - (`CAPTURE_ITEM` / `CAPTURE_TRACK` / `CAPTURE_MASTER`, currently all TailMode::None +- **Auto as the action default?** Should the two shipped capture actions + (`CAPTURE_ITEM` / `CAPTURE_TRACK`, currently all TailMode::None per `render_settings.cpp §captureActionTable`) flip to Auto tail by default once this ships, or should tail be a separate action variant / a modifier? Product call for Daniel. **Leaning:** a per-action-family toggle or a paired "…​with tail"