Skip to main content

rust_audio_api/nodes/
delay.rs

1use crate::types::{AudioUnit, empty_audio_unit};
2use std::collections::VecDeque;
3
4/// A node that delays the input signal by a specified number of audio blocks.
5///
6/// Each audio block ([`AudioUnit`]) contains [`AUDIO_UNIT_SIZE`](crate::types::AUDIO_UNIT_SIZE) samples.
7///
8/// Supports dynamic delay updates via [`ControlMessage::SetParameter`](crate::graph::ControlMessage::SetParameter).
9///
10/// # Example
11/// ```no_run
12/// use rust_audio_api::nodes::{DelayNode, NodeType};
13/// use rust_audio_api::{AudioContext, NodeParameter};
14///
15/// let mut ctx = AudioContext::new().unwrap();
16/// let sample_rate = ctx.sample_rate();
17///
18/// let mut delay_id = None;
19/// let dest_id = ctx.build_graph(|builder| {
20///     // Max delay 2 seconds, initial delay 0.5 seconds
21///     let max_units = (sample_rate as usize * 2) / rust_audio_api::types::AUDIO_UNIT_SIZE;
22///     let initial_units = (sample_rate as f32 * 0.5) as usize / rust_audio_api::types::AUDIO_UNIT_SIZE;
23///     
24///     let delay = builder.add_node(NodeType::Delay(DelayNode::new(max_units, initial_units)));
25///     delay_id = Some(delay);
26///     delay
27/// });
28///
29/// // Dynamically change the delay to 1.0 seconds
30/// let new_units = (sample_rate as f32 * 1.0) as usize / rust_audio_api::types::AUDIO_UNIT_SIZE;
31/// ctx.control_sender().send(
32///     rust_audio_api::graph::ControlMessage::SetParameter(
33///         delay_id.unwrap(),
34///         NodeParameter::DelayUnits(new_units)
35///     )
36/// ).unwrap();
37/// ```
38pub struct DelayNode {
39    queue: VecDeque<AudioUnit>,
40    delay_units: usize,
41    max_delay_units: usize,
42}
43
44impl DelayNode {
45    /// Creates a new `DelayNode`.
46    ///
47    /// # Parameters
48    /// - `max_delay_units`: The maximum delay buffer size in units.
49    /// - `default_delay_units`: The initial delay in units.
50    pub fn new(max_delay_units: usize, default_delay_units: usize) -> Self {
51        let delay_units = default_delay_units.min(max_delay_units);
52        let mut queue = VecDeque::with_capacity(max_delay_units + 1);
53
54        // Seed the queue with silent Units based on initial delay_units
55        for _ in 0..delay_units {
56            queue.push_back(empty_audio_unit());
57        }
58
59        Self {
60            queue,
61            delay_units,
62            max_delay_units,
63        }
64    }
65
66    /// Dynamically updates the delay time.
67    ///
68    /// If the new delay is larger than the current one, silent blocks are inserted.
69    /// If it is smaller, old blocks are discarded.
70    pub fn set_delay_units(&mut self, units: usize) {
71        let target_units = units.min(self.max_delay_units);
72
73        if target_units > self.delay_units {
74            // Increase delay: add silent Units
75            for _ in 0..(target_units - self.delay_units) {
76                self.queue.push_front(empty_audio_unit()); // Push to front; these are the new delayed units
77            }
78        } else if target_units < self.delay_units {
79            // Decrease delay: discard old Units (front represents the oldest)
80            for _ in 0..(self.delay_units - target_units) {
81                self.queue.pop_front();
82            }
83        }
84        self.delay_units = target_units;
85    }
86
87    #[inline(always)]
88    pub fn process(&mut self, input: Option<&AudioUnit>, output: &mut AudioUnit) {
89        // Core algorithm: push input (or silence) into the queue
90        if let Some(in_unit) = input {
91            self.queue.push_back(*in_unit);
92        } else {
93            self.queue.push_back(empty_audio_unit());
94        }
95
96        // Then pop a unit from the queue as current output
97        // If delay_units is 0, the queue will only contain the unit just pushed;
98        // popping it results in zero delay.
99        if let Some(delayed_unit) = self.queue.pop_front() {
100            output.copy_from_slice(&delayed_unit);
101        } else {
102            // Fallback mechanism; theoretically, the queue should always have at least one unit
103            dasp::slice::equilibrium(&mut output[..]);
104        }
105    }
106}