4.4 KiB
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.
renderBaketakes itsSampleDataBY VALUE and detachesSampleData::livebefore constructing aVoiceEnginefor 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 atnoteOffFrameso the tail is real, but even a pathological envelope cannot run past the window. - The whole signal chain is printed, master gain included.
renderBakescales its output by the dialed post-mixer gain, becauseresetAfterBakehands 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.
planBakereturns nullopt for a collapsed window, a non-positive rate, a window that rounds to no frames, and one pastkMaxBakeFrames— an unbounded window is abad_allocinside 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.
resetAfterBakestarts 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.cppcarries the argument at the assignment.
Modules
bake_plan—defaultBakeProgram(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, andplanBake, the oneResolvedNote+ rate -> frames resolution.bake_render—BakeAudioandrenderBake: the programmed note through the sample's own voice path, summed into an interleaved buffer at the source's own channel count.bake_reset—BakeResetandresetAfterBake: the ratified reset scope, answered for both the parameter set and the post-mixer master gain.
Gotchas
BakePlanspeaks 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.hsays 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.