Γ-W1-T7: make Preserve's splices pitch-synchronous — the jump is a whole number of the source's own period, detected once at load
30 Hz out-of-band energy 15.45% -> 0.00%; the 29 Hz rate-2.0 detune -133 -> +0 cents. An unknown period keeps the fixed-window geometry bit for bit. The detector cannot reach process(): sampler_core does not link it.
This commit is contained in:
@@ -32,20 +32,27 @@ namespace reasampler::instrument::engine {
|
||||
// actually fine. (The pre-stretch rate-1.0 engine's floor by the same inequality is P > 735,
|
||||
// ~60 Hz — what this range raises the floor from, not what it removes.)
|
||||
//
|
||||
// A SECOND, INDEPENDENT limit binds the same material, and no rate bound touches it. A splice
|
||||
// relocates the tap by the nominal window refined by a search over +/- window/4, so the
|
||||
// reachable relocation distances are exactly [0.75, 1.25] * window; a phase-aligned splice
|
||||
// needs a WHOLE NUMBER of source periods inside that one interval. The interval is 0.5*window
|
||||
// wide, so any period <= window/2 always has a multiple in it — but above that, coverage
|
||||
// breaks into disjoint bands (n=1 covers periods [0.75, 1.25]*window, n=2 covers
|
||||
// [0.375, 0.625]*window) and the gap between them is reachable by nothing. Because both the
|
||||
// interval and the period scale with the sample rate, the unalignable set is fixed in Hz by
|
||||
// the window's MILLISECONDS: at 50 ms that is f < 16 Hz and 26.7 Hz < f < 32 Hz. Measured
|
||||
// (Release, 44.1k and 48k) at 30 Hz: the rendered pitch stays correct, but energy outside the
|
||||
// fundamental is 3.6% at +2 st / rate 1.0 and 15.5% at rate 2.0, against 0.00% at 34 Hz under
|
||||
// identical conditions; at 29 Hz / rate 2.0 the tone itself lands 7.4% flat. Unlike the
|
||||
// cadence inequality above, this one is not about how OFTEN a splice fires — a window of at
|
||||
// least two source periods removes it outright, and nothing else does.
|
||||
// A SECOND, INDEPENDENT limit bound the same material, and no rate bound touched it. It is now
|
||||
// CLOSED for any source whose period is detected, but the geometry is worth keeping because it
|
||||
// is what the fixed-window fallback still lives under. A splice relocated the tap by the
|
||||
// nominal window refined by a search over +/- window/4, so the reachable relocation distances
|
||||
// were exactly [0.75, 1.25] * window; a phase-aligned splice needs a WHOLE NUMBER of source
|
||||
// periods inside that interval. The interval is 0.5*window wide, so any period <= window/2
|
||||
// always has a multiple in it — but above that, coverage breaks into disjoint bands (n=1 covers
|
||||
// periods [0.75, 1.25]*window, n=2 covers [0.375, 0.625]*window) and the gap between them was
|
||||
// reachable by nothing. Because both the interval and the period scale with the sample rate,
|
||||
// that unalignable set is fixed in Hz by the window's MILLISECONDS: at 50 ms, f < 16 Hz and
|
||||
// 26.7 Hz < f < 32 Hz. Measured there (Release, 44.1k and 48k) at 30 Hz: the rendered pitch
|
||||
// stayed correct, but energy outside the fundamental was 3.6% at +2 st / rate 1.0 and 15.5% at
|
||||
// rate 2.0, against 0.00% at 34 Hz under identical conditions; at 29 Hz / rate 2.0 the tone
|
||||
// itself landed 7.4% flat (-133 cents).
|
||||
//
|
||||
// The fix is not a wider window: it is a nominal jump that is a whole number of the source's
|
||||
// own periods, so an aligned landing point exists by construction (pitch_shift.h's
|
||||
// periodAlignedJump, fed by period_detect at load). The same measurements then read 0.00% and
|
||||
// 0.00%, and 29 Hz renders at +0.0 cents. What survives: a period longer than the reachable
|
||||
// jump (~1.25 windows, so below ~16 Hz at 50 ms) still cannot align, and a source with no
|
||||
// single period falls back to this fixed-window geometry by design.
|
||||
inline constexpr double kStretchRateMin = 0.5;
|
||||
inline constexpr double kStretchRateMax = 2.0;
|
||||
inline constexpr int kMaxFeedPerFrame = 2; // ceil(kStretchRateMax)
|
||||
|
||||
Reference in New Issue
Block a user