delays.lib

Delays library. Its official prefix is de.

This library provides reusable building blocks for delay-based processing: single and multi-tap delays, fractional delays and utilities for echo and spatial effects.

The Delays library is organized into 4 sections:

References

Basic Delay Functions


(de.)delay

Simple d samples delay where n is the maximum delay length as a number of samples. Unlike the @ delay operator, here the delay signal d is explicitly bounded to the interval [0..n]. The consequence is that delay will compile even if the interval of d can't be computed by the compiler. delay is a standard Faust function.

Usage

_ : delay(n,d) : _

Where:

  • n: the max delay length in samples
  • d: the delay length in samples (integer)

Test

de = library("delays.lib");
os = library("oscillators.lib");
delay_test = os.tosc(440) : de.delay(44100, 22050);

TODO: add MBH np2


(de.)fdelay

fdelay — response plots

Simple d samples fractional delay based on 2 interpolated delay lines where n is the maximum delay length as a number of samples.

fdelay is a standard Faust function.

Usage

_ : fdelay(n,d) : _

Where:

  • n: the max delay length in samples
  • d: the delay length in samples (float)

Test

de = library("delays.lib");
os = library("oscillators.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
fdelay_test = os.tosc(440) : de.fdelay(44100, 22050.5);
fdelay_slider_test = os.tosc(440) : de.fdelay(44100, hslider("fdelay:d", 22050.5, 0, 44100, 0.1));
fdelay_modulated_test = no.noise : de.fdelay(256, 16 + 112*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };

(de.)sdelay

s(mooth)delay: a mono delay that doesn't click and doesn't transpose when the delay time is changed.

Usage

_ : sdelay(n,it,d) : _

Where:

  • n: the max delay length in samples
  • it: interpolation time (in samples), for example 1024
  • d: the delay length in samples (float)

Test

de = library("delays.lib");
os = library("oscillators.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
sdelay_test = os.tosc(440) : de.sdelay(44100, 1024, 22050.5);
sdelay_slider_test = os.tosc(440) : de.sdelay(44100, 1024, hslider("sdelay:d", 22050.5, 0, 44100, 0.1));
sdelay_jump_test = no.noise : de.sdelay(4096, 1024, select2(sq, 1000, 3000)) with { P = int(ma.SR/10); sq = ba.period(2*P) < P; };

(de.)prime_power_delays

Prime Power Delay Line Lengths: outputs N constant delay lengths in samples, each one a power of a distinct prime. Delay line i is a power of the ith prime, chosen closest (in the log domain) to a length spaced geometrically between the travel times of pathmin and pathmax at the speed of sound. Each power is capped at the largest one that does not exceed 8192 samples, so that a delay line of 8192 samples holds all of them (8192 samples is 63.7 m at 44.1 kHz; at higher sampling rates, longer paths are capped).

Usage

prime_power_delays(N,pathmin,pathmax) : si.bus(N)

Where:

  • N: positive integer up to 16 (for higher powers of 2, extend 'primes' array below)
  • pathmin: minimum acoustic ray length in the reverberator (in meters)
  • pathmax: maximum acoustic ray length (meters) - think "room size"

The N outputs are the delay-line lengths in samples, all at most 8192.

Test

de = library("delays.lib");
prime_power_delays_test = de.prime_power_delays(4, 1, 10);
prime_power_delays_capped_test = de.prime_power_delays(16, 46, 63);
prime_power_delays_N1_test = de.prime_power_delays(1, 1, 10);

References

Lagrange Interpolation


(de.)fdelaylti, (de.)fdelayltv

fdelaylti — response plots

Fractional delay line using Lagrange interpolation.

Usage

_ : fdelaylti(N, n, d) : _
_ : fdelayltv(N, n, d) : _

Where:

  • N: the order of the Lagrange interpolation polynomial (1, 2, 3, ...), a constant numerical expression
  • n: the max delay length in samples
  • d: the delay length in samples

fdelaylti is most efficient, but designed for constant/slowly-varying delay. fdelayltv is more expensive and more robust when the delay varies rapidly.

Note: the requested delay should not be less than (N-1)/2.

Test

de = library("delays.lib");
os = library("oscillators.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
fdelaylti_test = os.tosc(440) : de.fdelaylti(3, 44100, 22050.5);
fdelayltv_test = os.tosc(440) : de.fdelayltv(3, 44100, 22050.5);
fdelaylti_slider_test = os.tosc(440) : de.fdelaylti(3, 44100, hslider("fdelaylti:d", 22050.5, 1, 44100, 0.1));
fdelaylti_modulated_test = no.noise : de.fdelaylti(3, 256, 2 + 62*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelayltv_slider_test = os.tosc(440) : de.fdelayltv(3, 44100, hslider("fdelayltv:d", 22050.5, 1, 44100, 0.1));
fdelayltv_modulated_test = no.noise : de.fdelayltv(3, 256, 2 + 62*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelayltv_max_test = no.noise : de.fdelayltv(3, 64, 63.3);

References


(de.)fdelay[N]

For convenience, fdelay1, fdelay2, fdelay3, fdelay4, fdelay5 are also available where N is the order of the interpolation, built using fdelayltv.

Usage

_ : fdelay1(maxdel,d) : _
_ : fdelay2(maxdel,d) : _
_ : fdelay3(maxdel,d) : _
_ : fdelay4(maxdel,d) : _
_ : fdelay5(maxdel,d) : _

Where:

  • maxdel: maximum delay in samples
  • d: current (float) delay in samples; must satisfy d > (N-1)/2 for order-N Lagrange interpolation (see de.fdelayltv)

Test

de = library("delays.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
os = library("oscillators.lib");
fdelay1_test = os.tosc(440) : de.fdelay1(44100, 22050.5);
fdelay2_test = os.tosc(440) : de.fdelay2(44100, 22050.5);
fdelay3_test = os.tosc(440) : de.fdelay3(44100, 22050.5);
fdelay4_test = os.tosc(440) : de.fdelay4(44100, 22050.5);
fdelay5_test = os.tosc(440) : de.fdelay5(44100, 22050.5);
fdelay3_modulated_test = no.noise : de.fdelay3(256, 2 + 62*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };

Thiran Allpass Interpolation

Thiran Allpass Interpolation.

References


(de.)fdelay[N]a

Delay lines interpolated using Thiran allpass interpolation.

Usage

_ : fdelay[N]a(n, d) : _

(exactly like fdelay)

Where:

  • N=1,2,3, or 4 is the order of the Thiran interpolation filter (constant numerical expression), and the delay argument is at least N-1/2. First-order: d at least 0.5, second-order: d at least 1.5, third-order: d at least 2.5, fourth-order: d at least 3.5.
  • n: the max delay length in samples
  • d: the delay length in samples

Test

de = library("delays.lib");
os = library("oscillators.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
fdelay2a_test = os.tosc(440) : de.fdelay2a(44100, 22050.5);
fdelay1a_test = os.tosc(440) : de.fdelay1a(44100, 22050.5);
fdelay3a_test = os.tosc(440) : de.fdelay3a(44100, 22050.5);
fdelay4a_test = os.tosc(440) : de.fdelay4a(44100, 22050.5);
fdelay2a_slider_test = os.tosc(440) : de.fdelay2a(44100, hslider("fdelay2a:d", 22050.5, 1.5, 44100, 0.1));
fdelay1a_modulated_test = no.noise : de.fdelay1a(256, 0.6 + 30*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelay1a_jump_test = no.noise : de.fdelay1a(256, select2(sq, 0.6, 40)) with { P = int(ma.SR/10); sq = ba.period(2*P) < P; };
fdelay2a_modulated_test = no.noise : de.fdelay2a(256, 1.6 + 30*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelay2a_jump_test = no.noise : de.fdelay2a(256, select2(sq, 1.6, 40)) with { P = int(ma.SR/10); sq = ba.period(2*P) < P; };
fdelay3a_modulated_test = no.noise : de.fdelay3a(256, 2.6 + 30*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelay3a_jump_test = no.noise : de.fdelay3a(256, select2(sq, 2.6, 40)) with { P = int(ma.SR/10); sq = ba.period(2*P) < P; };
fdelay4a_modulated_test = no.noise : de.fdelay4a(256, 3.6 + 30*tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
fdelay4a_jump_test = no.noise : de.fdelay4a(256, select2(sq, 3.6, 40)) with { P = int(ma.SR/10); sq = ba.period(2*P) < P; };

Note

The interpolated delay should not be less than N-1/2. (The allpass delay ranges from N-1/2 to N+1/2). This constraint can be alleviated by altering the code, but be aware that allpass filters approach zero delay by means of pole-zero cancellations.

Delay arguments too small will produce an UNSTABLE allpass!

Because allpass interpolation is recursive, it is not as robust as Lagrange interpolation under time-varying conditions (you may hear clicks when changing the delay rapidly).

Others


(de.)multiTapSincDelay

Variable delay line using multi-tap sinc interpolation.

This function implements a continuously variable delay line by superposing (2K+2) auxiliary delayed signals whose positions and gains are determined by a sinc-based interpolation method. It extends the traditional crossfade delay technique to significantly reduce spectral coloration artifacts, which are problematic in applications like Wave Field Synthesis (WFS) and auralization.

Operation:

  • If tau1 and tau2 are very close (|tau2 - tau1| ≈ 0), a simple fixed fractional delay is applied
  • Otherwise, a variable delay is synthesized by:

    • Computing (2K+2) taps symmetrically distributed around tau1 and tau2
    • Applying sinc-based weighting to each tap, based on its offset from the target interpolated delay tau
    • Summing all the weighted taps to produce the output

Features:

  • Smooth delay variation without introducing Doppler pitch shifts
  • Significant reduction of comb-filter coloration compared to classical crossfading
  • Switching between fixed and variable delay modes to ensure stability

Usage

_ : multiTapSincDelay(K, MaxDelay, tau1, tau2, alpha) : _

Where:

  • K (integer): number of auxiliary tap pairs (a constant numerical expression). Total number of taps = 2*K + 2
  • MaxDelay: maximum allowable delay in samples (buffer size)
  • tau1: initial delay in samples (can be fractional)
  • tau2: target delay in samples (can be fractional)
  • alpha: interpolation factor between tau1 and tau2 (in [0,1] with 0 = tau1, 1 = tau2)

Test

de = library("delays.lib");
os = library("oscillators.lib");
ba = library("basics.lib");
ma = library("maths.lib");
no = library("noises.lib");
multiTapSincDelay_test = os.tosc(440) : de.multiTapSincDelay(2, 4096, 1024.0, 1536.0, 0.5);
multiTapSincDelay_slider_test = os.tosc(440) : de.multiTapSincDelay(2, 4096, hslider("multiTapSincDelay:tau1", 1024, 0, 4000, 0.1), hslider("multiTapSincDelay:tau2", 1536, 0, 4000, 0.1), hslider("multiTapSincDelay:alpha", 0.5, 0, 1, 0.01));
multiTapSincDelay_modulated_test = no.noise : de.multiTapSincDelay(2, 4096, 1024.0, 1536.0, tri) with { P = int(ma.SR/10); tri = 1 - abs(2*ba.period(P)/P - 1); };
multiTapSincDelay_near_test = os.tosc(440) : de.multiTapSincDelay(2, 4096, 1024.0, 1024.001, 0.5);

References

T. Carpentier, "Implementation of a continuously variable delay line by crossfading between several tap delays", 2024: https://hal.science/hal-04646939