// Standalone tests for reasampler::PitchShifter — the S16 Preserve-engine DSP core. No VST3,
// no REAPER, no vendor, no test framework. The compile-time proof it does NOT drag the WDL
// <windows.h> chain is the CMake target linking only pitch_shift (+ peaks).
//
// Covers (PLAN.md S16 / CONTEXT.md §Pitch engine modes — Preserve):
//   1. duration invariance — N inputs yield N outputs at every shift ratio (the load-bearing
//      Preserve property: a transposed render is the SAME frame length as the un-transposed one).
//   2. unity pass-through fidelity — ratio 1.0 reproduces the input closely (a shifter at unity
//      must not mangle the signal).
//   3. transpose direction — an octave-up shift raises the observed pitch (period shortens), an
//      octave-down lowers it (period lengthens), measured on a synthesized sine.
//   4. RT discipline surrogate — after configure()+warm() (the off-thread setup), a long
//      process() run never resizes the ring (checked via window() constancy) and never returns
//      NaN/inf; pass-through (unconfigured) returns input verbatim.
//   5. spectral purity + onset integrity (GA / GA2 regressions) — a PRIMED repitched PURE
//      SINE must come out as a SINGLE tone at the shifted frequency FROM THE VERY FIRST
//      MILLISECOND: no zero-gaps anywhere (the GA2 DAW report: silence-warmed rings made
//      every early splice jump into zeros — burst/gap/burst stutter in the first few ms),
//      and a per-block least-squares residual floor that catches harmonics, splice-cadence
//      sideband combs, and crossfade cancellation alike. Ratios cover the FULL playable
//      range the DAW report exercised: +2/-3 st, +/-1 octave, +24 st, +48 st (C8 from C4,
//      ratio 16) and -36 st (C1 from C4, ratio 1/8).
//   6. unity + latency contract — asserted bit-exactly: a warm()ed shifter at ratio 1.0 IS a
//      clean window delay; a prime()d one has ZERO added latency (out[i] == src[i] to the
//      bit) — the GA2 immediate-onset claim.
//   9. time-stretch — the write rate (duration) and the tap rate (pitch) are independent: a
//      source fed faster/slower than the output runs moves along the output timeline with its
//      pitch untouched, composes with the full transposition range, and never resamples to do
//      it. A resampled read is the explicit non-tautology witness in the duration test.
//   8. stereo linked lag (Q-W0 T1-01) — a follower channel driven via processLinked() mirrors
//      the master's splice decision (jump/lag/frac/fadeLen AND firing frame) exactly, on
//      decorrelated stereo content where an independent per-channel search provably diverges.
//  10. pitch-synchronous splices — the nominal jump snapped to a whole number of source
//      periods: the jump law itself, the bit-identical unknown-period fallback, 30 Hz and
//      29 Hz (the two symptoms of the unalignable gap), and the cadence corner, which this
//      leaves where it found it.

#include "../src/core/instrument/engine/pitch_shift.h"
#include "tone_metrics.h"

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <vector>

using namespace reasampler;
using namespace reasampler::instrument::engine;

static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
    std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)

static bool approx(double a, double b, double tol) { return std::fabs(a - b) <= tol; }

constexpr double kPi = 3.14159265358979323846;

// A sine of `cycles` periods over `frames` frames.
static std::vector<AudioSample> sine(std::size_t frames, double cycles) {
    std::vector<AudioSample> s(frames);
    for (std::size_t i = 0; i < frames; ++i) {
        s[i] = static_cast<float>(std::sin(2.0 * kPi * cycles *
               static_cast<double>(i) / static_cast<double>(frames)));
    }
    return s;
}

// Average spacing between positive-going zero crossings (the observed period).
static double observedPeriod(const std::vector<AudioSample>& out, std::size_t from) {
    std::vector<std::size_t> up;
    for (std::size_t i = from + 1; i < out.size(); ++i) {
        if (out[i - 1] <= 0.0f && out[i] > 0.0f) up.push_back(i);
    }
    if (up.size() < 2) return 0.0;
    double sum = 0.0;
    for (std::size_t i = 1; i < up.size(); ++i) sum += static_cast<double>(up[i] - up[i - 1]);
    return sum / static_cast<double>(up.size() - 1);
}

// --- 1. Duration invariance across shift ratios. ---
static void testDurationInvariance() {
    // The core Preserve property: whatever the shift ratio, one input frame yields one output
    // frame. So a shifter fed N frames produces exactly N frames — a transposed render is the
    // same length as an un-transposed one (unlike Varispeed, where an octave up halves length).
    const std::size_t n = 4000;
    const std::vector<AudioSample> in = sine(n, 40.0);
    const double ratios[] = {0.5, 1.0, 2.0, std::pow(2.0, 7.0 / 12.0)};
    for (double r : ratios) {
        PitchShifter ps;
        ps.configure(2205);  // ~50 ms @ 44.1k
        ps.warm();
        ps.setShiftRatio(r);
        std::size_t produced = 0;
        for (std::size_t i = 0; i < n; ++i) {
            const AudioSample o = ps.process(in[i]);
            (void)o;
            ++produced;  // exactly one output per input, unconditionally.
        }
        CHECK(produced == n);  // duration held at every ratio.
    }
}

// --- 2. Unity pass-through fidelity. ---
static void testUnityRoughlyReproduces() {
    // At ratio 1.0 the shifter should reproduce the input's PITCH faithfully (the OLA taps run
    // in lockstep with the writer). Amplitude/phase warble is allowed (basic OLA), but the
    // observed period must match the source period within a small tolerance past the warm-up.
    const std::size_t n = 8000;
    const double cycles = 40.0;
    const double nativePeriod = static_cast<double>(n) / cycles;  // 200
    const std::vector<AudioSample> in = sine(n, cycles);
    PitchShifter ps;
    ps.configure(2205);
    ps.warm();
    ps.setShiftRatio(1.0);
    std::vector<AudioSample> out(n);
    for (std::size_t i = 0; i < n; ++i) out[i] = ps.process(in[i]);
    // Measure past the initial half-window latency region.
    const double p = observedPeriod(out, 3000);
    CHECK(p > 0.0);
    CHECK(approx(p, nativePeriod, nativePeriod * 0.10));  // within 10% of source period
}

