fix(preserve): clamp Preserve feed to real-content bound at tail; lock sub-sample refinement with non-integer f0; fix stale onset-latency doc + clamp comment
This commit is contained in:
+25
-16
@@ -188,24 +188,29 @@ static void testRtDisciplineAndPassthrough() {
|
||||
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 = 1102/196 ≈ 5.622 cycles (frac ≈ 0.622) — 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 = 22.5 EXACTLY — at ratio 4 the write head advances 4 taps
|
||||
// per output frame, so each splice-period the outgoing tap crosses the writer at the
|
||||
// HALF-period point of the source waveform (sign flip), producing a visible null when
|
||||
// gNew == gOld if fadeLen_ is not clamped to headroom. With f0=0.005 this product
|
||||
// is 22.05 (frac ≈ 0.05), near a zero-crossing — the artifact is near-benign, so the
|
||||
// +24 st purity case would pass even with the clamping reverted. f0=1/196 forces the
|
||||
// half-integer alignment that makes the pre-fix artifact catastrophic.
|
||||
// (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.0; // source: period 196 samples; see adversarial note above
|
||||
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)
|
||||
@@ -282,10 +287,14 @@ static void testRepitchSpectralPurityAndOnset() {
|
||||
// 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 30 dB under the tone.
|
||||
// (Post-fix the engine measures ~-75 dB and better; the old integer-lag splices
|
||||
// sat near -59 dB sidebands and the warm-zero onset failed outright.)
|
||||
CHECK(residRms < 0.0316 * fitRms);
|
||||
// ...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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user