fix: guard filter tail NaNs, skip static-filter envelope work, pin stereo filter path, correct stale comments

Codec fallback for non-finite filter fields, a modAmount==0 early-out in tickFilterCutoff,
new stereo render tests for the dual-mono mirror, and comment/test accuracy fixes flagged
in review (stale deck-width claims, restated invariants, drifted CMake link comments).
This commit is contained in:
2026-07-30 17:35:45 -04:00
parent 67215509cb
commit 39389c1183
14 changed files with 137 additions and 41 deletions
+26
View File
@@ -11,6 +11,7 @@
#include <cstdio>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
@@ -608,6 +609,30 @@ static void testFilterTailRoundTripsLosslessly() {
reasampler::instrument::engine::VelocityCurve::flat()));
}
// A non-finite modAmount/velAmount/keyTrack (a corrupt blob, or any writer that skipped the
// same guard the v8 master gain already applies) must lift to the neutral default rather than
// reach Voice::tickFilterCutoff, where both clamp compares are false against NaN and the
// static_cast<int> is UB. Mirrors testCorruptFieldsFallBackToDefaults' per-field precedent.
static void testNonFiniteFilterFieldsLiftToTheNeutralDefault() {
ComponentState in;
in.selectionId = "pad";
FilterSeconds& f = in.params.play.filter;
f.enabled = true;
f.modAmount = std::numeric_limits<double>::quiet_NaN();
f.velAmount = std::numeric_limits<double>::infinity();
f.keyTrack = -std::numeric_limits<double>::infinity();
const ComponentState out =
deserializeComponentState(serializeComponentState(in), 48000.0);
const FilterSeconds& g = out.params.play.filter;
const FilterSeconds def;
CHECK(g.modAmount == def.modAmount);
CHECK(g.velAmount == def.velAmount);
CHECK(g.keyTrack == def.keyTrack);
// The fallback is per-field, not per-record: the untouched fields still round-trip.
CHECK(g.enabled);
}
// The WRITER emits the CURRENT payload version, and the marker + version sit at the head of
// the payload — the self-describing property every legacy branch depends on. Asserted
// against the semantic constants, not literals.
@@ -1080,6 +1105,7 @@ int main() {
testTruncationDegradesCleanly();
testV8RecordLiftsToTheOffNeutralFilter();
testFilterTailRoundTripsLosslessly();
testNonFiniteFilterFieldsLiftToTheNeutralDefault();
if (failures == 0) {
std::printf("component_state_io_tests: all tests passed\n");
return 0;