// --- 3. Transpose direction: up shortens the period, down lengthens it. ---
static void testTransposeDirection() {
    const std::size_t n = 12000;
    const double cycles = 60.0;
    const double nativePeriod = static_cast<double>(n) / cycles;  // 200
    const std::vector<AudioSample> in = sine(n, cycles);

    // Octave up: output period ~ half the source period (higher pitch).
    {
        PitchShifter ps;
        ps.configure(2205);
        ps.warm();
        ps.setShiftRatio(2.0);
        std::vector<AudioSample> out(n);
        for (std::size_t i = 0; i < n; ++i) out[i] = ps.process(in[i]);
        const double p = observedPeriod(out, 4000);
        CHECK(p > 0.0);
        CHECK(approx(p, nativePeriod / 2.0, nativePeriod * 0.15));  // period halves
    }
    // Octave down: output period ~ double the source period (lower pitch).
    {
        PitchShifter ps;
        ps.configure(2205);
        ps.warm();
        ps.setShiftRatio(0.5);
        std::vector<AudioSample> out(n);
        for (std::size_t i = 0; i < n; ++i) out[i] = ps.process(in[i]);
        const double p = observedPeriod(out, 4000);
        CHECK(p > 0.0);
        CHECK(approx(p, nativePeriod * 2.0, nativePeriod * 0.30));  // period doubles
    }
}

// --- 4. RT discipline surrogate + pass-through. ---
static void testRtDisciplineAndPassthrough() {
    // Unconfigured shifter passes input through verbatim (a Varispeed voice never allocates one).
    {
        PitchShifter ps;
        CHECK(!ps.configured());
        CHECK(ps.process(0.37f) == 0.37f);   // exact pass-through
        CHECK(ps.process(-0.9f) == -0.9f);
    }
    // Configured: the window is fixed at configure() and never changes across a long run (no
    // per-frame Resize), and no output is NaN/inf (numerically well-behaved OLA).
    {
        PitchShifter ps;
        ps.configure(1024);
        ps.warm();
        const std::int64_t w = ps.window();
        CHECK(w == 1024);
        ps.setShiftRatio(std::pow(2.0, 5.0 / 12.0));
        const std::vector<AudioSample> in = sine(20000, 100.0);
        for (std::size_t i = 0; i < in.size(); ++i) {
            const AudioSample o = ps.process(in[i]);
            CHECK(std::isfinite(o));
        }
        CHECK(ps.window() == w);  // window unchanged -> ring never resized mid-run
    }
    // A non-positive shift ratio is ignored (keeps the last valid ratio) — never stalls/reverses.
    {
        PitchShifter ps;
        ps.configure(512);
        ps.warm();
        ps.setShiftRatio(1.0);
        ps.setShiftRatio(-2.0);   // ignored
        ps.setShiftRatio(0.0);    // ignored
        for (int i = 0; i < 2000; ++i) CHECK(std::isfinite(ps.process(0.5f)));
    }
    // Degenerate window (<= 1) stays pass-through even after configure.
    {
        PitchShifter ps;
        ps.configure(1);
        CHECK(!ps.configured());
        CHECK(ps.process(0.25f) == 0.25f);
    }
}

// --- 5. Spectral purity + onset integrity: a PRIMED repitched pure sine is a SINGLE shifted
//        tone from the very first millisecond. ---
static void testRepitchSpectralPurityAndOnset() {
    // Frequencies are in cycles/sample (rate-free). The source tone is chosen ADVERSARIALLY
    // on TWO axes simultaneously:
    //   (a) f0*(w/2) ≈ (2205/2)/196.37 ≈ 5.609 cycles (frac ≈ 0.609) — content half a window
    //       apart in the ring is near ANTI-PHASE.  The old dual-tap design cancelled almost
    //       completely at every crossfade midpoint for such tones — the DAW "severe beating /
    //       multiple partials from a pure sine" bug.
    //   (b) ringLen_*f0 ≈ 4410/196.37 ≈ 22.46 — near the half-integer alignment that makes the
    //       pre-fix fade-headroom artifact visible at ratio 4 (+24 st). Additionally, period
    //       196.37 is NON-INTEGER, so the correlation peak is NOT on the integer lag grid; the
    //       sub-sample parabolic refinement is LOAD-BEARING to stay at the -84 dB floor — the
    //       old integer f0=1/196 put the optimum on the grid and the parabola contributed
    //       nothing, making the -30 dB floor reachable without it.
    //
    // The shifter is driven exactly as the Voice drives it since GA2: prime() with the first
    // window of the source, then stream the CONTINUATION — so the measurements start at
    // output frame 0 and the onset regime (early splices near the primed boundary, the DAW
    // "zero-sample gaps in the first few ms" report) is inside the assertions, not skipped.
    const std::int64_t w = 2205;              // ~50 ms @ 44.1k (the product window)
    const double f0 = 1.0 / 196.37;          // NON-INTEGER period: the sub-sample correlation peak
                                              // is NOT on the integer grid, so the parabolic
                                              // refinement MUST contribute to achieve a clean
                                              // aligned splice — reverting it now FAILS this test.
                                              // (Old integer 1/196 put the optimum on the grid,
                                              // letting the parabola contribute nothing; the −30 dB
                                              // residual floor was then reachable without it.)
    const double ratios[] = {std::pow(2.0, 2.0 / 12.0),    // +2 semitones (D from C)
                             std::pow(2.0, -3.0 / 12.0),   // -3 semitones (down-shift path)
                             2.0,                          // octave up (the GA2 report: C5)
                             std::pow(2.0, 24.0 / 12.0),   // +24 st: ratio 4 — the ratio-scaled-
                                                           // fade target (unscaled fade would
                                                           // read stale data at ~75% gain)
                             std::pow(2.0, 48.0 / 12.0),   // +48 st: ratio 16 — C8 from C4 (the
                                                           // GA2 "awful at C8" report; fast
                                                           // splice cadence, short fades)
                             std::pow(2.0, -12.0 / 12.0),  // octave down (full down-shift path)
                             std::pow(2.0, -36.0 / 12.0)}; // -36 st: ratio 1/8 — C1 from C4
                                                           // (the GA2 down-shift report)
    for (double r : ratios) {
        PitchShifter ps;
        ps.configure(w);
        const std::size_t n = 120000;
        std::vector<AudioSample> src(n + static_cast<std::size_t>(w));
        for (std::size_t i = 0; i < src.size(); ++i) {
            src[i] = static_cast<AudioSample>(
                std::sin(2.0 * kPi * f0 * static_cast<double>(i)));
        }
        ps.prime(src.data(), w);   // the Voice's note-on path: real content, not warm zeros
        ps.setShiftRatio(r);
        std::vector<double> out(n);
        for (std::size_t i = 0; i < n; ++i) {
            out[i] = static_cast<double>(ps.process(src[i + static_cast<std::size_t>(w)]));
        }

        // (a) ONSET/GAP integrity over the ENTIRE run, frame 0 included: no near-zero run
        // longer than 32 frames (~0.7 ms). A unit-amplitude shifted sine dwells below 1e-3
        // for well under one frame per zero crossing even at the lowest ratio here, while the
        // pre-fix onset gaps were hundreds to thousands of frames of literal silence.
        std::size_t worstGap = 0, run = 0;
        for (std::size_t i = 0; i < n; ++i) {
            if (std::fabs(out[i]) < 1e-3) {
                ++run;
                if (run > worstGap) worstGap = run;
            } else {
                run = 0;
            }
        }
        CHECK(worstGap < 32);

        // (b) PER-BLOCK least-squares fit of a*sin + b*cos at the SHIFTED frequency, from the
        // FIRST block. Fitting phase per block deliberately tolerates the slow (pitch-true,
        // inaudible) SOLA phase wander across seconds while catching everything audible:
        // harmonics ("square-ish"), splice-cadence sideband combs (the spectrogram alias
        // lines), crossfade cancellation, and onset gaps all land in the residual or collapse
        // the in-block fit amplitude. Solve the exact 2x2 normal equations per block.
        const double f1 = r * f0;
        const std::size_t block = 4096;
        for (std::size_t b0 = 0; b0 + block <= n; b0 += block) {
            double sss = 0.0, scc = 0.0, ssc = 0.0, sys = 0.0, syc = 0.0;
            for (std::size_t i = b0; i < b0 + block; ++i) {
                const double ph = 2.0 * kPi * f1 * static_cast<double>(i);
                const double s = std::sin(ph), c = std::cos(ph);
                sss += s * s; scc += c * c; ssc += s * c;
                sys += out[i] * s; syc += out[i] * c;
            }
            const double det = sss * scc - ssc * ssc;
            CHECK(det > 0.0);
            const double a = (sys * scc - syc * ssc) / det;
            const double b = (syc * sss - sys * ssc) / det;
            double residSq = 0.0, fitSq = 0.0;
            for (std::size_t i = b0; i < b0 + block; ++i) {
                const double ph = 2.0 * kPi * f1 * static_cast<double>(i);
                const double fit = a * std::sin(ph) + b * std::cos(ph);
                residSq += (out[i] - fit) * (out[i] - fit);
                fitSq += fit * fit;
            }
            const double fitRms = std::sqrt(fitSq / static_cast<double>(block));
            const double residRms = std::sqrt(residSq / static_cast<double>(block));
            // The shifted tone is there at full amplitude (unit sine RMS ~0.707) in EVERY
            // block — a gapped or beating block collapses this...
            CHECK(fitRms > 0.6);
            CHECK(fitRms < 0.8);
            // ...and it is the ONLY thing there: residual at least 84 dB under the tone.
            // With the non-integer f0=1/196.37, the sub-sample parabolic refinement is
            // LOAD-BEARING: reverting it raises the floor to ~-50 dB (ratio 1/8), failing here.
            // With integer f0=1/196 the optimum was on the integer grid and the parabola
            // contributed nothing — the old floor of -30 dB was reachable without it.
            // Engine steady-state measures -84 dB and better across all 7 tested ratios;
            // moderate ratios (+/-2 st, octaves) sit at -88 dB typical.
            CHECK(residRms < 0.000063 * fitRms);  // -84 dB floor
        }
    }
}

