Fix spline-drag rollback, bake contour leak, held-note live miss, amp clamp

Editor close mid-drag now rolls back published contours like capture-loss does; bake detaches liveContours so a printed capture can't observe a live drag; a held note now receives a live publish that lands in the same block as a note-on; amp spline read clamped to [0,1].
This commit is contained in:
2026-09-25 13:59:37 -04:00
parent f7505d0e53
commit 4567744b8f
10 changed files with 109 additions and 18 deletions
+33
View File
@@ -1167,6 +1167,38 @@ static void testVelocityGainSurvivesAHostilePublishThatReallyLands() {
CHECK(proportional);
}
// A note-on that lands in the same block boundary as a publish must not let the already-
// sounding voice miss it: startVoice used to call refreshLive() bare, which consumes the
// generation stamp without applying it to the voices already active — a held note that shared
// a block with a note-on then never saw the publish at all (not merely delayed a block).
static void testHeldVoiceReceivesALivePublishThatCoincidesWithANoteOn() {
SampleData s;
s.frames.assign(200000, 1.0f); // DC: the envelope alone shapes the output
s.sampleRate = kRate;
s.rootNote = 60;
s.play.adsr.attackFrames = 0;
s.play.adsr.sustainLevel = 1.0;
s.play.adsr.releaseFrames = 480000; // long enough that release never intrudes
LiveParams block;
s.live = █
VoiceEngine engine(2, s);
engine.noteOn(60, 100); // voice A, sustaining at the dialed 1.0
LiveValues quieter = foldLive(s.play, s.keyTrack);
quieter.adsr.sustainLevel = 0.2;
block.publish(quieter);
engine.noteOn(61, 100); // voice B starts in the SAME generation as the publish
std::vector<AudioSample> out;
for (int i = 0; i < 20; ++i) engine.render(out, 512); // let voice A's glide settle
// Both voices settle at 0.2 — a held voice that missed the publish would still sit at 1.0,
// for a combined 1.2 rather than 0.4.
CHECK(std::fabs(static_cast<double>(out.back()) - 0.4) < 0.01);
}
int main() {
testStageDurationChangeHoldsPhase();
testShortenedStageStillLandsContinuously();
@@ -1189,6 +1221,7 @@ int main() {
testAPublishedPitchOffsetRefitsThePitchEnvelopeSpan();
testPitchRatioAndVelocityGainStayLatched();
testVelocityGainSurvivesAHostilePublishThatReallyLands();
testHeldVoiceReceivesALivePublishThatCoincidesWithANoteOn();
if (g_fail == 0) std::printf("live_delivery tests passed\n");
return g_fail == 0 ? 0 : 1;
}