1use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
2
3use crate::common::{input_sample, prepare_channels, prepared_output_channels};
4
5#[derive(Clone, Debug, PartialEq)]
7pub struct DelayLine {
8 buffer: Vec<f32>,
9 write: usize,
10}
11
12impl DelayLine {
13 pub fn new(max_delay_samples: usize) -> Self {
15 Self {
16 buffer: vec![0.0; max_delay_samples.max(2) + 2],
17 write: 0,
18 }
19 }
20
21 pub fn reset(&mut self) {
23 self.buffer.fill(0.0);
24 self.write = 0;
25 }
26
27 #[cfg(all(test, not(debug_assertions)))]
28 pub(crate) fn allocated_capacity(&self) -> usize {
29 self.buffer.capacity()
30 }
31
32 pub fn read(&self, delay_samples: f32) -> f32 {
35 let len = self.buffer.len();
36 let delay = delay_samples.clamp(0.0, (len - 2) as f32);
37 let read = (self.write as f32 - delay).rem_euclid(len as f32);
38 let i0 = read.floor() as usize % len;
39 let i1 = (i0 + 1) % len;
40 let frac = read - i0 as f32;
41 self.buffer[i0] * (1.0 - frac) + self.buffer[i1] * frac
42 }
43
44 pub fn push(&mut self, sample: f32) {
46 self.buffer[self.write] = sample;
47 self.write = (self.write + 1) % self.buffer.len();
48 }
49}
50
51#[derive(Clone, Debug, PartialEq)]
53pub struct DelayProcessor {
54 delay_seconds: f32,
55 max_delay_seconds: f32,
56 feedback: f32,
57 wet: f32,
58 dry: f32,
59 sample_rate_hz: f32,
60 lines: Vec<DelayLine>,
61}
62
63impl DelayProcessor {
64 pub fn new(delay_seconds: f32, max_delay_seconds: f32) -> Self {
67 Self {
68 delay_seconds: delay_seconds.max(0.0),
69 max_delay_seconds: max_delay_seconds.max(delay_seconds).max(0.001),
70 feedback: 0.0,
71 wet: 1.0,
72 dry: 0.0,
73 sample_rate_hz: 48_000.0,
74 lines: Vec::new(),
75 }
76 }
77
78 pub fn milliseconds(delay_ms: f32, max_delay_ms: f32) -> Self {
80 Self::new(delay_ms / 1000.0, max_delay_ms / 1000.0)
81 }
82
83 pub fn with_mix(mut self, dry: f32, wet: f32) -> Self {
85 self.dry = dry;
86 self.wet = wet;
87 self
88 }
89
90 pub fn with_feedback(mut self, feedback: f32) -> Self {
92 self.feedback = feedback.clamp(-0.99, 0.99);
93 self
94 }
95
96 fn delay_samples(&self) -> f32 {
97 self.delay_seconds * self.sample_rate_hz
98 }
99
100 fn max_delay_samples(&self) -> usize {
101 (self.max_delay_seconds * self.sample_rate_hz).ceil() as usize
102 }
103
104 #[cfg(all(test, not(debug_assertions)))]
105 pub(crate) fn realtime_state_snapshot(&self) -> Vec<usize> {
106 let mut snapshot = Vec::with_capacity(self.lines.len() + 1);
107 snapshot.push(self.lines.capacity());
108 snapshot.extend(self.lines.iter().map(DelayLine::allocated_capacity));
109 snapshot
110 }
111}
112
113impl Processor for DelayProcessor {
114 fn prepare(&mut self, cfg: PrepareConfig) {
115 self.sample_rate_hz = cfg.sample_rate_hz as f32;
116 let line = DelayLine::new(self.max_delay_samples());
117 prepare_channels(&mut self.lines, cfg.out_channels as usize, line);
118 }
119
120 fn reset(&mut self) {
121 for line in &mut self.lines {
122 line.reset();
123 }
124 }
125
126 fn process(&mut self, block: &mut ProcessBlock<'_>) {
127 let channels = prepared_output_channels(block, self.lines.len(), "DelayProcessor");
128 let delay = self.delay_samples();
129 let frames = block.frames as usize;
130 for channel in 0..channels {
131 let line = &mut self.lines[channel];
132 for frame in 0..frames {
133 let input = input_sample(block, channel, frame);
134 let delayed = if delay <= f32::EPSILON {
135 input
136 } else {
137 line.read(delay)
138 };
139 line.push(input + delayed * self.feedback);
140 block.out_audio[channel][frame] = input * self.dry + delayed * self.wet;
141 }
142 }
143 }
144
145 fn tail_frames(&self) -> u64 {
146 self.delay_samples().ceil() as u64
147 }
148}
149
150#[derive(Clone, Debug, PartialEq)]
152pub struct FractionalDelay {
153 inner: DelayProcessor,
154}
155
156impl FractionalDelay {
157 pub fn milliseconds(delay_ms: f32, max_delay_ms: f32) -> Self {
159 Self {
160 inner: DelayProcessor::milliseconds(delay_ms, max_delay_ms),
161 }
162 }
163
164 #[cfg(all(test, not(debug_assertions)))]
165 pub(crate) fn realtime_state_snapshot(&self) -> Vec<usize> {
166 self.inner.realtime_state_snapshot()
167 }
168}
169
170impl Processor for FractionalDelay {
171 fn prepare(&mut self, cfg: PrepareConfig) {
172 self.inner.prepare(cfg);
173 }
174
175 fn reset(&mut self) {
176 self.inner.reset();
177 }
178
179 fn process(&mut self, block: &mut ProcessBlock<'_>) {
180 self.inner.process(block);
181 }
182
183 fn tail_frames(&self) -> u64 {
184 self.inner.tail_frames()
185 }
186}
187
188#[derive(Clone, Debug, PartialEq)]
190pub struct CombFilter {
191 delay: DelayProcessor,
192}
193
194impl CombFilter {
195 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
197 Self {
198 delay: DelayProcessor::milliseconds(delay_ms, delay_ms.max(1.0))
199 .with_feedback(feedback)
200 .with_mix(0.0, 1.0),
201 }
202 }
203
204 #[cfg(all(test, not(debug_assertions)))]
205 pub(crate) fn realtime_state_snapshot(&self) -> Vec<usize> {
206 self.delay.realtime_state_snapshot()
207 }
208}
209
210impl Processor for CombFilter {
211 fn prepare(&mut self, cfg: PrepareConfig) {
212 self.delay.prepare(cfg);
213 }
214
215 fn reset(&mut self) {
216 self.delay.reset();
217 }
218
219 fn process(&mut self, block: &mut ProcessBlock<'_>) {
220 self.delay.process(block);
221 }
222
223 fn tail_frames(&self) -> u64 {
224 self.delay.tail_frames()
225 }
226}
227
228#[derive(Clone, Debug, PartialEq)]
230pub struct AllPassFilter {
231 delay_seconds: f32,
232 feedback: f32,
233 sample_rate_hz: f32,
234 lines: Vec<DelayLine>,
235}
236
237impl AllPassFilter {
238 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
240 Self {
241 delay_seconds: (delay_ms / 1000.0).max(0.0),
242 feedback: feedback.clamp(-0.99, 0.99),
243 sample_rate_hz: 48_000.0,
244 lines: Vec::new(),
245 }
246 }
247
248 fn delay_samples(&self) -> f32 {
249 self.delay_seconds * self.sample_rate_hz
250 }
251
252 #[cfg(all(test, not(debug_assertions)))]
253 pub(crate) fn realtime_state_snapshot(&self) -> Vec<usize> {
254 let mut snapshot = Vec::with_capacity(self.lines.len() + 1);
255 snapshot.push(self.lines.capacity());
256 snapshot.extend(self.lines.iter().map(DelayLine::allocated_capacity));
257 snapshot
258 }
259}
260
261impl Processor for AllPassFilter {
262 fn prepare(&mut self, cfg: PrepareConfig) {
263 self.sample_rate_hz = cfg.sample_rate_hz as f32;
264 let samples = self.delay_samples().ceil() as usize;
265 prepare_channels(
266 &mut self.lines,
267 cfg.out_channels as usize,
268 DelayLine::new(samples),
269 );
270 }
271
272 fn reset(&mut self) {
273 for line in &mut self.lines {
274 line.reset();
275 }
276 }
277
278 fn process(&mut self, block: &mut ProcessBlock<'_>) {
279 let channels = prepared_output_channels(block, self.lines.len(), "AllPassFilter");
280 let delay = self.delay_samples();
281 let frames = block.frames as usize;
282 for channel in 0..channels {
283 let line = &mut self.lines[channel];
284 for frame in 0..frames {
285 let input = input_sample(block, channel, frame);
286 let delayed = line.read(delay);
287 let output = delayed - self.feedback * input;
288 line.push(input + self.feedback * output);
289 block.out_audio[channel][frame] = output;
290 }
291 }
292 }
293
294 fn tail_frames(&self) -> u64 {
295 self.delay_samples().ceil() as u64
296 }
297}