docs: record the bake-prints-limiter track and close Phase Gamma

This commit is contained in:
2026-08-02 21:32:04 -04:00
parent 3313ac6b96
commit 53e7d35178
2 changed files with 64 additions and 48 deletions
+45
View File
@@ -1249,6 +1249,51 @@ limiter. Until that lands this is a recorded, known limitation, not an oversight
**Neither track has been verified in a running DAW; both are asserted in CTest only.**
### Γ-W3-T3 — bake-prints-limiter
The bake's master stage now prints the limiter as well as the gain multiply, closing the
audible gap Γ-W3-T2 recorded and left open: a capture baked with the limiter engaged
returns limited audio rather than unlimited audio. `renderBake`
(`core/instrument/bake/bake_render.cpp`) instantiates its own `Limiter` — the same
bake-only-engine precedent its `VoiceEngine` already set — never linked into
`reaper_reasampler`; `src/app/CMakeLists.txt`'s exclusion comment names the limiter
alongside `sampler_core`/`pitch_shift`/the filter, so the extension's link graph gains no
new edge.
**The lookahead needed compensation, which was open at spec time.** The limiter delays its
output by `kLimiterLookaheadSeconds` (0.002 s = 96 samples at 48 kHz), so the render's
buffers carry `renderFrames() + flushFrames` frames, the extra span fed silence rather than
more rendered audio, and the capture is read out starting at `leadInFrames + flushFrames`
instead of `leadInFrames` alone — the file is the same frames it would be bypassed, not the
same capture shifted 2 ms late.
**A sequencing trap, recorded inline at the call site.** `Limiter::prepare()` ends by
calling `reset()`, which snaps to whatever the enable target already is, so the render calls
`setEnabled(true)` before `prepare()`. Reversed, the limiter would take its live-engage path
instead — `process()`'s prime-then-fade — muting and then fading in the first ~12 ms of
every capture (the delay-line prime plus `kLimiterMuteSeconds`, per `limiter.h`).
**The bypassed path is unchanged.** `test_bake_render.cpp` asserts bypass ≡ engaged
bit-for-bit under the ceiling (a ramp fixture at unity gain, verified against the source
sample for sample too) and separately confirms the printed-limiter path holds the ceiling
and stays stereo-linked under a DC fixture driven well past it; repeat bakes stay
bit-identical with the limiter engaged as well, since `renderBake` builds a fresh `Limiter`
per call and `prepare()` zeroes every one of its state fields.
**Double-limiting is a named boundary, not a defect** (`bake/CLAUDE.md`): a printed capture
replayed through an engaged limiter is limited twice. The post-bake reset ordinarily
prevents it, since `limiterEnabled` is not on the survive list.
**`bake/CLAUDE.md`'s invariant is corrected alongside the code.** The text Γ-W3-T2 left in
place ("the limiter is not printed") is replaced with "the whole chain is printed — voice,
master gain, then the limiter, in the processor's own order," and the track's three
`[propose at review]` open questions are answered inline in the same section: the bake
instantiates its own `Limiter`; the lookahead does need in-render compensation, exactly the
above; and yes, this track also corrects the invariant text rather than leaving it to a
later pass.
**Not verified in a running DAW — CTest-asserted only.**
### Γ-W4-T1 — vst3-parameter-set
The instrument now reports its automatable parameters to the host: 44 of 44 issue, under a
+19 -48
View File
@@ -597,6 +597,9 @@ system's is in **`docs/product/parameter-automation.md` §§610**. Read §1.2
table) and §7 (collisions) before dispatching any track here — every number in this phase is
derived there, and `docs/TODO.md`'s old deck-rework geometry is superseded.
**All four waves have landed — Phase Γ is complete.** W1 through W4 each carry their own
landed note below; see `docs/COMPLETED.md` for every track's full narrative.
**Fork state — SEVEN ruled, ONE OF THEM LATER REVERSED, NONE OPEN.** Indexed at spec §8,
folded into the tracks below:
- **Γ-F1**`kEditorMinHeight` stays **680**.
@@ -1013,9 +1016,9 @@ than re-derived. **T2 depended on Phase Ξ** — `Ξ-W2-T1 (resample-bake-chain)
first, the phase's only external gate — see `docs/COMPLETED.md` for the full narrative of each
landed track.
**Two tracks have landed** — Γ-W3-T1 (`deck-reflow`) and Γ-W3-T2 (`bake-reset-amendment`) — see
`docs/COMPLETED.md` for the full narrative of each. **A third track is now open and has not
run:** Γ-W3-T3 (`bake-prints-limiter`), added below on Daniel's ruling of 2026-08-02.
**All three tracks have landed** — Γ-W3-T1 (`deck-reflow`), Γ-W3-T2
(`bake-reset-amendment`), and Γ-W3-T3 (`bake-prints-limiter`) — see `docs/COMPLETED.md` for
the full narrative of each.
**None of the three tracks takes a payload rung.** T1 was layout only; T2 changed a reset list,
not a format; T3 changes what the render's audio contains, not what is stored.
@@ -1066,56 +1069,24 @@ field-by-field assertions over two independently-dialled fixtures, never struct
mutation-verified spot-check sweep confirming both fixtures actually moved every asserted field
off its default.
**One invariant correction:** `bake/CLAUDE.md` had claimed the whole signal chain prints, master
gain included. It doesn't — the render's gain multiply is the only master-stage value it
prints; the limiter runs in the processor's block, off the bake path entirely.
**Outstanding, not closed by this track.** A capture baked with the limiter engaged comes back
unlimited — a real audible gap, and Daniel has ruled that a future track will change the bake to
print the limiter. **That track is now Γ-W3-T3, below.**
**One invariant correction, at the time this track landed:** `bake/CLAUDE.md` had claimed the
whole signal chain prints, master gain included. It didn't yet — the render's gain multiply was
the only master-stage value it printed; the limiter ran in the processor's block, off the bake
path entirely, so a capture baked with the limiter engaged came back unlimited. **Γ-W3-T3
(below) has since closed that gap** — the limiter is printed too now, and `bake/CLAUDE.md`'s
invariant text is corrected again to match.
**Neither track has been verified in a running DAW; both are asserted in CTest only.**
#### Γ-W3-T3 — `bake-prints-limiter`
**Not started. Opened by Daniel's ruling, 2026-08-02.**
**Goal.** Print the limiter through the bake's master stage, so a capture baked with the
limiter engaged returns limited audio rather than unlimited audio.
**Why this exists.** Γ-W3-T2's own finding disproved the premise
`docs/product/instrument-control-surface.md` §3.4's reset classification rested on:
`renderBake` (`core/instrument/bake/bake_render.cpp`) prints only a flat master-gain multiply,
and the limiter (`core/instrument/engine/limiter`) runs in the processor's `process()` block,
off the bake path entirely. Until this track lands, this is a recorded, known limitation — see
`docs/COMPLETED.md`'s Γ-W3-T2 entry — not an oversight.
**Consolidates:** nothing from the seventeen. A correction, on the same footing as Γ-W3-T2 (see
"Work in this plan that is not one of the seventeen").
**Spec:** none yet written. This ruling postdates §3.4 and has no product-doc section of its
own; §3.4 is superseded on this one point, which a future scoping pass of this track should
correct there as well as here.
**Surface boundary — likely, not yet confirmed against a full scoping pass:** owns
`core/instrument/bake/bake_render` (the gain-multiply step, extended to also run the signal
through a limiter), consuming `core/instrument/engine/limiter` — not owned, not modified. Does
not own the processor's live block, the limiter DSP itself, the parameter surface, or
`bake_reset` (the limiter-enable reset classification is already Γ-W3-T2's, landed).
**Open questions — none of this is ruled yet, only the goal is:**
- **[propose at review]** Whether the bake instantiates its own `Limiter` — mirroring
`renderBake`'s existing bake-only `VoiceEngine`, off the audio thread, never linked into
`reaper_reasampler` — or reaches the limiter's settled behavior some other way. The
bake-only-engine precedent (`bake/CLAUDE.md`) argues for the former.
- **[propose at review]** Whether the limiter's lookahead needs any accommodation in an
offline, non-realtime render — the processor's `getLatencySamples()` PDC report exists for
the live block, and a bake is not on that clock, so this may be a non-issue; it has not been
checked.
- **[propose at review]** Whether this track also corrects `bake/CLAUDE.md`'s invariant text
("the limiter is not [printed]") alongside the code, once scoped in full.
- **No [Daniel] question on the goal itself** — the ruling above is the goal; what is open is
the mechanism, not whether to do it.
**Landed** — see `docs/COMPLETED.md` for the full narrative. The bake's master stage now
prints the limiter as well as the gain multiply: `renderBake` instantiates its own `Limiter`
(the bake-only-engine precedent, never linked into `reaper_reasampler`), the lookahead is
compensated inside the render (an extra `flushFrames` of silence past the window, the capture
read out at `leadInFrames + flushFrames`), and `bake/CLAUDE.md`'s invariant text is corrected
alongside the code. All three of the track's `[propose at review]` open questions are
answered in that entry.
---