Files
reasampler/src/core/instrument/bake/CLAUDE.md
T

4.4 KiB

src/core/instrument/bake — the resample bake's pure half

Scope

The offline pass that turns the dialed instrument into a file, and the reset that hands the instrument back neutral afterwards. A fifth peer of engine/ / map/ / note/ / ui/ under core/instrument/, pure by the same rule — no REAPER types, no VST3 types, no host.

It is neither engine (it owns no voice), mapping (it resolves no capture), nor note (it holds no program): it is the composition of the three into one render, plus the one decision about what the render made obsolete.

Invariants

  • The bake renders on its OWN engine, never the live one. renderBake takes its SampleData BY VALUE and detaches SampleData::live before constructing a VoiceEngine for the render alone. Two consequences, both load-bearing: the audio thread's live block can neither be observed nor disturbed by a bake, and a repeated bake of one dialed sound is byte-identical because nothing outside the passed value can vary between runs.
  • The window bounds the render; the envelope does not. Termination is structural — the 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 whole signal chain is printed, master gain included. renderBake scales its output by the dialed post-mixer gain, because resetAfterBake hands that control back at unity. A render that summed voices alone would return every iteration shifted by 1/gain, and a gain dialed to silence would come back at full level.
  • A degenerate or unholdable window is refused, not rendered. planBake returns nullopt for 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 seconds→frames narrowing is undefined long before the allocation would fail.
  • The reset's survive list is written out; everything else defaults. resetAfterBake starts from a default-constructed parameter set and copies back only the mapping facts. A parameter added later therefore resets by default — the safe direction, since under-resetting applies the same processing twice while over-resetting costs a re-dial. A new mapping fact must be added to the copy list explicitly.
  • Play mode resets to TRIGGER, not to the value struct's Gate default — the one classification this track made against the ratified rule rather than reading off it. The bake's product is a finished one-shot, and Trigger is the mode that plays a finished one-shot verbatim; Gate would re-gate the printed release tail and each iteration would truncate the previous one's. "Neutral" here means "adds no processing", not "the struct's own default". bake_reset.cpp carries the argument at the assignment.

Modules

  • bake_plandefaultBakeProgram (the program a bake uses until the capture-signal popup ships; its end offset is DERIVED from the dialed sound, never constant), BakePlan (the render window, the captured slice of it, and the two event frames), kMaxBakeFrames, and planBake, the one ResolvedNote + rate -> frames resolution.
  • bake_renderBakeAudio and renderBake: the programmed note through the sample's own voice path, summed into an interleaved buffer at the source's own channel count.
  • bake_resetBakeReset and resetAfterBake: the ratified reset scope, answered for both the parameter set and the post-mixer master gain.

Gotchas

  • BakePlan speaks two frame domains — the captured file's and the render's, which are offset from each other whenever the note and the capture window do not start together. bake_plan.h says which field is in which; do not read them as one clock.
  • defaultBakeProgram's Trigger window bounds the Varispeed read stretch, it does not model it. A downward pitch offset makes the read head take longer to cross the play span, so the window is scaled by the deepest downward offset the voice can reach — an upper bound, so a shallower excursion leaves trailing silence in the file. The capture-signal popup is where a user sets the window exactly.
  • The render's channel count is the loaded SampleData's, which is already the instance's channel-mode decision — a mono-mode instance bakes mono, and that is faithful, not a fold.