Skip to main content

proteus_lib/dsp/effects/basic_reverb/
mod.rs

1//! Delay reverb effect using a simple feedback delay line.
2
3use log::info;
4use serde::{Deserialize, Serialize};
5
6use super::EffectContext;
7
8const DEFAULT_DURATION_MS: u64 = 100;
9const MAX_AMPLITUDE: f32 = 0.8;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(default)]
13pub struct DelayReverbSettings {
14    pub duration_ms: u64,
15    pub amplitude: f32,
16}
17
18impl DelayReverbSettings {
19    pub fn new(duration_ms: u64, amplitude: f32) -> Self {
20        Self {
21            duration_ms: duration_ms.clamp(0, u64::MAX),
22            amplitude: amplitude.clamp(0.0, MAX_AMPLITUDE),
23        }
24    }
25
26    fn amplitude(&self) -> f32 {
27        self.amplitude.clamp(0.0, MAX_AMPLITUDE)
28    }
29}
30
31impl Default for DelayReverbSettings {
32    fn default() -> Self {
33        Self {
34            duration_ms: DEFAULT_DURATION_MS,
35            amplitude: 0.7,
36        }
37    }
38}
39
40/// Delay reverb effect (feedback delay + mix).
41#[derive(Clone, Serialize, Deserialize)]
42#[serde(default)]
43pub struct DelayReverbEffect {
44    pub enabled: bool,
45    #[serde(alias = "dry_wet", alias = "wet_dry")]
46    pub mix: f32,
47    #[serde(flatten)]
48    pub settings: DelayReverbSettings,
49    #[serde(skip)]
50    state: Option<DelayReverbState>,
51}
52
53impl Default for DelayReverbEffect {
54    fn default() -> Self {
55        Self {
56            enabled: true,
57            mix: 0.0,
58            settings: DelayReverbSettings::default(),
59            state: None,
60        }
61    }
62}
63
64impl std::fmt::Debug for DelayReverbEffect {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("DelayReverbEffect")
67            .field("enabled", &self.enabled)
68            .field("mix", &self.mix)
69            .field("settings", &self.settings)
70            .finish()
71    }
72}
73
74impl DelayReverbEffect {
75    /// Create a new delay reverb effect.
76    pub fn new(mix: f32) -> Self {
77        Self {
78            mix: mix.clamp(0.0, 1.0),
79            ..Default::default()
80        }
81    }
82
83    /// Process interleaved samples through a feedback delay line.
84    pub fn process(&mut self, samples: &[f32], context: &EffectContext, _drain: bool) -> Vec<f32> {
85        self.ensure_state(context);
86        if !self.enabled || self.mix <= 0.0 {
87            return samples.to_vec();
88        }
89
90        let Some(state) = self.state.as_mut() else {
91            return samples.to_vec();
92        };
93
94        let amplitude = if self.mix > 0.0 {
95            self.mix.clamp(0.0, MAX_AMPLITUDE)
96        } else {
97            self.settings.amplitude()
98        };
99
100        if samples.is_empty() {
101            if _drain {
102                return state.drain_tail(amplitude);
103            }
104            return Vec::new();
105        }
106
107        let mut output = Vec::with_capacity(samples.len());
108        state.process_samples(samples, amplitude, &mut output);
109        output
110    }
111
112    /// Reset any internal state (none for delay reverb).
113    pub fn reset_state(&mut self) {
114        if let Some(state) = self.state.as_mut() {
115            state.reset();
116        }
117        self.state = None;
118    }
119
120    /// Mutable access to settings.
121    pub fn settings_mut(&mut self) -> &mut DelayReverbSettings {
122        &mut self.settings
123    }
124
125    fn ensure_state(&mut self, context: &EffectContext) {
126        let delay_samples = delay_samples(
127            context.sample_rate,
128            context.channels,
129            self.settings.duration_ms,
130        );
131        let needs_reset = self
132            .state
133            .as_ref()
134            .map(|state| state.delay_samples != delay_samples)
135            .unwrap_or(true);
136        if needs_reset {
137            self.state = Some(DelayReverbState::new(delay_samples));
138        }
139    }
140}
141
142#[derive(Clone)]
143struct DelayReverbState {
144    delay_samples: usize,
145    delay_line: Vec<f32>,
146    write_pos: usize,
147}
148
149impl DelayReverbState {
150    fn new(delay_samples: usize) -> Self {
151        info!("Using Delay Reverb!");
152        Self {
153            delay_samples,
154            delay_line: vec![0.0; delay_samples.max(1)],
155            write_pos: 0,
156        }
157    }
158
159    fn reset(&mut self) {
160        self.delay_line.fill(0.0);
161        self.write_pos = 0;
162    }
163
164    fn process_samples(&mut self, samples: &[f32], amplitude: f32, out: &mut Vec<f32>) {
165        if self.delay_samples == 0 {
166            out.extend_from_slice(samples);
167            return;
168        }
169
170        let delay_len = self.delay_line.len();
171        for &sample in samples {
172            let delayed = self.delay_line[self.write_pos];
173            let output = sample + (delayed * amplitude);
174            out.push(output);
175
176            // Feedback delay for smoother tails.
177            self.delay_line[self.write_pos] = sample + (delayed * amplitude);
178            self.write_pos += 1;
179            if self.write_pos >= delay_len {
180                self.write_pos = 0;
181            }
182        }
183    }
184
185    fn drain_tail(&mut self, amplitude: f32) -> Vec<f32> {
186        if self.delay_samples == 0 {
187            return Vec::new();
188        }
189
190        let delay_len = self.delay_line.len();
191        let mut out = Vec::with_capacity(delay_len);
192        for _ in 0..delay_len {
193            let delayed = self.delay_line[self.write_pos];
194            let output = delayed * amplitude;
195            out.push(output);
196
197            // Feed silence to decay the tail.
198            self.delay_line[self.write_pos] = delayed * amplitude;
199            self.write_pos += 1;
200            if self.write_pos >= delay_len {
201                self.write_pos = 0;
202            }
203        }
204
205        out
206    }
207}
208
209fn delay_samples(sample_rate: u32, channels: usize, duration_ms: u64) -> usize {
210    if duration_ms == 0 {
211        return 0;
212    }
213    let ns = duration_ms.saturating_mul(1_000_000);
214    let samples = ns.saturating_mul(sample_rate as u64) / 1_000_000_000 * channels as u64;
215    samples as usize
216}
217
218#[deprecated(note = "Use DelayReverbSettings instead.")]
219pub type BasicReverbSettings = DelayReverbSettings;
220
221#[deprecated(note = "Use DelayReverbEffect instead.")]
222pub type BasicReverbEffect = DelayReverbEffect;