rustriff_lib/services/effects/delay/
delay.rs1use crate::domain::audio_processor::AudioProcessor;
2use crate::domain::dto::effect::delay_dto::DelayDto;
3use crate::domain::dto::effect::effect_dto::EffectDto;
4use crate::domain::effect::Effect;
5use atomic_float::AtomicF32;
6use std::collections::HashMap;
7use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
8use std::sync::Arc;
9
10pub struct Delay {
11 id: u32,
12 name: String,
13 is_active: Arc<AtomicBool>,
14 color: String,
15 delay_time: Arc<AtomicU32>, level: Arc<AtomicF32>, delay_buffer: Vec<f32>,
18 write_pos: usize,
19 sample_rate: u32,
20 delay_in_samples: usize,
21 last_feedback_output: f32,
22}
23
24const MAX_DELAY_TIME_FLOAT: f32 = 800.0;
25
26impl Delay {
27 pub fn new(
28 id: u32,
29 name: String,
30 is_active: bool,
31 color: String,
32 sample_rate: u32,
33 delay_time: u32,
34 level: f32,
35 ) -> Self {
36 let level_arc = Arc::new(AtomicF32::new(level.clamp(0.0, 0.95)));
37 let delay_time_arc = Arc::new(AtomicU32::new(delay_time.clamp(20, 800)));
38
39 let max_samples = (MAX_DELAY_TIME_FLOAT * sample_rate as f32 / 1000.0) as usize;
40 let delay_buffer = vec![0.0; max_samples + 1];
41
42 let mut instance = Self {
43 id,
44 name,
45 is_active: Arc::new(AtomicBool::new(is_active)),
46 color,
47 delay_time: delay_time_arc,
48 level: level_arc,
49 delay_buffer,
50 write_pos: 0,
51 sample_rate,
52 delay_in_samples: 0,
53 last_feedback_output: 0.0,
54 };
55
56 instance.calc_delay_in_samples();
57 instance
58 }
59
60 fn calc_delay_in_samples(&mut self) {
64 self.delay_in_samples = (self.delay_time.load(Ordering::Relaxed) as f32
65 * self.sample_rate as f32
66 / 1000.0) as usize;
67 }
68
69 pub fn delay_time(&self) -> &Arc<AtomicU32> {
71 &self.delay_time
72 }
73
74 pub fn level(&self) -> &Arc<AtomicF32> {
75 &self.level
76 }
77
78 pub fn delay_buffer(&self) -> &Vec<f32> {
79 &self.delay_buffer
80 }
81
82 pub fn write_pos(&self) -> usize {
83 self.write_pos
84 }
85
86 pub fn sample_rate(&self) -> u32 {
87 self.sample_rate
88 }
89
90 pub fn set_delay_time(&mut self, delay_time: u32) {
95 self.delay_time
96 .store(delay_time.clamp(20, 800), Ordering::Relaxed);
97 self.calc_delay_in_samples()
98 }
99
100 pub fn set_level(&mut self, level: f32) {
102 self.level.store(level.clamp(0.0, 0.95), Ordering::Relaxed);
103 }
104
105 pub fn set_sample_rate(&mut self, sample_rate: u32) {
110 self.sample_rate = sample_rate;
111 let max_samples = (MAX_DELAY_TIME_FLOAT * sample_rate as f32 / 1000.0) as usize;
112 self.delay_buffer.resize(max_samples + 1, 0.0);
113 self.calc_delay_in_samples();
114 }
115}
116
117impl AudioProcessor for Delay {
118 fn process(&mut self, sample: f32) -> f32 {
127 if self.delay_buffer.is_empty() {
128 return sample;
129 }
130
131 let feedback_amount = self.level.load(Ordering::Relaxed);
132
133 let target_delay_samples = self.delay_in_samples as f32;
134 let buf_len = self.delay_buffer.len() as f32;
135 let read_pos = (self.write_pos as f32 - target_delay_samples + buf_len) % buf_len;
136
137 let i_part = read_pos.floor() as usize;
138 let f_part = read_pos - i_part as f32;
139 let next_i = (i_part + 1) % self.delay_buffer.len();
140
141 let delayed_sample =
142 self.delay_buffer[i_part] * (1.0 - f_part) + self.delay_buffer[next_i] * f_part;
143
144 let filtered_feedback = (delayed_sample * 0.5) + (self.last_feedback_output * 0.5);
145 self.last_feedback_output = filtered_feedback;
146
147 self.delay_buffer[self.write_pos] = sample + (filtered_feedback * feedback_amount);
148
149 self.write_pos = (self.write_pos + 1) % self.delay_buffer.len();
150
151 sample + (delayed_sample * feedback_amount)
152 }
153}
154
155impl Effect for Delay {
156 fn id(&self) -> u32 {
157 self.id
158 }
159
160 fn name(&self) -> &str {
161 self.name.as_str()
162 }
163
164 fn get_color(&self) -> String {
165 self.color.clone()
166 }
167
168 fn to_dto(&self) -> EffectDto {
176 EffectDto::Delay(DelayDto {
177 id: self.id(),
178 name: self.name.clone(),
179 is_active: self.is_active(),
180 color: self.color.clone(),
181 delay_time: self.delay_time.load(Ordering::Relaxed),
182 level: self.level.load(Ordering::Relaxed),
183 })
184 }
185
186 fn active_flag(&self) -> Arc<AtomicBool> {
187 self.is_active.clone()
188 }
189
190 fn f32_params(&self) -> HashMap<&'static str, Arc<AtomicF32>> {
191 let mut map = HashMap::new();
192 map.insert("level", Arc::clone(&self.level));
193 map
194 }
195
196 fn u32_params(&self) -> HashMap<&'static str, Arc<AtomicU32>> {
197 let mut map = HashMap::new();
198 map.insert("delay_time", Arc::clone(&self.delay_time));
199 map
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 mod success_path {
208 use super::*;
209
210 #[test]
211 fn test_initialization_and_buffer_size() {
212 let sample_rate = 44100;
213 let delay_time_ms = 100;
214 let delay = Delay::new(
215 1,
216 "Test".to_string(),
217 true,
218 "blue".to_string(),
219 sample_rate,
220 delay_time_ms,
221 0.5,
222 );
223
224 assert!(delay.delay_buffer().len() >= 35280);
226 assert_eq!(delay.sample_rate(), sample_rate);
227 }
228
229 #[test]
230 fn test_signal_passthrough_on_first_sample() {
231 let mut delay = Delay::new(
232 1,
233 "Test".to_string(),
234 true,
235 "blue".to_string(),
236 44100,
237 100,
238 0.5,
239 );
240 let input = 0.8;
241 let output = delay.process(input);
242
243 assert_eq!(output, input);
246 }
247
248 #[test]
249 fn test_delay_echo_occurs() {
250 let sample_rate = 1000; let delay_time_ms = 100; let mut delay = Delay::new(
253 1,
254 "Test".to_string(),
255 true,
256 "blue".to_string(),
257 sample_rate,
258 delay_time_ms,
259 0.5,
260 );
261
262 delay.process(1.0);
264
265 for _ in 0..99 {
267 delay.process(0.0);
268 }
269
270 let echo = delay.process(0.0);
272 assert!(echo > 0.0, "Echo should be audible after delay period");
273 }
274 }
275
276 mod failure_path {
277 use super::*;
278
279 #[test]
280 fn test_parameter_clamping() {
281 let mut delay = Delay::new(
283 1,
284 "Test".to_string(),
285 true,
286 "blue".to_string(),
287 44100,
288 100,
289 2.0,
290 );
291 assert!(delay.level().load(Ordering::Relaxed) <= 0.95);
292
293 delay.set_delay_time(1000);
295 assert_eq!(delay.delay_time().load(Ordering::Relaxed), 800);
296
297 delay.set_delay_time(5);
298 assert_eq!(delay.delay_time().load(Ordering::Relaxed), 20);
299 }
300
301 #[test]
302 fn test_empty_buffer_safety() {
303 let mut delay = Delay::new(
304 1,
305 "Test".to_string(),
306 true,
307 "blue".to_string(),
308 44100,
309 100,
310 0.5,
311 );
312 delay.set_sample_rate(0);
314 let output = delay.process(0.5);
316 assert_eq!(output, 0.5);
317 }
318 }
319}