Close Γ-W1-T7 re-review: pitch-sync cadence math, floor-model regression check, evidence-count fix, one-home comments
New cadence-collapse-band test at P=1470 shows PSOLA eliminates the corner rather than regressing it (18.52% -> 0.00%).
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
using namespace reasampler;
|
||||
@@ -920,6 +921,9 @@ static void testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown() {
|
||||
{"34 Hz rate 2.0", 34.0, 2.0, 0.0},
|
||||
};
|
||||
double controlWorst = 0.0, subjectWorst = 0.0, subjectOffWorst = 0.0;
|
||||
// std::max alone floors a NEGATIVE floor-relative excess to 0 — but a negative excess means
|
||||
// the floor model mismatches the render, not a clean one, so track the signed minimum too.
|
||||
double subjectWorstMin = std::numeric_limits<double>::infinity();
|
||||
for (const Row& r : rows) {
|
||||
const double period = 44100.0 / r.freq;
|
||||
std::vector<AudioSample> src(srcLen);
|
||||
@@ -943,6 +947,7 @@ static void testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown() {
|
||||
controlWorst = std::max(controlWorst, pctOn);
|
||||
} else {
|
||||
subjectWorst = std::max(subjectWorst, pctOn);
|
||||
subjectWorstMin = std::min(subjectWorstMin, pctOn);
|
||||
subjectOffWorst = std::max(subjectOffWorst, pctOff);
|
||||
}
|
||||
}
|
||||
@@ -953,6 +958,8 @@ static void testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown() {
|
||||
std::printf(" [30 Hz] worst subject excess %.2f%% vs worst control excess %.2f%%\n",
|
||||
subjectWorst, controlWorst);
|
||||
CHECK(subjectWorst < 0.10);
|
||||
CHECK(subjectWorstMin > -0.10); // a negative excess this large is a model
|
||||
// mismatch, not a clean render — catch it too
|
||||
CHECK(subjectWorst <= controlWorst + 0.05); // 0.05 absorbs the floor subtraction's sign noise
|
||||
// Vacuity guard, matching testTwentyNineHertzAtRateTwoKeepsItsPitch's sibling check: the
|
||||
// FIXED-WINDOW (no period set) arm is asserted too, so a setSourcePeriod that silently did
|
||||
@@ -1029,6 +1036,48 @@ static void testCadenceCornerIsUnmovedByAPitchSynchronousSplice() {
|
||||
}
|
||||
}
|
||||
|
||||
// M1 (Gamma-W1-T7 re-review): the corner above sits where periodAlignedJump narrows the jump
|
||||
// by only ~10% (2205 -> 2000/2400/2100 at P=500/600/700), never reaching the collapse band
|
||||
// (jump_->0.63-0.67*window) time_stretch.h's own derivation flags as where the recurrence
|
||||
// interval shrinks hardest. P=1470 (30 Hz at 44.1k) is the case that track exists for: n=2
|
||||
// overshoots jumpMax_ (2*1470=2940 > 2756), forcing n=1 and jump_=1470=0.667*window — 1.5x the
|
||||
// splice rate of the fixed-window fallback.
|
||||
//
|
||||
// MEASURED (Debug, this machine): metric floor 55.86% (P=1470's want-period of 5880 fr under a
|
||||
// 32768-frame segment leaks a lot of mainlobe, same effect as the P=500-700 rows, just larger),
|
||||
// fixed-window excess 18.52%, pitch-synchronous excess 0.00%. The faster cadence does NOT
|
||||
// regress this corner — every splice at n=1 lands exactly one source period away, so despite
|
||||
// firing 1.5x as often each one is phase-perfect rather than merely aligned-on-average, and the
|
||||
// corner clears rather than worsens. Recorded as read, not tuned: if a future change moves
|
||||
// these numbers, update this comment to match, don't loosen the bounds to hide it.
|
||||
static void testCadenceCollapseBandAtThirtyHertzUnderPitchSynchronousSplice() {
|
||||
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); // -24 st, the same corner as above
|
||||
const double period = 1470.0; // 30 Hz at 44.1k
|
||||
const std::size_t outFrames = 60000, from = 20000, len = 32768;
|
||||
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 * static_cast<double>(i) / period));
|
||||
}
|
||||
const std::vector<double> off = runStretch(src, w, rate, shift, outFrames, nullptr);
|
||||
const std::vector<double> on = runStretch(src, w, rate, shift, outFrames, nullptr, period);
|
||||
for (double v : on) CHECK(std::isfinite(v));
|
||||
const double want = period / shift;
|
||||
const double floor = idealToneFloorPercent(want, from, len);
|
||||
const double pctOff = energyOutsideFundamentalPercent(off, from, len, want);
|
||||
const double pctOn = energyOutsideFundamentalPercent(on, from, len, want);
|
||||
std::printf(" [cadence collapse band, PSOLA] period %.0f (want %.0f, metric floor %.2f%%): "
|
||||
"excess %.2f%% -> %.2f%%\n", period, want, floor, pctOff - floor, pctOn - floor);
|
||||
CHECK(pctOn - floor < 1.0); // measured 0.00%: phase-perfect at n=1, not merely aligned
|
||||
// Vacuity guard (shape of testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown's :961): the
|
||||
// FIXED-WINDOW arm is asserted too (measured 18.52% excess), so a setSourcePeriod that
|
||||
// silently did nothing would render both arms identically and pass the bound above by luck.
|
||||
CHECK(pctOff - floor > pctOn - floor + 1.0);
|
||||
}
|
||||
|
||||
// The two new entry points on a shifter that was never configured (a Varispeed voice's) —
|
||||
// neither may touch the empty ring.
|
||||
static void testStretchEntryPointsOnPassThrough() {
|
||||
@@ -1056,6 +1105,7 @@ int main() {
|
||||
testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown();
|
||||
testTwentyNineHertzAtRateTwoKeepsItsPitch();
|
||||
testCadenceCornerIsUnmovedByAPitchSynchronousSplice();
|
||||
testCadenceCollapseBandAtThirtyHertzUnderPitchSynchronousSplice();
|
||||
testStretchEntryPointsOnPassThrough();
|
||||
|
||||
if (g_fail == 0) {
|
||||
|
||||
Reference in New Issue
Block a user