// --- 6. Unity + latency contract: warm = bit-exact window delay; primed = bit-exact ZERO
//        latency. ---
static void testUnityBitExactAndLatency() {
    // A configured shifter at ratio 1.0 parks the tap mid-band (no splice ever fires) at an
    // integral delay (no interpolation error). After warm() that delay is exactly one window
    // of declared silence, so out[i] == in[i - w] to the bit. After prime() with the first
    // window of source the tap sits ON src[0] — out[i] == src[i] to the bit from the very
    // first frame: the GA2 zero-structural-latency (immediate onset) claim.
    const std::int64_t w = 2205;                 // the product window
    const std::size_t n = 6000;
    const std::vector<AudioSample> in = sine(n + static_cast<std::size_t>(w), 37.0);
    // warm(): a clean, bit-exact one-window delay of the streamed input.
    {
        PitchShifter ps;
        ps.configure(w);
        ps.warm();
        ps.setShiftRatio(1.0);
        std::vector<AudioSample> out(n);
        for (std::size_t i = 0; i < n; ++i) out[i] = ps.process(in[i]);
        std::size_t badSilence = 0, badDelay = 0;
        for (std::size_t i = 0; i < static_cast<std::size_t>(w); ++i) {
            if (out[i] != 0.0f) ++badSilence;    // pre-latency region: declared silence, exact
        }
        for (std::size_t i = static_cast<std::size_t>(w); i < n; ++i) {
            if (out[i] != in[i - static_cast<std::size_t>(w)]) ++badDelay;  // bit-exact delay
        }
        CHECK(badSilence == 0);
        CHECK(badDelay == 0);
    }
    // prime(): zero added latency — the output IS the source from frame 0, bit-exact.
    {
        PitchShifter ps;
        ps.configure(w);
        ps.prime(in.data(), w);
        ps.setShiftRatio(1.0);
        std::size_t badZeroLat = 0;
        for (std::size_t i = 0; i < n; ++i) {
            if (ps.process(in[i + static_cast<std::size_t>(w)]) != in[i]) ++badZeroLat;
        }
        CHECK(badZeroLat == 0);
    }
}

