# 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** — the gain multiply in
  `bake_render.cpp` carries the argument for why.
- **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.
  `bake_reset.cpp` carries 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`, and
  `planBake`, the one `ResolvedNote` + rate -> frames resolution.
- `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.
- `bake_reset` — `BakeReset` 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.
