Ξ-W2-T1 re-review cleanup: soften ordering claim, bound bake guard fields, dedup gain/play-mode rationale, quiet foreign WrongProject noise
This commit is contained in:
@@ -21,10 +21,8 @@ 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 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.
|
||||
- **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
|
||||
@@ -35,11 +33,8 @@ decision about what the render made obsolete.
|
||||
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.
|
||||
classification this track made against the ratified rule rather than reading off it.
|
||||
`bake_reset.cpp` carries the argument at the assignment.
|
||||
|
||||
## Modules
|
||||
|
||||
|
||||
@@ -21,7 +21,14 @@ constexpr std::int64_t kBlockFrames = 512;
|
||||
BakeAudio renderBake(SampleData sample, const BakePlan& plan, double masterGainLinear) {
|
||||
BakeAudio out;
|
||||
if (!sample.playable() || plan.totalFrames <= 0 || plan.sampleRate <= 0) return out;
|
||||
if (plan.leadInFrames < 0 || plan.renderFrames() > kMaxBakeFrames) return out;
|
||||
// Each field bounded BEFORE the sum: renderFrames() adds them, and a hand-built plan
|
||||
// (planBake already bounds both — bake_plan.cpp) could otherwise carry leadInFrames
|
||||
// near INT64_MAX and signed-overflow inside the guard meant to catch exactly that.
|
||||
if (plan.leadInFrames < 0 || plan.leadInFrames > kMaxBakeFrames ||
|
||||
plan.totalFrames > kMaxBakeFrames) {
|
||||
return out;
|
||||
}
|
||||
if (plan.renderFrames() > kMaxBakeFrames) return out;
|
||||
|
||||
// The live block is the audio thread's moving target; a render that observed it would
|
||||
// depend on what the user happened to be dragging. The dialed values are already in
|
||||
@@ -67,8 +74,11 @@ BakeAudio renderBake(SampleData sample, const BakePlan& plan, double masterGainL
|
||||
const auto lead = static_cast<std::size_t>(plan.leadInFrames);
|
||||
const auto total = static_cast<std::size_t>(plan.totalFrames);
|
||||
out.interleaved.resize(total * static_cast<std::size_t>(channels));
|
||||
// A flat multiply, not the processor's per-sample ramp: the gain is constant for the
|
||||
// whole render, which is exactly what that ramp exists to converge to.
|
||||
// Printed here rather than left for the processor: resetAfterBake hands master gain
|
||||
// back to unity, so a render that only summed voices would return every iteration
|
||||
// shifted by 1/gain, and a gain dialed to silence would come back at full level. A flat
|
||||
// multiply, not the processor's per-sample ramp: the gain is constant for the whole
|
||||
// render, which is exactly what that ramp exists to converge to.
|
||||
const auto gain = static_cast<AudioSample>(masterGainLinear);
|
||||
for (std::size_t f = 0; f < total; ++f) {
|
||||
out.interleaved[f * channels] = left[lead + f] * gain;
|
||||
|
||||
@@ -30,9 +30,10 @@ struct BakeAudio {
|
||||
};
|
||||
|
||||
// Renders `plan` through `sample`'s own voice path, scaled by `masterGainLinear` — the
|
||||
// post-mixer gain the processor applies after the engine, printed here because the bake's
|
||||
// reset hands that control back at unity. The result is the plan's captured window: the
|
||||
// lead-in frames are rendered and dropped. An unplayable sample yields an empty result.
|
||||
// post-mixer gain the processor applies after the engine; see the gain multiply in
|
||||
// bake_render.cpp for why it is printed here rather than left to the processor. The result
|
||||
// is the plan's captured window: the lead-in frames are rendered and dropped. An unplayable
|
||||
// sample yields an empty result.
|
||||
BakeAudio renderBake(SampleData sample, const BakePlan& plan, double masterGainLinear);
|
||||
|
||||
} // namespace reasampler::instrument::bake
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
namespace reasampler::instrument::bake {
|
||||
|
||||
// The two surfaces a bake resets. Master gain lives on the processor rather than in the
|
||||
// parameter set, but renderBake prints it into the file, so it belongs to the same decision
|
||||
// and is answered here rather than left to the shell.
|
||||
// parameter set; it is answered here because renderBake prints it into the file (see
|
||||
// bake_render.cpp's gain multiply) rather than left to the shell.
|
||||
struct BakeReset {
|
||||
map::InstrumentParams params;
|
||||
double masterGainLinear = 1.0; // unity — renderBake printed the dialed gain
|
||||
|
||||
Reference in New Issue
Block a user