// --- 7. Tail wind-down (GA3): freezeTail() at source exhaustion keeps the output a
//        continuous, full-amplitude tone at the shifted frequency — the splice machinery
//        recycles the ring's frozen ALL-REAL tail instead of chopping against held-DC
//        padding (the DAW "ring modulation" troughs growing toward the note end). ---
static void testFreezeTailContinuousTone() {
    const std::int64_t w = 2205;
    const double f0 = 1.0 / 196.37;   // non-integer period (the test-5 adversarial tone)
    const std::size_t stream = 20000; // frames fed before exhaustion (several splice cycles)
    const double ratios[] = {std::pow(2.0, 7.0 / 12.0),    // +7 st (the DAW report regime)
                             2.0,                          // octave up
                             std::pow(2.0, 24.0 / 12.0),   // +24 st: fast frozen drain
                             std::pow(2.0, -5.0 / 12.0),   // -5 st (down-shift tail)
                             1.0};                         // unity: frozen delay drains at 1 —
                                                           // splices NOW fire even at unity
    for (double r : ratios) {
        PitchShifter ps;
        ps.configure(w);
        std::vector<AudioSample> src(stream + static_cast<std::size_t>(w));
        for (std::size_t i = 0; i < src.size(); ++i) {
            src[i] = static_cast<AudioSample>(
                std::sin(2.0 * kPi * f0 * static_cast<double>(i)));
        }
        ps.prime(src.data(), w);
        ps.setShiftRatio(r);
        for (std::size_t i = 0; i < stream; ++i) {
            (void)ps.process(src[i + static_cast<std::size_t>(w)]);
        }
        // Source exhausted: freeze (idempotent) and keep producing for one full window — the
        // longest a Voice runs frozen (its own note end lands within a window of exhaustion).
        CHECK(!ps.tailFrozen());
        ps.freezeTail();
        ps.freezeTail();   // double-freeze harmless
        CHECK(ps.tailFrozen());
        const std::size_t tail = static_cast<std::size_t>(w);
        std::vector<double> out(tail);
        for (std::size_t i = 0; i < tail; ++i) {
            out[i] = static_cast<double>(ps.process(0.0f));  // input ignored while frozen
            CHECK(std::isfinite(out[i]));
        }
        // (a) No dead stretches: a unit-amplitude tone dwells below 0.05 only a few frames
        //     per zero crossing; the pre-GA3 DC chop ran hundreds.
        std::size_t worstGap = 0, run = 0;
        for (std::size_t i = 0; i < tail; ++i) {
            if (std::fabs(out[i]) < 0.05) {
                ++run;
                if (run > worstGap) worstGap = run;
            } else {
                run = 0;
            }
        }
        CHECK(worstGap < 24);
        // (b) Full amplitude throughout: every 256-frame block spans > a half period at all
        //     tested ratios, so a continuous tone peaks near 1.0 in each.
        for (std::size_t b = 0; b + 256 <= tail; b += 256) {
            double peak = 0.0;
            for (std::size_t i = b; i < b + 256; ++i) {
                if (std::fabs(out[i]) > peak) peak = std::fabs(out[i]);
            }
            CHECK(peak > 0.5);
            CHECK(peak < 1.1);   // aligned complementary fades: no cancellation, no bulge
        }
    }

    // Freeze landing MID-CROSSFADE: at ratio 2 from a fresh prime the tap drains from delay
    // w at 1/frame, splices at w/4 (frame 3w/4), then fades for w/4 frames — so frame
    // 3w/4 + w/8 is deterministically mid-fade. The frozen writer makes the outgoing tap
    // close at the FULL ratio; the transition caps the live fade so it completes before
    // reading lapped content — output must stay finite, gap-free, and bounded.
    {
        PitchShifter ps;
        ps.configure(w);
        std::vector<AudioSample> src(4 * static_cast<std::size_t>(w));
        for (std::size_t i = 0; i < src.size(); ++i) {
            src[i] = static_cast<AudioSample>(
                std::sin(2.0 * kPi * f0 * static_cast<double>(i)));
        }
        ps.prime(src.data(), w);
        ps.setShiftRatio(2.0);
        const std::size_t preFreeze = static_cast<std::size_t>(3 * w / 4 + w / 8);
        double lastPre = 0.0;
        for (std::size_t i = 0; i < preFreeze; ++i) {
            lastPre = static_cast<double>(ps.process(src[i + static_cast<std::size_t>(w)]));
        }
        ps.freezeTail();
        std::size_t worstGap = 0, run = 0;
        for (std::size_t i = 0; i < static_cast<std::size_t>(w); ++i) {
            const double o = static_cast<double>(ps.process(0.0f));
            CHECK(std::isfinite(o));
            CHECK(std::fabs(o) < 1.1);
            if (std::fabs(o) < 0.05) {
                ++run;
                if (run > worstGap) worstGap = run;
            } else {
                run = 0;
            }
        }
        CHECK(worstGap < 24);
        // reset()/prime() clear the freeze: the shifter is fully reusable for the next
        // note-on, and a primed unity run is STILL bit-exact zero-latency (no stale state).
        ps.reset();
        CHECK(!ps.tailFrozen());
        ps.prime(src.data(), w);
        ps.setShiftRatio(1.0);
        std::size_t badZeroLat = 0;
        for (std::size_t i = 0; i < 2000; ++i) {
            if (ps.process(src[i + static_cast<std::size_t>(w)]) != src[i]) ++badZeroLat;
        }
        CHECK(badZeroLat == 0);
    }

    // STEP-DETECTOR: freeze-transition continuity using a ramp source where tapA and tapB
    // read values that differ by a predictable constant (≈ A * w / N), making the crossfade
    // gain step directly visible in the output. With a ramp, the per-frame natural change is
    // A/(N) * ratio ≈ 0.0002 per frame; the un-fixed gain step is ~0.247 * (w/N) ≈ 0.062 —
    // roughly 300x the natural rate. A threshold of 0.02 clearly separates fixed from unfixed.
    //
    // The ramp also defeats correlation-alignment (all lags score equally on a linear ramp),
    // so the splice jump of one window guarantees tapA - tapB = −A*w/N regardless of lag.
    {
        PitchShifter ps;
        ps.configure(w);
        // Ramp from 0.0 to 1.0 over 4*w frames (same buffer size as the mid-crossfade case).
        const std::size_t rampLen = 4 * static_cast<std::size_t>(w);
        std::vector<AudioSample> ramp(rampLen);
        for (std::size_t i = 0; i < rampLen; ++i) {
            ramp[i] = static_cast<AudioSample>(static_cast<double>(i) /
                                               static_cast<double>(rampLen - 1));
        }
        ps.prime(ramp.data(), w);
        ps.setShiftRatio(2.0);
        // Drive to the deterministic mid-fade freeze point: same preFreeze offset as above.
        const std::size_t preFreeze = static_cast<std::size_t>(3 * w / 4 + w / 8);
        double lastPre = 0.0;
        for (std::size_t i = 0; i < preFreeze; ++i) {
            lastPre = static_cast<double>(
                ps.process(ramp[i + static_cast<std::size_t>(w)]));
        }
        ps.freezeTail();
        // First frozen frame — if gNew steps at the freeze boundary the output jumps by
        // ~deltaGain * (tapA - tapB) ≈ 0.247 * 0.25 = 0.062.
        const double firstFrozen = static_cast<double>(ps.process(0.0f));
        CHECK(std::isfinite(firstFrozen));
        // Natural per-frame ramp advance at ratio 2 ≈ 2/(4*w - 1) ≈ 0.0002; the un-fixed
        // step is ~0.062. Threshold 0.02 is 100x the natural rate but well below the step.
        const double transitionStep = std::fabs(firstFrozen - lastPre);
        CHECK(transitionStep < 0.02);
    }
}

