Fix cross-rate Preserve splices, re-stamp on host-rate change, name the rate refusal

The splice crossfade now counts output frames and stops before the ring's far end (this also fixes a lap at matched rate). The search strides by the rate ratio. The editor takes its time base from its own decode. Same-rate output is pinned to hashes from before the change.
This commit is contained in:
2026-09-25 14:17:11 -04:00
parent ec60cf78c6
commit 64f314a6a4
24 changed files with 592 additions and 183 deletions
+6 -4
View File
@@ -128,14 +128,16 @@ BakeChainResult runBake(ReaSamplerProcessor& processor) {
// The snapshot comes first: the default program's window is derived from the sound it
// carries (a Gate release, a Trigger play span), not from a constant.
std::optional<SampleData> snapshot = processor.bakeSnapshot(sampleRate);
if (!snapshot) return fail("the loaded capture could not be decoded for the render");
ReaSamplerProcessor::BuiltCapture built = processor.bakeSnapshot(sampleRate);
if (built.rateRefused) return fail("cannot render: " + sourceRateRefusalText());
if (!built.sample) return fail("the loaded capture could not be decoded for the render");
SampleData& snapshot = *built.sample;
// The note fires at the velocity the user has been auditioning at: three velocity curves
// are live, so a sound dialed at 127 does not bake as one dialed at 40.
const Velocity velocity = Velocity::of(processor.previewVelocity());
const instrument::bake::PlannedBake planned = planBake(
resolveNote(defaultBakeProgram(*snapshot, sampleRate, dialed.bakeHold, velocity),
resolveNote(defaultBakeProgram(snapshot, sampleRate, dialed.bakeHold, velocity),
*tempo),
sampleRate, rootNote);
if (!planned.plan) {
@@ -146,7 +148,7 @@ BakeChainResult runBake(ReaSamplerProcessor& processor) {
const instrument::bake::BakePlan& plan = *planned.plan;
const instrument::bake::BakeAudio audio =
renderBake(std::move(*snapshot), plan, processor.masterGainLinear(),
renderBake(std::move(snapshot), plan, processor.masterGainLinear(),
dialed.limiterEnabled);
if (audio.empty()) return fail("the offline pass produced no audio");