fix: close review findings on spline EGs — engine, codec, and popup/overlay UI grammar

Live pitch depth, Gate/Spline enable-rule agreement, inert kTrigLength, NaN wire guards, hard-flag-tail corruption no longer wipes the record, RT/cold spline tie-break, retired alt-click, marker-shadow fix, plus new test coverage.
This commit is contained in:
2026-07-31 22:27:41 -04:00
parent e44bd42dd9
commit 1c774226d3
22 changed files with 414 additions and 88 deletions
+8 -2
View File
@@ -183,10 +183,16 @@ struct PlayParams {
// sample length, which IS the Trigger/one-shot playback model — so Gate is not available while
// any spline EG is active. resolvePlay enforces it on the way to the engine; the editor's
// play-mode toggle refuses the Gate segment so the two agree.
//
// The pitch/filter terms are gated on their own `enabled` flag to match Voice::start's binder
// (voice.cpp only binds pitchSplineCur_/filterSplineCur_ when that flag is set): without this,
// a Spline mode flip on a disabled pitch/filter envelope would cost Gate for zero modulation,
// since the binder would never actually engage. Amp has no such flag, so it counts unconditionally.
template <class Play>
bool splineActive(const Play& p) {
return p.ampSpline.mode == EnvMode::Spline || p.pitchSpline.mode == EnvMode::Spline ||
p.filterSpline.mode == EnvMode::Spline;
return p.ampSpline.mode == EnvMode::Spline ||
(p.pitchEnv.enabled && p.pitchSpline.mode == EnvMode::Spline) ||
(p.filter.enabled && p.filterSpline.mode == EnvMode::Spline);
}
// [start, end) frames, half-open. A zero-length loop (start == end) is the "no sustain loop"