// --- 8. Stereo linked lag (Q-W0 T1-01): a follower channel driven via processLinked()
//        applies EXACTLY the master's splice decision — same firing frame, same jump, same
//        lag, same sub-sample frac, same fade length — so a stereo pair shares ONE splice
//        schedule (no inter-channel offset re-drawn per splice: the pre-fix image-wander /
//        mono-sum-combing mechanism). The divergence witness: an INDEPENDENT shifter fed the
//        follower's content picks a different lag on the same schedule, proving the mirror
//        assertion is not vacuous (the two channels' contents genuinely disagree on the best
//        alignment). A third shifter (`mirror`), primed with the SAME content as the master
//        and driven via processLinked() with the master's own decisions, must reproduce the
//        master's output BIT-IDENTICALLY every frame — this is the review-rider strengthening:
//        the `ef == em` mirror check above only proves lastSplice_ was copied verbatim (which
//        applySplice() always does), not that applySplice() actually reproduces splice()'s
//        effect on posA_/fadeLen_/audio output; a same-content bit-identical check catches a
//        real divergence there (e.g. an asymmetry between applySplice()'s unconditional
//        `max(1, ev.fadeLen)` and splice()'s own fadeLen_ assignment). This driven-every-frame
//        setup keeps both master and follower in lockstep the whole run (posA_/writePos_ stay
//        identical since jumps are geometric, not content-dependent), so it exercises
//        applySplice() on every splice — never the Q-W0 remediation self-healing fallback
//        (own-search splice on a stale follower), which only fires when a follower has been
//        skipped a block relative to the master (mono-render-block starvation). ---
static void testStereoLinkedLagSharedSchedule() {
    const std::int64_t w = 2205;                 // the product window
    const std::size_t n = 40000;                 // ~17 splice cycles at ratio 2
    // Decorrelated "stereo" content: two different non-integer-period tones, so each
    // channel's own correlation optimum lands on a different lag.
    const double fL = 1.0 / 196.37;
    const double fR = 1.0 / 123.13;
    std::vector<AudioSample> srcL(n + static_cast<std::size_t>(w));
    std::vector<AudioSample> srcR(n + static_cast<std::size_t>(w));
    for (std::size_t i = 0; i < srcL.size(); ++i) {
        srcL[i] = static_cast<AudioSample>(std::sin(2.0 * kPi * fL * static_cast<double>(i)));
        srcR[i] = static_cast<AudioSample>(std::sin(2.0 * kPi * fR * static_cast<double>(i)));
    }
    PitchShifter master, follower, independent, mirror;
    master.configure(w);
    follower.configure(w);
    independent.configure(w);
    mirror.configure(w);
    master.prime(srcL.data(), w);
    follower.prime(srcR.data(), w);        // linked: R content, master's decisions
    independent.prime(srcR.data(), w);     // control: R content, OWN search (pre-fix behavior)
    mirror.prime(srcL.data(), w);          // SAME content as master: bit-identical witness
    master.setShiftRatio(2.0);
    follower.setShiftRatio(2.0);
    independent.setShiftRatio(2.0);
    mirror.setShiftRatio(2.0);

    int spliceCount = 0;
    bool followerDiverged = false;
    bool independentDiverged = false;
    bool mirrorDiverged = false;
    for (std::size_t i = 0; i < n; ++i) {
        const std::size_t si = i + static_cast<std::size_t>(w);
        const AudioSample oM = master.process(srcL[si]);
        const SpliceEvent& em = master.lastSplice();
        const AudioSample oR = follower.processLinked(srcR[si], em);
        CHECK(std::isfinite(oR));
        // The follower mirrors the master's decision EXACTLY, every frame (fired == false
        // frames included). In this driven-every-frame lockstep run the follower never falls
        // behind, so it never reaches the Q-W0 self-healing fallback — every splice here goes
        // through applySplice(), same as the mirror check below.
        const SpliceEvent& ef = follower.lastSplice();
        if (ef.fired != em.fired || ef.jump != em.jump || ef.lag != em.lag ||
            ef.frac != em.frac || ef.fadeLen != em.fadeLen) {
            followerDiverged = true;
        }
        if (em.fired) ++spliceCount;
        // The control: same content as the follower, own search. Its decision differing
        // from the master's proves the mirror assertion above is load-bearing.
        (void)independent.process(srcR[si]);
        const SpliceEvent& ei = independent.lastSplice();
        if (ei.fired != em.fired || ei.lag != em.lag || ei.frac != em.frac) {
            independentDiverged = true;
        }
        // The bit-identical witness: same content as the master, master's decisions applied
        // via applySplice() instead of computed via splice() — the two code paths must produce
        // the exact same sample stream.
        const AudioSample oMirror = mirror.processLinked(srcL[si], em);
        if (oMirror != oM) mirrorDiverged = true;
    }
    CHECK(spliceCount >= 3);        // the run actually exercised several splices
    CHECK(!followerDiverged);       // linked lag: one decision, one schedule, both channels
    CHECK(independentDiverged);     // non-tautology witness: unlinked channels DO disagree
    CHECK(!mirrorDiverged);         // applySplice() reproduces splice() bit-identically
}

// --- 9. Time-stretch: the WRITE rate is duration, the TAP rate is pitch, and they are
//        independent. Feeding faster/slower than the output runs moves the content along the
//        output timeline WITHOUT moving its pitch — no resampling anywhere, which is what
//        "WDL_Resampler is not a Preserve engine" asks for. ---

// Drives the shifter with a fractional feed rate the way the Voice does: all but the last
// source frame due on an output frame go through writeFrame (no output), the last through
// process(); an output frame with none due takes processNoInput(). Returns the output plus,
// via `consumed`, how much source it ate.
// `sourcePeriod` > 0 puts the shifter on the pitch-synchronous jump the loader would have
// given it; 0 (the default) is the fixed-window fallback every pre-PSOLA call here exercises.
static std::vector<double> runStretch(const std::vector<AudioSample>& src, std::int64_t w,
                                      double feedRate, double shift, std::size_t outFrames,
                                      std::size_t* consumed, double sourcePeriod = 0.0) {
    PitchShifter ps;
    ps.configure(w);
    ps.prime(src.data(), w);
    ps.setShiftRatio(shift);
    ps.setFeedRate(feedRate);
    ps.setSourcePeriod(sourcePeriod);
    std::size_t pos = static_cast<std::size_t>(w);
    double debt = 0.0;
    std::vector<double> out(outFrames);
    for (std::size_t i = 0; i < outFrames; ++i) {
        debt += feedRate;
        const int due = static_cast<int>(debt);
        debt -= static_cast<double>(due);
        AudioSample last = 0.0f;
        bool fed = false;
        for (int k = 0; k < due; ++k) {
            if (fed) ps.writeFrame(last);
            last = pos < src.size() ? src[pos] : 0.0f;
            ++pos;
            fed = true;
        }
        out[i] = static_cast<double>(fed ? ps.process(last) : ps.processNoInput());
    }
    if (consumed != nullptr) *consumed = pos - static_cast<std::size_t>(w);
    return out;
}

