Print the limiter through the bake's master stage, compensating its lookahead so an engaged bake is the approved sound and a bypassed one is unchanged

This commit is contained in:
2026-08-02 20:57:29 -04:00
parent a9c166b8c2
commit 0d316b7b9e
9 changed files with 232 additions and 75 deletions
+17 -4
View File
@@ -21,9 +21,21 @@ decision about what the render made obsolete.
loop runs to `BakePlan::renderFrames()` and stops. That is why a Gate bake with a sustain
loop active terminates: the gate is released at `noteOffFrame` so the tail is real, but
even a pathological envelope cannot run past the window.
- **The voice chain and master gain are printed; the limiter is not.** The gain multiply in
`bake_render.cpp` carries the argument for the gain, and `bake_reset.h` records where the
printed master stage stops.
- **The whole chain is printed — voice, master gain, then the limiter, in the processor's
own order.** `bake_render.cpp`'s master stage carries the argument. The limiter is printed
only when it is ENGAGED; bypassed, `renderBake` never constructs one and the result is the
pre-limiter render frame for frame. The lookahead is compensated inside the render — the
buffers carry an extra flush window and the capture is read past it — so an engaged bake
under the ceiling is bit-identical to a bypassed one, not the same audio 2 ms late.
- **A printed capture replayed through an engaged limiter is limited TWICE — a NAMED
boundary, not a bug**, and the same shape as the automation-lane limitation below. The
reset is what normally prevents it (`limiterEnabled` is not on the survive list, so a bake
hands the enable back off), and at unity the second pass has nothing to take: every sample
of the printed file is already at or under the ceiling, and the limiter reduces only where
its detector reads ABOVE it — which after a bake means its inter-sample estimate alone. Dial
the enable back on over raised gain, though, and the capture is limited on top of limiting
that is already in its samples. Not detectable from inside the instrument and not corrected
there; the user's remedy is to leave the enable where the bake put it.
- **A degenerate or unholdable window is refused, not rendered.** `planBake` refuses a
collapsed window, a non-positive rate, a window that rounds to no frames, and one past
`kMaxBakeFrames` — an unbounded window is a `bad_alloc` inside a UI tick, and the
@@ -72,7 +84,8 @@ decision about what the render made obsolete.
render window, the captured slice of it, and the two event frames), `kMaxBakeFrames`, and
`planBake`, the one `ResolvedNote` + rate -> frames resolution, answering a `PlannedBake`.
- `bake_render``BakeAudio` and `renderBake`: the programmed note through the sample's
own voice path, summed into an interleaved buffer at the source's own channel count.
own voice path and then the master stage, summed into an interleaved buffer at the
source's own channel count.
- `bake_reset``BakeReset` and `resetAfterBake`: the ratified reset scope, answered for
both the parameter set and the post-mixer master gain.