Selectable causal fractional delay¶
FractionalDelay owns one prepared f64 channel and an immutable Integer,
Linear, Lagrange3rd or Thiran1st policy. Construction validates an integer
maximum 0..=4,194,300 samples, then fallibly reserves maximum+4 history values,
at most 32MiB. Zero maximum is legal. Extra slots supply cubic history and add no
hidden delay. Creation/destruction are control work; sample/block processing,
validated delay edits and reset perform no heap allocation/deallocation, locks or
I/O. Reset visits the full prepared ring, so its work depends on capacity.
Requested delay starts at zero. set_delay_samples accepts finite 0..=maximum,
retaining all history and applying the edit immediately without smoothing or
crossfade. Invalid edits preserve requested control, cursor and every history.
One input produces exactly one output and one advancement; a stateful allpass
cannot be read repeatedly as independent taps. Existing DelayLine separate
read/write/advance and chorus/comb/reverb behavior remain unchanged.
For d=n+f, n=floor(d), Integer returns x[t-n], truncating the fractional part.
Linear returns (1-f)x[t-n]+f*x[t-n-1]. Lagrange3rd evaluates the unique cubic
through four causal points x[t-n-j], j=0..3, at fractional coordinate f on nodes
0..3. This deliberately uses one-sided support near current input; it imposes no
extra one-sample minimum or centered-kernel delay. Initial negative-index history
is zero. FIR interpolation is not bandlimited, and cubic reconstruction can
overshoot; neither normalization nor a clamp is applied.
For Thiran1st, v=x[t-n], a=(1-f)/(1+f), and
y=a*v+previous_v-a*previous_y. At a fixed noninteger delay, the transfer is
z^-n(a+z^-1)/(1+az^-1). It has unity magnitude and DC group delay n+f, with
frequency-dependent group delay. This is not an ideal broadband fractional delay.
Small fractional delay places a pole near -1 and leaves an arbitrarily long
alternating tail. Positive fractions whose f64 coefficient rounds to one reject
before controls change; no hidden stability clamp substitutes a different delay.
Exactly integer delay bypasses recursion and updates both prior values to the
current integer tap. Retuning across integer boundaries changes the tap feeding
retained recursive history; static allpass facts do not describe modulation or
feedback transients.
process_sample writes current input before reconstruction and advancement, so
zero delay returns it exactly. process_in_place preserves temporal order.
process_separate requires exact equal lengths, rejecting mismatches before any
state/output edit; two empty slices do nothing. Nonfinite input can propagate and
poison history until reset. Consumers own channel synchronization, feedback,
headroom/denormals, sample-rate/millisecond conversion, tail/drain and host latency.
Independent fixtures cover absolute tap history, cubic polynomials/basis/overshoot, fixed frequency transfer, allpass DC group delay and explicit impulse-tail geometric remainder, integer-edge modulation, rounded-stability rejection, state/output preservation, wrap/reset/partitions, maximum allocation/capacity and callback heap counts. An actual stereo Foundation processor through prepared Graph checks distinct channel delay histories, variable blocks, rejected outer preflight, reset/repeat and allocation-free callbacks. Offline evidence does not establish device/DAW/native timing/deadline or universal modulation stability. See decision 0237.