// Mean spacing between positive-going zero crossings over [from, to).
static double periodIn(const std::vector<double>& v, std::size_t from, std::size_t to) {
    double sum = 0.0;
    std::size_t prev = 0, count = 0;
    for (std::size_t i = from + 1; i < to; ++i) {
        if (v[i - 1] <= 0.0 && v[i] > 0.0) {
            if (count > 0) sum += static_cast<double>(i - prev);
            prev = i;
            ++count;
        }
    }
    return count > 1 ? sum / static_cast<double>(count - 1) : 0.0;
}

static void testStretchMovesDurationNotPitch() {
    // A source that changes pitch ONCE, at a known source frame: period 200 before it, period
    // 100 after. Where that change lands in the OUTPUT is duration; what the two periods
    // measure is pitch. A stretcher moves the first and not the second; a resampled read moves
    // both, which is exactly the distinction under test.
    const std::int64_t w = 2205;
    const std::size_t change = 40000;          // source frame where the period halves
    const std::size_t srcLen = 160000;
    std::vector<AudioSample> src(srcLen);
    double phase = 0.0;
    for (std::size_t i = 0; i < srcLen; ++i) {
        phase += 2.0 * kPi / (i < change ? 200.0 : 100.0);
        src[i] = static_cast<AudioSample>(std::sin(phase));
    }
    for (double rate : {0.5, 1.0, 2.0}) {
        // Duration: the source is consumed at the feed rate, so the change lands at
        // change/rate in the output — the run is sized to reach past it at every rate.
        const std::size_t changeOut = static_cast<std::size_t>(change / rate);
        const std::size_t outFrames = changeOut + 12000;
        std::size_t consumed = 0;
        const std::vector<double> out =
            runStretch(src, w, rate, /*shift=*/1.0, outFrames, &consumed);
        // Pitch: measured well clear of the transition on both sides, and UNCHANGED by the
        // rate — 200 before, 100 after, at 0.5x, 1x and 2x alike.
        const double before = periodIn(out, changeOut / 4, changeOut / 4 + 6000);
        const double after = periodIn(out, changeOut + 2000, changeOut + 8000);
        CHECK(approx(before, 200.0, 10.0));
        CHECK(approx(after, 100.0, 5.0));
        // Non-tautology witness: a RESAMPLED read of the same source at the same rate would
        // have produced 200/rate and 100/rate here. At rate != 1 those differ from the
        // measurements above by far more than the tolerances, so the assertions genuinely
        // separate a stretch from a resample.
        if (rate != 1.0) {
            CHECK(std::fabs(before - 200.0 / rate) > 20.0);
            CHECK(std::fabs(after - 100.0 / rate) > 20.0);
        }
        // ...and the source really was consumed at the rate (the duration half of the claim).
        CHECK(approx(static_cast<double>(consumed),
                     static_cast<double>(outFrames) * rate, 2.0));
        // No dead stretches anywhere, frame 0 included: the stretch path must not reintroduce
        // the onset gap prime() exists to close.
        std::size_t worstGap = 0, run = 0;
        for (std::size_t i = 0; i < outFrames; ++i) {
            if (std::fabs(out[i]) < 1e-3) {
                ++run;
                if (run > worstGap) worstGap = run;
            } else {
                run = 0;
            }
        }
        CHECK(worstGap < 32);
    }
}

// The stretch and the transposition compose over the SAME ring, and a feed rate the shift does
// not match is where the splice fade's headroom is tightest (the outgoing tap closes on the
// writer at ratio - feedRate, which setFeedRate exists to tell it). Bounded, finite, gap-free
// across the corners of the engine's rate range crossed with the full transposition range.
static void testStretchAndShiftComposeSafely() {
    const std::int64_t w = 2205;
    const double f0 = 1.0 / 196.37;   // the adversarial non-integer 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)));
    }
    for (double rate : {0.5, 0.75, 1.0, 1.5, 2.0}) {
        for (double semis : {-24.0, -12.0, -5.0, 0.0, 7.0, 12.0, 24.0}) {
            const double shift = std::pow(2.0, semis / 12.0);
            const std::size_t outFrames = 60000;
            const std::vector<double> out =
                runStretch(src, w, rate, shift, outFrames, nullptr);
            std::size_t worstGap = 0, run = 0;
            double peak = 0.0;
            for (std::size_t i = 0; i < outFrames; ++i) {
                CHECK(std::isfinite(out[i]));
                const double a = std::fabs(out[i]);
                if (a > peak) peak = a;
                if (a < 1e-3) {
                    ++run;
                    if (run > worstGap) worstGap = run;
                } else {
                    run = 0;
                }
            }
            CHECK(worstGap < 32);   // continuous: every splice landed in real, aligned history
            CHECK(peak < 1.2);      // complementary fades: no cancellation, no bulge
            CHECK(peak > 0.8);      // ...and it played at full level
            // Pitch is the TAP's, not the feed's: the observed period is the source period
            // divided by the shift, whatever the rate.
            const double p = periodIn(out, 20000, 50000);
            if (!approx(p, 196.37 / shift, 196.37 / shift * 0.12)) {
                std::printf("  rate %.2f semis %.0f: period %.2f want %.2f\n", rate, semis, p,
                            196.37 / shift);
            }
            CHECK(approx(p, 196.37 / shift, 196.37 / shift * 0.12));
        }
    }
}

