Fix inverted splice-cadence test: assert artifact energy, not zero-crossing period

Zero-crossing counting was anti-correlated with the real defect (splice debris
fools it). Now asserts energy outside the fundamental, with an alignable control,
matching test_preserve_low_frequency.cpp's approach.
This commit is contained in:
2026-08-01 21:31:32 -04:00
parent ac653aa581
commit abace156a5
3 changed files with 108 additions and 19 deletions
+49 -14
View File
@@ -32,6 +32,7 @@
// decorrelated stereo content where an independent per-channel search provably diverges.
#include "../src/core/instrument/engine/pitch_shift.h"
#include "energy_outside_fundamental.h"
#include <cmath>
#include <cstdio>
@@ -740,15 +741,30 @@ static void testStretchAndShiftComposeSafely() {
// The [0.5, 2.0] rate bound (time_stretch.h) narrows the splice-cadence failure onto the
// source fundamental rather than eliminating it. At rate 2.0, shift 0.25 (-24 st) — both
// inside the shipped range — the header's own derivation puts the safe-source floor at a
// period of 315 frames (~140 Hz @ 44.1k): testStretchAndShiftComposeSafely's probe period of
// 196.37 frames (~225 Hz) sits ABOVE that floor, so it passes because of the probe, not
// because of headroom. This probe sits BELOW the floor on purpose, asserting the corner
// rather than assuming it. A failure here is the inequality's PREDICTED outcome, not a
// defect this test exists to chase — report it, don't retune the tolerance to hide it.
static void testStretchCadenceBelowSafeFloorAtRate2ShiftQuarter() {
// period of 315 frames (~140 Hz @ 44.1k): P=500/600/700 sit above that floor, on purpose,
// asserting the corner rather than assuming it. Zero-crossing period is NOT the right
// observable here: an investigation (test_preserve_low_frequency.cpp) found the P=500
// render's FUNDAMENTAL within 0.03% of target by autocorrelation and spectral peak alike,
// while the zero-crossing estimator read 23% flat — splice debris adds spurious crossings
// the count cannot tell from a real detune. Energy outside the fundamental tracks the actual
// damage instead: measured here (same rate/shift/source, this file's own metric parameters)
// at 7.31% / 14.41% / 21.22% for P=500/600/700, against 0.10% on an alignable control (P=200,
// below the safe floor) at the same rate and shift — so that is what this asserts: a known,
// characterised property of the range, not a pass/fail on a period estimate. A failure on
// either bound below is a finding — report it, don't retune the thresholds to hide it.
static void testStretchCadenceCornerArtifactEnergyAtRate2ShiftQuarter() {
using reasampler::test_support::energyOutsideFundamentalPercent;
const std::int64_t w = 2205;
const double rate = 2.0;
const double shift = std::pow(2.0, -24.0 / 12.0); // 0.25
const std::size_t outFrames = 60000;
const std::size_t from = 20000, len = 32768;
// Below the safe floor (P > 315 frames): the cadence inequality predicts real damage,
// measured at 7-21% (see above). The threshold (5%) sits above the alignable control's
// near-zero floor and under the observed range, so it discriminates a genuine cadence hit
// from a clean render; the ceiling (30%) is a generous margin above the highest measured
// value, there to catch a much worse regression rather than to chase today's exact number.
for (double period : {500.0, 600.0, 700.0}) {
const double f0 = 1.0 / period;
const std::size_t srcLen = 400000;
@@ -756,16 +772,35 @@ static void testStretchCadenceBelowSafeFloorAtRate2ShiftQuarter() {
for (std::size_t i = 0; i < srcLen; ++i) {
src[i] = static_cast<AudioSample>(std::sin(2.0 * kPi * f0 * static_cast<double>(i)));
}
const std::size_t outFrames = 60000;
const std::vector<double> out = runStretch(src, w, rate, shift, outFrames, nullptr);
for (double v : out) CHECK(std::isfinite(v));
const double p = periodIn(out, 20000, 50000);
const double want = period / shift;
const bool ok = approx(p, want, want * 0.12);
std::printf(" [floor probe] period %.0f (rate 2.0, -24 st): observed %.2f want %.2f "
"-> %s\n", period, p, want, ok ? "held" : "FAILED (predicted by the "
"inequality in time_stretch.h)");
CHECK(ok);
const double energyPct = energyOutsideFundamentalPercent(out, from, len, want);
std::printf(" [cadence corner] period %.0f (rate 2.0, -24 st): energy outside "
"fundamental %.2f%% (want period %.1f fr)\n", period, energyPct, want);
CHECK(energyPct > 5.0);
CHECK(energyPct < 30.0);
}
// The alignable control: same rate/shift, a source period (200 < 315) the cadence
// inequality does not reach. Without this, a future change that raised the noise floor
// EVERYWHERE (not just at this corner) would still read "under 30%" above and slide
// through — this is what catches that case.
{
const double period = 200.0;
const double f0 = 1.0 / period;
const std::size_t srcLen = 400000;
std::vector<AudioSample> src(srcLen);
for (std::size_t i = 0; i < srcLen; ++i) {
src[i] = static_cast<AudioSample>(std::sin(2.0 * kPi * f0 * static_cast<double>(i)));
}
const std::vector<double> out = runStretch(src, w, rate, shift, outFrames, nullptr);
for (double v : out) CHECK(std::isfinite(v));
const double want = period / shift;
const double energyPct = energyOutsideFundamentalPercent(out, from, len, want);
std::printf(" [alignable control] period %.0f (rate 2.0, -24 st): energy outside "
"fundamental %.2f%% (want period %.1f fr)\n", period, energyPct, want);
CHECK(energyPct < 5.0);
}
}
@@ -790,7 +825,7 @@ int main() {
testStereoLinkedLagSharedSchedule();
testStretchMovesDurationNotPitch();
testStretchAndShiftComposeSafely();
testStretchCadenceBelowSafeFloorAtRate2ShiftQuarter();
testStretchCadenceCornerArtifactEnergyAtRate2ShiftQuarter();
testStretchEntryPointsOnPassThrough();
if (g_fail == 0) {