Skip to main content

rill_digital_effects/
delay.rs

1//! Delay effect with feedback
2
3use rill_core::{
4    buffer::DelayLine,
5    math::Transcendental,
6    traits::{Node, NodeCategory, NodeMetadata, NodeState, Processor},
7    NodeId, ParamValue, ParameterId, Port, ProcessError, ProcessResult, RenderContext,
8};
9
10/// Maximum delay time in seconds
11const MAX_DELAY_SECONDS: f32 = 0.5;
12/// Maximum sample rate we support (48 kHz)
13const MAX_SAMPLE_RATE: f32 = 48_000.0;
14/// Maximum delay in samples (2 seconds at max sample rate)
15const MAX_DELAY_SAMPLES: usize = (MAX_DELAY_SECONDS * MAX_SAMPLE_RATE) as usize;
16
17/// Delay effect with feedback
18///
19/// Parameters:
20/// - delay_time: delay time in seconds (0.01 - 2.0)
21/// - feedback: feedback amount (0.0 - 0.99)
22/// - mix: dry/wet mix (0.0 - 1.0)
23pub struct Delay<T: Transcendental, const BUF_SIZE: usize> {
24    /// Node identifier
25    id: NodeId,
26    /// Node metadata
27    metadata: NodeMetadata,
28    /// Input ports
29    inputs: Vec<Port<T, BUF_SIZE>>,
30    /// Output ports
31    outputs: Vec<Port<T, BUF_SIZE>>,
32    /// Control ports
33    controls: Vec<Port<T, BUF_SIZE>>,
34    /// Node state
35    state: NodeState<T, BUF_SIZE>,
36    /// Delay time in seconds
37    pub delay_time: f32,
38    /// Delay time in samples
39    delay_samples: usize,
40    /// Feedback amount (0.0 - 0.99)
41    pub feedback: f32,
42    /// Dry/wet mix (0.0 = dry, 1.0 = wet)
43    pub mix: f32,
44    /// Delay line
45    delay_line: DelayLine<T, MAX_DELAY_SAMPLES>,
46    /// Sample rate (cached)
47    sample_rate: f32,
48}
49
50impl<T: Transcendental, const BUF_SIZE: usize> Delay<T, BUF_SIZE> {
51    /// Create a new delay effect with default parameters
52    pub fn new(sample_rate: f32) -> Self {
53        let metadata = NodeMetadata::new("Delay", NodeCategory::Processor);
54
55        let mut inputs = Vec::new();
56        let mut outputs = Vec::new();
57
58        // Create one audio input and one audio output
59        inputs.push(Port::input(NodeId(0), 0, "signal_in"));
60        outputs.push(Port::output(NodeId(0), 0, "signal_out"));
61
62        let delay_time = 0.5;
63        let delay_samples = (delay_time * sample_rate) as usize;
64        let mut delay_line = DelayLine::new(sample_rate);
65        delay_line.set_delay_samples(delay_samples);
66
67        Self {
68            id: NodeId(0),
69            metadata,
70            inputs,
71            outputs,
72            controls: Vec::new(),
73            state: NodeState::new(sample_rate),
74            delay_time,
75            delay_samples,
76            feedback: 0.3,
77            mix: 0.5,
78            delay_line,
79            sample_rate,
80        }
81    }
82
83    /// Create a new delay effect with custom parameters
84    pub fn with_params(sample_rate: f32, delay_time: f32, feedback: f32, mix: f32) -> Self {
85        let mut instance = Self::new(sample_rate);
86        instance.set_delay_time(delay_time);
87        instance.set_feedback(feedback);
88        instance.set_mix(mix);
89        instance
90    }
91
92    /// Set delay time in seconds
93    pub fn set_delay_time(&mut self, time: f32) {
94        self.delay_time = time.clamp(0.01, MAX_DELAY_SECONDS);
95        self.update_delay_samples();
96    }
97
98    /// Set feedback amount
99    pub fn set_feedback(&mut self, fb: f32) {
100        self.feedback = fb.clamp(0.0, 0.99);
101    }
102
103    /// Set dry/wet mix
104    pub fn set_mix(&mut self, mix: f32) {
105        self.mix = mix.clamp(0.0, 1.0);
106    }
107
108    /// Update delay samples based on current sample rate
109    fn update_delay_samples(&mut self) {
110        self.delay_samples = (self.delay_time * self.sample_rate) as usize;
111        if self.delay_samples >= MAX_DELAY_SAMPLES {
112            self.delay_samples = MAX_DELAY_SAMPLES - 1;
113        }
114        self.delay_line.set_delay_samples(self.delay_samples);
115    }
116
117    /// Process a single sample (internal helper)
118    pub fn process_sample(&mut self, input: T) -> T {
119        // Read delayed sample
120        let delayed = self.delay_line.read_delayed(self.delay_samples);
121        // Output mix
122        let dry = input;
123        let wet = delayed;
124        let mix = T::from_f32(self.mix);
125        let one_minus_mix = T::ONE - mix;
126        let output = dry.mul(one_minus_mix).add(wet.mul(mix));
127        // Write input with feedback
128        let feedback = T::from_f32(self.feedback);
129        let write_sample = input.add(delayed.mul(feedback));
130        self.delay_line.write(write_sample);
131        output
132    }
133}
134
135impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for Delay<T, BUF_SIZE> {
136    fn node_type_id(&self) -> rill_core::NodeTypeId
137    where
138        Self: 'static + Sized,
139    {
140        rill_core::NodeTypeId::of::<Self>()
141    }
142
143    fn id(&self) -> NodeId {
144        self.id
145    }
146
147    fn set_id(&mut self, id: NodeId) {
148        self.id = id;
149        // Update port IDs? For simplicity, we ignore for now.
150    }
151
152    fn metadata(&self) -> NodeMetadata {
153        self.metadata.clone()
154    }
155
156    fn init(&mut self, sample_rate: f32) {
157        self.sample_rate = sample_rate;
158        self.update_delay_samples();
159        self.delay_line.clear();
160    }
161
162    fn reset(&mut self) {
163        self.state.sample_pos = 0;
164        self.state.blocks_processed = 0;
165        self.delay_line.clear();
166    }
167
168    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
169        let name = id.as_str();
170        match name {
171            "delay_time" => Some(ParamValue::Float(self.delay_time)),
172            "feedback" => Some(ParamValue::Float(self.feedback)),
173            "mix" => Some(ParamValue::Float(self.mix)),
174            _ => None,
175        }
176    }
177
178    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
179        let name = id.as_str();
180        if let Some(v) = value.as_f32() {
181            match name {
182                "delay_time" => {
183                    self.set_delay_time(v);
184                    Ok(())
185                }
186                "feedback" => {
187                    self.set_feedback(v);
188                    Ok(())
189                }
190                "mix" => {
191                    self.set_mix(v);
192                    Ok(())
193                }
194                _ => Err(ProcessError::parameter(format!(
195                    "Unknown parameter: {}",
196                    name
197                ))),
198            }
199        } else {
200            Err(ProcessError::parameter("Expected float value"))
201        }
202    }
203
204    fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
205        self.inputs.get(index)
206    }
207
208    fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
209        self.inputs.get_mut(index)
210    }
211
212    fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
213        self.outputs.get(index)
214    }
215
216    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
217        self.outputs.get_mut(index)
218    }
219
220    fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
221        self.controls.get(index)
222    }
223
224    fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
225        self.controls.get_mut(index)
226    }
227
228    fn num_inputs(&self) -> usize {
229        self.inputs.len()
230    }
231
232    fn num_outputs(&self) -> usize {
233        self.outputs.len()
234    }
235
236    fn num_signal_inputs(&self) -> usize {
237        self.inputs.len()
238    }
239
240    fn num_signal_outputs(&self) -> usize {
241        self.outputs.len()
242    }
243
244    fn state(&self) -> &NodeState<T, BUF_SIZE> {
245        &self.state
246    }
247
248    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
249        &mut self.state
250    }
251}
252
253impl<T: Transcendental, const BUF_SIZE: usize> Processor<T, BUF_SIZE> for Delay<T, BUF_SIZE> {
254    fn process(
255        &mut self,
256        _ctx: &RenderContext,
257        _signal_inputs: &[&[T; BUF_SIZE]],
258        _control_inputs: &[T],
259        _clock_inputs: &[RenderContext],
260        _feedback_inputs: &[&[T; BUF_SIZE]],
261    ) -> ProcessResult<()> {
262        let inp = self.inputs[0].read();
263        let out = self.outputs[0].write();
264        let mix = T::from_f32(self.mix);
265        let one_minus_mix = T::ONE - mix;
266        let feedback = T::from_f32(self.feedback);
267        let delay_samples = self.delay_samples;
268        for i in 0..BUF_SIZE {
269            let input = inp[i];
270            let delayed = self.delay_line.read_delayed(delay_samples);
271            let output = input.mul(one_minus_mix).add(delayed.mul(mix));
272            let write_sample = input.add(delayed.mul(feedback));
273            self.delay_line.write(write_sample);
274            out[i] = output;
275        }
276        self.state.advance();
277        Ok(())
278    }
279
280    fn latency(&self) -> usize {
281        0
282    }
283}