// 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): 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 is 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. Those readings are stable and are what the bounds below hold.
//
// **CORRECTED — what those three readings MEAN.** They were once read as the corner's damage.
// They are almost entirely the metric's own floor: an ideal tone at the same want-period,
// measured identically, reads 7.08% / 13.90% / 20.92% (idealToneFloorPercent, below), because
// a long period under a 32768-frame segment leaks part of its own mainlobe outside the +/-6%
// band. The corner's real EXCESS over that floor is 0.23% / 0.51% / 0.30% — small, real, and
// nothing like the headline numbers. The alignable control's 0.10% is genuinely near-zero only
// because its want-period is short enough to have almost no floor. The bounds below are kept
// as a stable regression tripwire on the raw readings; read the excess, not the reading.
//
// This measures the FIXED-WINDOW path — no source period is set, which is what a capture with
// no single period (percussive, polyphonic, noise) gets. What the same corner does once the
// splice is pitch-synchronous is the test immediately after this one.
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): per the header's CORRECTED note, this raw reading
    // is mostly the metric's own mainlobe-leakage floor, not cadence damage (the real excess is
    // ~0.2-0.5%, asserted in testCadenceCornerIsUnmovedByAPitchSynchronousSplice). The bounds
    // below are a RAW-READING STABILITY TRIPWIRE, not a discriminator — they catch a large swing
    // in the raw measurement; 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;
        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("  [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);
    }
}

// --- 10. Pitch-synchronous splices: the nominal jump is a whole number of SOURCE periods. ---

// The out-of-band metric's OWN floor at a given period: a mathematically perfect tone, measured
// with exactly the parameters a render is. A long period under a fixed segment leaks part of
// its own mainlobe outside the +/-6% band, and that leakage grows steeply with the period — so
// a raw reading at period 2800 is not comparable with one at period 800, and neither is
// comparable with zero. The EXCESS over this floor is the honest "how much of this render is
// not the tone" number.
static double idealToneFloorPercent(double wantPeriod, std::size_t from, std::size_t len) {
    std::vector<double> v(from + len + 2);
    for (std::size_t i = 0; i < v.size(); ++i) {
        v[i] = std::sin(2.0 * kPi * static_cast<double>(i) / wantPeriod);
    }
    return reasampler::test_support::energyOutsideFundamentalPercent(v, from, len, wantPeriod);
}

// A splice can only phase-align on a landing point a whole number of source periods away, and
// the search only reaches [0.75, 1.25] windows. periodAlignedJump is what puts an aligned point
// at the CENTRE of that interval instead of hoping one falls inside it.
static void testPeriodAlignedJumpSnapsToWholePeriodsWithinTheReachableBound() {
    const std::int64_t w = 2205;          // the product window at 44.1k
    const std::int64_t maxJump = 2756;    // 1.25 * w, the shifter's own jumpMax_

    // Unknown period, and a period too long for even ONE whole one to fit: the fixed window,
    // unchanged. Both are the documented fallback, and both must be EXACTLY today's geometry.
    CHECK(periodAlignedJump(w, maxJump, 0.0) == w);
    CHECK(periodAlignedJump(w, maxJump, -5.0) == w);
    CHECK(periodAlignedJump(w, maxJump, 3000.0) == w);

    // 30 Hz at 44.1k (P = 1470): two periods overshoot the bound, so it takes ONE — which is
    // the case the whole track exists for. The pre-PSOLA geometry could reach neither 1470 nor
    // 2940 from a 2205 nominal, since the search only spans [1654, 2756].
    CHECK(periodAlignedJump(w, maxJump, 1470.0) == 1470);
    CHECK(2 * 1470 > maxJump);   // the witness that one period is forced, not merely chosen

    // 220 Hz (P = 200.4545): eleven periods land within a frame of the window itself, so the
    // splice cadence is essentially untouched while every landing is aligned.
    CHECK(periodAlignedJump(w, maxJump, 44100.0 / 220.0) == 2205);

    // A period just under the bound is taken whole; the result is never over the bound, at any
    // period in the band. Sweeping is what proves the shrink loop terminates correctly rather
    // than one hand-picked value doing so.
    for (double p = 20.0; p < 3200.0; p += 0.37) {
        const std::int64_t j = periodAlignedJump(w, maxJump, p);
        CHECK(j >= 1);
        if (p > static_cast<double>(maxJump)) {
            CHECK(j == w);          // out of reach -> fallback
        } else {
            CHECK(j <= maxJump);
            // Aligned: the jump is a whole number of periods, to within the rounding to frames.
            const double n = static_cast<double>(j) / p;
            CHECK(std::fabs(n - std::floor(n + 0.5)) * p < 0.51);
        }
    }
}

// setSourcePeriod(0) and never calling it are the same state, not merely similar ones — the
// cheap half of the fallback claim. The EXPENSIVE half, that the fallback still matches the
// engine as it shipped, is testPreserveUnityRateIsBitIdenticalToTheShippedRead in
// test_sampler_core.cpp: it hashes four rendered streams (including transposed ones that
// really splice) against a baseline captured from commit 0a7778b, and it passes unmodified.
static void testAnUnknownPeriodIsBitIdenticalToTheFixedWindowGeometry() {
    const std::int64_t w = 2205;
    const std::vector<AudioSample> src = sine(400000, 400000.0 / 196.37);
    const std::vector<double> never = runStretch(src, w, 1.0, 1.5, 40000, nullptr);
    const std::vector<double> zeroed = runStretch(src, w, 1.0, 1.5, 40000, nullptr, 0.0);
    bool same = true;
    for (std::size_t i = 0; i < never.size(); ++i) if (never[i] != zeroed[i]) same = false;
    CHECK(same);
}

