Skip to main content

DelayLine

Struct DelayLine 

pub struct DelayLine { /* private fields */ }
Expand description

Generic delay line: a ring buffer with WDF-compatible ports.

This is a two-port element that splits the WDF tree into separate subtrees coupled by an implicit one-sample delay. The write side accepts signal from the “record” subtree; the read side feeds the “playback” subtree(s).

The delay time is continuously modulatable — changing it at audio rate moves the read pointer smoothly, creating pitch bending effects.

§WDF integration

The compiler detects delay_line() in a netlist and automatically splits the WDF tree at that point. Trees are processed in order: write tree first (fills buffer), then read tree(s) consume from it. The implicit one-sample delay between write and read ensures causality.

§Multi-tap

Multiple Tap elements can read from the same DelayLine at different positions (ratios of the base delay time). This models multi-head tape machines (RE-201) and multi-tap digital delays.

Implementations§

§

impl DelayLine

pub fn new( min_delay_sec: f64, max_delay_sec: f64, sample_rate: f64, interpolation: Interpolation, ) -> DelayLine

Create a new delay line.

  • min_delay_sec — minimum delay in seconds (e.g., 0.001 for 1ms)
  • max_delay_sec — maximum delay in seconds (e.g., 1.2 for 1200ms)
  • sample_rate — audio sample rate in Hz
  • interpolation — fractional-sample interpolation mode

pub fn write(&mut self, input: f64)

Write a sample into the delay buffer, process medium zones, and advance the write pointer.

The input signal is mixed with the feedback sample before writing. After writing, zone-based medium processing runs on any samples that have crossed zone boundaries since the last tick. Call this once per audio sample, before reading any taps.

pub fn read(&mut self) -> f64

Read from the delay buffer at the current base delay time.

Returns the interpolated sample at write_pos - current_delay_samples. This is equivalent to reading from tap ratio 1.0.

pub fn read_at_ratio(&mut self, ratio: f64, tap_index: usize) -> f64

Read from the delay buffer at a specific ratio of the base delay.

  • ratio — multiplier for the base delay (e.g., 2.0 = twice the delay)
  • tap_index — index for allpass state tracking (0 = base, 1+ = taps)

The effective delay in samples is: current_delay_samples * speed_mod * ratio

pub fn set_delay_normalized(&mut self, norm: f64)

Set the delay time as a normalized value (0.0 = min, 1.0 = max).

Uses logarithmic interpolation for a natural feel — small changes at short delays, larger changes at long delays.

pub fn set_delay_seconds(&mut self, seconds: f64)

Set the delay time in seconds directly.

pub fn set_speed_mod(&mut self, factor: f64)

Set the speed modulation factor.

A value of 1.0 means no modulation. Values > 1.0 slow the “tape” (longer delay, lower pitch). Values < 1.0 speed it up. Typically modulated by an LFO for wow/flutter effects.

pub fn add_speed_mod(&mut self, offset: f64)

Add a speed modulation offset (additive, for multiple LFO sources).

The total speed_mod = 1.0 + sum of all modulation offsets.

pub fn reset_speed_mod(&mut self)

Reset speed modulation to 1.0 (called at start of each sample).

pub fn set_feedback(&mut self, feedback: f64)

Set the feedback amount.

pub fn set_feedback_sample(&mut self, sample: f64)

Store the feedback sample (called after downstream processing).

pub fn delay_time(&self) -> f64

Get the current delay time in seconds.

pub fn effective_delay_samples(&self) -> f64

Get the current delay time in samples (with speed modulation).

pub fn set_sample_rate(&mut self, sample_rate: f64)

Update for a new sample rate.

pub fn ensure_tap_capacity(&mut self, max_ratio: f64)

Grow the ring buffer so a tap at max_ratio × the base delay can be read without clamping. DelayLine::new sizes the buffer for the base (head-1) max_delay only; a multi-head tape (RE-201 heads at 2x/3x) reads up to max_ratio × max_delay, which would otherwise clamp at the buffer end (heads 2/3 collapse onto the buffer-max position). The base max_delay_sec — and thus the set_delay_normalized log law — is unchanged; only the storage grows. No-op when max_ratio <= 1.0 or the buffer already fits.

pub fn reset(&mut self)

Reset all state (buffer, allpass states, feedback).

pub fn min_delay_sec(&self) -> f64

Minimum delay in seconds.

pub fn max_delay_sec(&self) -> f64

Maximum delay in seconds.

pub fn set_medium(&mut self, medium: Medium)

Set the physical storage medium model.

pub fn medium(&self) -> Medium

Get the current medium model.

pub fn sample_rate(&self) -> f64

Get the current sample rate.

pub fn write_direct(&mut self, sample: f64)

Write a sample directly to the buffer without feedback mixing.

Does NOT advance the write position — call [advance_write] after reading to complete the tick. This is used by BbdDelayLine which manages its own feedback path (with soft-clipping between mix and write).

pub fn buffer_read(&mut self, delay_samples: f64, tap_slot: usize) -> f64

Read from the buffer at the given delay (in samples), using the current interpolation mode.

Lower-level than [read]/[read_at_ratio]: the caller supplies the exact fractional-sample delay instead of using the stored delay time.

pub fn advance_write(&mut self)

Advance the write position by one sample and run medium processing.

Must be called exactly once per tick, after [write_direct] and any reads are complete.

pub fn configure_zones_from_taps( &mut self, tap_ratios: &[f64], coefficients: Option<&[f64]>, )

Configure zone boundaries from tap ratios.

Creates one zone boundary at each tap position, with coefficients that increase with distance (modeling progressive signal degradation). An additional zone is placed at the end of the buffer for the feedback path.

  • tap_ratios — tap positions as ratios of base delay (e.g., [1.0, 2.0, 4.0])
  • coefficients — optional per-zone LP coefficients; if None, uses defaults based on medium type and tap count

Trait Implementations§

§

impl Clone for DelayLine

§

fn clone(&self) -> DelayLine

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for DelayLine

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more