diff --git a/COMPLETED.md b/COMPLETED.md index c4a3b22..f2d3391 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -284,6 +284,51 @@ round-trip of the lane index. REAPER-free, unit-tested; mirror of D1. CONTEXT.md --- +## D2-W2 — shell: lane application + new-content detection +**Goal:** The view shell applies the planner's managed-lane ops in the DAW and the +bank_panel timer detects new content and auto-tags it to the active mode. Resolves +the two flagged implementation design points (I_FIXEDLANE reorder/renumber +fragility; the auto-tag / manual-lane detection heuristic). See CONTEXT.md +§Two-canvas sub-phase (Module architecture — shell; New-content detection). +**Verify (in DAW):** Toggling a mode shows + plays only the active mode's managed +lane, hides + silences the inactive-mode lane, and **never touches a manual lane** +(its `C_LANEPLAYS` stays exactly as the user set it); new content created while a +mode is active is tagged to that mode; pre-existing content stays Arrange (no +mass-tag on the first poll after open). +**Depends on:** D2-W1. + +- [x] Apply managed-lane ops in the view shell (`I_FREEMODE`/`I_FIXEDLANE`/ + `C_LANEPLAYS`/`B_FIXEDLANE_HIDDEN` via the item/track info setters; + `UpdateTimeline()` after `I_FREEMODE`); **managed lanes only, never manual**. + Verify every flag name/signature against the SDK header. +- [x] New-content detection on the bank_panel timer: diff the live track/item GUID + set against the previous poll; tag any GUID new since the last poll to the + then-active mode, with a **first-poll-after-open guard** (pre-existing ⇒ Arrange, + no mass-tag) and the **manual-lane exemption** (items in a manual lane not tagged). +- [x] Resolve the manual-lane detection heuristic (which new items are exempt) and + the `I_FIXEDLANE` lane-identity fragility (index survival across lane + reorder/renumber/deletion) — the two open design points from CONTEXT.md. +- [x] D2-W1 review polish: document the one-managed-lane-per-mode-per-track + exclusivity assumption in `laneModeState` (comment / debug-guard); clarify the + `serialize()` one-line style note; optional round-trip tests for the + last-writer-wins lane-replace contract. + +**Notes/decisions:** +- Two new pure modules added with unit tests: `guid_diff` (diffs live track/item + GUID sets between polls) and `lane_keys` (manages lane identity via durable + `P_LANENAME` rather than the renumber-prone `I_FIXEDLANE` ordinal, reconciled each + apply — the resolution to the lane-identity fragility design point). CTest green. +- **Manual-lane protection:** a single pure predicate `isOnManualLane` is the + exclusive gate; manual lanes — including REAPER's default unnamed fixed lanes — + are provably never driven or auto-tagged. +- **Track-level auto-tag and park behavior is live.** Item-lane show/hide is + correctly structured but is a provable no-op on real projects until D2-W3 mints + the `reasampler:`-prefixed named lanes. End-to-end DAW verification of item-lane + show/hide is sequenced after D2-W3 for this reason. +- W1 review polish was folded in during this wave. + +--- + ## Milestone 7 — capture action family **Goal:** Bindable capture actions for master / selected tracks / selected items / razor area, each with wet-dry + tail options. CONTEXT.md §actions, Build order 7. @@ -433,3 +478,38 @@ path. - **Follow-ons noted, not done:** Manual fine-adjust UI; per-project persistence of the toggle (currently extension-session lifetime, resets to None on unload); T2 realtime tail. + +--- + +## T2 — realtime tail (follow-on to T1) +**Goal:** The parallel tail path for the M8 realtime backend, which does not drive +`RENDER_*`: record an 8 s-capped tail window past the range end, then **trim in a +PCM decay-scan** to the -72 dB point (Manual = record fixed tail, skip the scan). +See `docs/product/capture-tail.md` §The realtime path. +**Verify (in DAW):** A realtime Auto capture of a decaying source records ≥ the range +then trims at the -72 dB decay point (± inherent realtime tolerance); realtime tail is +**not** asserted bit-identical (documented non-determinism). +**Depends on:** T1, M8. + +- [x] Record `[start, end + clamp(tail, 8 s)]` (extend the record time selection in + `capture_realtime.cpp`); Manual skips the scan, Auto proceeds to it. +- [x] Pure decay-scan helper alongside `peaks`: `lastFrameAboveThreshold(interleaved, + channels, frames, linearThreshold) -> frameIndex` (backward scan, per-frame max-abs + across channels, no fold); unit-tested with a synthetic decaying ramp. (Spec §realtime + path option (a) — recommended over bending `computeEnvelope`.) +- [x] Realtime shell: read the recorded wav PCM into a float buffer, find the trim + frame, rewrite the file truncated (new I/O the backend does not do today). + +**Notes/decisions:** +- New pure module `wav_trim` (`src/wav_trim.{h,cpp}`): 32-bit-float WAV parse + header-aware + truncate plan (RIFF/data size rewrite). Rejects WAVE_FORMAT_EXTENSIBLE with non-float + SubFormat GUID. Depends on `peaks` for the `AudioSample` float alias. Unit-tested via a + new `wav_trim_tests` CTest target. +- `peaks` gained `lastFrameAboveThreshold` (backward PCM scan, per-frame max-abs across + channels, no fold) for the Auto decay scan. +- **Auto/Manual/Off semantics.** Auto: records `[start, end + 8 s cap]`, scans backward + for the last frame above -72 dBFS, truncates the WAV header-aware at that frame. Manual: + records `[start, end + fixed tail]`, skips the scan. Off: byte-identical to the pre-tail + exact-bounds capture. +- **Realtime tail is non-deterministic by design** (inherent to the realtime backend). + Bit-identical repeats are not asserted for the realtime path; this is documented, not a defect. diff --git a/PLAN.md b/PLAN.md index f637adc..5325c0b 100644 --- a/PLAN.md +++ b/PLAN.md @@ -65,39 +65,6 @@ landed milestone. --- -# Milestone T — capture tail (rider on the offline render path) - -> **Rider, not a new pillar.** Tail preservation wires into the already-shipped -> offline `OfflineRenderBackend` (M3/M7) — no new backend, no new render trigger. -> It takes a **T** tag (not an M-number) because it is an enhancement to landed -> capture, sequenced independently of M8–M11. Authoritative spec: -> **`docs/product/capture-tail.md`** (full `RENDER_*` values, the surgical -> `RENDER_NORMALIZE`, the realtime parallel path, invariant interactions, -> acceptance criteria, DAW-confirm items). Parameters set by Daniel: auto-trim -> threshold **-72 dB**, max-tail cap **8 s**. When a point lands, doc-keeper moves -> it to `COMPLETED.md`. - -## T2 — realtime tail (follow-on to T1) -**Goal:** The parallel tail path for the M8 realtime backend, which does not drive -`RENDER_*`: record an 8 s-capped tail window past the range end, then **trim in a -PCM decay-scan** to the -72 dB point (Manual = record fixed tail, skip the scan). -See `docs/product/capture-tail.md` §The realtime path. -**Verify (in DAW):** A realtime Auto capture of a decaying source records ≥ the range -then trims at the -72 dB decay point (± inherent realtime tolerance); realtime tail is -**not** asserted bit-identical (documented non-determinism). -**Depends on:** T1, M8. - -- [ ] Record `[start, end + clamp(tail, 8 s)]` (extend the record time selection in - `capture_realtime.cpp`); Manual skips the scan, Auto proceeds to it. -- [ ] Pure decay-scan helper alongside `peaks`: `lastFrameAboveThreshold(interleaved, - channels, frames, linearThreshold) -> frameIndex` (backward scan, per-frame max-abs - across channels, no fold); unit-tested with a synthetic decaying ramp. (Spec §realtime - path option (a) — recommended over bending `computeEnvelope`.) -- [ ] Realtime shell: read the recorded wav PCM into a float buffer, find the trim - frame, rewrite the file truncated (new I/O the backend does not do today). - ---- - # Phase D2 — Two-canvas (item-level mode projection; additive to D1) > **Design View sub-phase.** Extends D1's track-level mode projection to **item @@ -111,36 +78,7 @@ then trims at the -72 dB decay point (± inherent realtime tolerance); realtime > spec. Product framing: `docs/product/design-view.md` §Two-canvas direction. When a > point lands, doc-keeper moves it to `COMPLETED.md`. > -> **D2-W1 (pure lane extension) has landed** — see `COMPLETED.md`. - -## D2-W2 — shell: lane application + new-content detection -**Goal:** The view shell applies the planner's managed-lane ops in the DAW and the -bank_panel timer detects new content and auto-tags it to the active mode. Resolves -the two flagged implementation design points (I_FIXEDLANE reorder/renumber -fragility; the auto-tag / manual-lane detection heuristic). See CONTEXT.md -§Two-canvas sub-phase (Module architecture — shell; New-content detection). -**Verify (in DAW):** Toggling a mode shows + plays only the active mode's managed -lane, hides + silences the inactive-mode lane, and **never touches a manual lane** -(its `C_LANEPLAYS` stays exactly as the user set it); new content created while a -mode is active is tagged to that mode; pre-existing content stays Arrange (no -mass-tag on the first poll after open). -**Depends on:** D2-W1. - -- [ ] Apply managed-lane ops in the view shell (`I_FREEMODE`/`I_FIXEDLANE`/ - `C_LANEPLAYS`/`B_FIXEDLANE_HIDDEN` via the item/track info setters; - `UpdateTimeline()` after `I_FREEMODE`); **managed lanes only, never manual**. - Verify every flag name/signature against the SDK header. -- [ ] New-content detection on the bank_panel timer: diff the live track/item GUID - set against the previous poll; tag any GUID new since the last poll to the - then-active mode, with a **first-poll-after-open guard** (pre-existing ⇒ Arrange, - no mass-tag) and the **manual-lane exemption** (items in a manual lane not tagged). -- [ ] Resolve the manual-lane detection heuristic (which new items are exempt) and - the `I_FIXEDLANE` lane-identity fragility (index survival across lane - reorder/renumber/deletion) — the two open design points from CONTEXT.md. -- [ ] D2-W1 review polish: document the one-managed-lane-per-mode-per-track - exclusivity assumption in `laneModeState` (comment / debug-guard); clarify the - `serialize()` one-line style note; optional round-trip tests for the - last-writer-wins lane-replace contract. +> **D2-W1 (pure lane extension) and D2-W2 (shell: lane application + new-content detection) have landed** — see `COMPLETED.md`. ## D2-W3 — actions, persist wiring, panel UI **Goal:** Any new lane/mode-management actions, the persist slice serializing the