// THE case this track exists for. 30 Hz sits in the only unalignable gap above 16 Hz at the
// product's 50 ms window: its nearest whole multiple misses the reachable interval by 184
// frames (45 degrees of phase), and the investigation measured the resulting sidebands at
// 3.57% out-of-band at +2 st / rate 1.0 and 15.45% at rate 2.0, against 0.00% on alignable
// controls. Here the same two conditions run with and without the source period, against a
// 34 Hz control that was alignable all along.
//
// The absolute numbers are NOT the harness's: a 1470-frame period under a 32768-frame segment
// leaks part of its own mainlobe outside the +/-6% band, so every reading here carries the same
// floor. That is exactly why the control is measured at the same length — the assertion is that
// 30 Hz reaches the control's floor, not that it reaches zero.
static void testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown() {
    using reasampler::test_support::energyOutsideFundamentalPercent;
    const std::int64_t w = 2205;
    const std::size_t srcLen = 400000, outFrames = 60000, from = 20000, len = 32768;

    struct Row { const char* label; double freq; double rate; double semis; };
    const Row rows[] = {
        {"30 Hz +2 st, rate 1.0", 30.0, 1.0, 2.0},
        {"30 Hz rate 2.0",        30.0, 2.0, 0.0},
        {"34 Hz +2 st, rate 1.0", 34.0, 1.0, 2.0},   // control: alignable without a period
        {"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);
        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 double shift = std::pow(2.0, r.semis / 12.0);
        const double want = period / shift;
        const std::vector<double> off = runStretch(src, w, r.rate, shift, outFrames, nullptr);
        const std::vector<double> on =
            runStretch(src, w, r.rate, shift, outFrames, nullptr, period);
        for (double v : on) CHECK(std::isfinite(v));
        const double floor = idealToneFloorPercent(want, from, len);
        const double pctOff = energyOutsideFundamentalPercent(off, from, len, want) - floor;
        const double pctOn = energyOutsideFundamentalPercent(on, from, len, want) - floor;
        std::printf("  [30 Hz] %-24s (want %6.1f fr, metric floor %.2f%%) excess energy: "
                    "fixed window %6.2f%% -> pitch-synchronous %6.2f%%\n", r.label, want, floor,
                    pctOff, pctOn);
        if (r.freq == 34.0) {
            controlWorst = std::max(controlWorst, pctOn);
        } else {
            subjectWorst = std::max(subjectWorst, pctOn);
            subjectWorstMin = std::min(subjectWorstMin, pctOn);
            subjectOffWorst = std::max(subjectOffWorst, pctOff);
        }
    }
    // 30 Hz stops being a special case: with the period known its excess over the metric's own
    // floor is no worse than the alignable neighbour's, measured identically. Against the
    // control rather than against a fixed number, so the assertion cannot be satisfied by a
    // change that merely raised the floor everywhere.
    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
    // nothing would render both arms identically and fail here rather than passing on the
    // absolute bound above by luck.
    CHECK(subjectOffWorst > subjectWorst + 1.0);
}

// The sharpest single symptom of the geometry: at 29 Hz the nearest multiple misses the
// reachable interval ONE-SIDED rather than straddling, so the per-splice phase steps stop
// cancelling and accumulate into a real detune — the investigation measured -133 cents at
// rate 2.0 with NO transposition at all. Rate moves duration; it must not move pitch.
static void testTwentyNineHertzAtRateTwoKeepsItsPitch() {
    using reasampler::test_support::autocorrelationPeriod;
    const std::int64_t w = 2205;
    const double period = 44100.0 / 29.0;      // 1520.7 frames
    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));
    }
    auto centsOf = [&](double sourcePeriod) {
        const std::vector<double> out =
            runStretch(src, w, /*rate=*/2.0, /*shift=*/1.0, 60000, nullptr, sourcePeriod);
        const double got = autocorrelationPeriod(out, 20000, 20000,
                                                 static_cast<std::int64_t>(period * 0.5),
                                                 static_cast<std::int64_t>(period * 1.7));
        return 1200.0 * std::log2(got / period);
    };
    const double centsOff = centsOf(0.0);
    const double centsOn = centsOf(period);
    std::printf("  [29 Hz] rate 2.0, no transposition: fixed window %+.1f cents -> "
                "pitch-synchronous %+.1f cents\n", centsOff, centsOn);
    CHECK(std::fabs(centsOn) < 10.0);
    // The fixed-window reading is asserted too, and that is what makes the pair non-vacuous: a
    // setSourcePeriod that silently did nothing would render both identically and fail here.
    CHECK(std::fabs(centsOff) > 50.0);
}

// The cadence corner (rate 2.0, -24 st, source periods above the 315-frame safe floor) is the
// OTHER mechanism — a splice landing inside a single perceived cycle. Measured against the
// metric's own floor, PSOLA moves it by nothing: 0.23/0.51/0.30% excess becomes 0.24/0.49/0.30%.
//
// That is not a shortfall, it is what the corner turned out to be. Correcting the previous
// test's reading (see its comment) shrank the corner from a 7-21% headline to a sub-1% excess,
// which leaves PSOLA nothing to recover there — a pitch-synchronous jump makes each splice
// land in phase, and these splices already did; what it cannot do is make them less frequent.
// So this asserts NO REGRESSION, not an improvement, and says so rather than claiming one.
static void testCadenceCornerIsUnmovedByAPitchSynchronousSplice() {
    using reasampler::test_support::energyOutsideFundamentalPercent;
    const std::int64_t w = 2205;
    const double shift = std::pow(2.0, -24.0 / 12.0);
    const std::size_t outFrames = 60000, from = 20000, len = 32768;
    for (double period : {500.0, 600.0, 700.0}) {
        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, 2.0, shift, outFrames, nullptr);
        const std::vector<double> on =
            runStretch(src, w, 2.0, 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 corner, PSOLA] period %.0f (want %.0f, metric floor %.2f%%): "
                    "excess %.2f%% -> %.2f%%\n", period, want, floor, pctOff - floor,
                    pctOn - floor);
        CHECK(pctOn - floor < 1.0);            // the corner's real excess, PSOLA or not
        CHECK(pctOn < pctOff + 0.05);          // and PSOLA costs it nothing
    }
}

// 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() {
    PitchShifter ps;
    CHECK(!ps.configured());
    ps.writeFrame(0.5f);              // no ring to write into
    CHECK(ps.processNoInput() == 0.0f);  // no input to pass through
    CHECK(ps.process(0.25f) == 0.25f);   // and the 1:1 path still passes through
}

int main() {
    testDurationInvariance();
    testUnityRoughlyReproduces();
    testTransposeDirection();
    testRtDisciplineAndPassthrough();
    testRepitchSpectralPurityAndOnset();
    testUnityBitExactAndLatency();
    testFreezeTailContinuousTone();
    testStereoLinkedLagSharedSchedule();
    testStretchMovesDurationNotPitch();
    testStretchAndShiftComposeSafely();
    testStretchCadenceCornerArtifactEnergyAtRate2ShiftQuarter();
    testPeriodAlignedJumpSnapsToWholePeriodsWithinTheReachableBound();
    testAnUnknownPeriodIsBitIdenticalToTheFixedWindowGeometry();
    testThirtyHertzSplicesAlignOnceTheSourcePeriodIsKnown();
    testTwentyNineHertzAtRateTwoKeepsItsPitch();
    testCadenceCornerIsUnmovedByAPitchSynchronousSplice();
    testCadenceCollapseBandAtThirtyHertzUnderPitchSynchronousSplice();
    testStretchEntryPointsOnPassThrough();

    if (g_fail == 0) {
        std::printf("all pitch_shift tests passed\n");
        return 0;
    }
    std::printf("%d pitch_shift check(s) failed\n", g_fail);
    return 1;
}
