sim_lib_audio_dsp/
delay.rs1use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
2
3use crate::common::{input_sample, output_channels, prepare_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 pub fn read(&self, delay_samples: f32) -> f32 {
30 let len = self.buffer.len();
31 let delay = delay_samples.clamp(0.0, (len - 2) as f32);
32 let read = (self.write as f32 - delay).rem_euclid(len as f32);
33 let i0 = read.floor() as usize % len;
34 let i1 = (i0 + 1) % len;
35 let frac = read - i0 as f32;
36 self.buffer[i0] * (1.0 - frac) + self.buffer[i1] * frac
37 }
38
39 pub fn push(&mut self, sample: f32) {
41 self.buffer[self.write] = sample;
42 self.write = (self.write + 1) % self.buffer.len();
43 }
44}
45
46#[derive(Clone, Debug, PartialEq)]
48pub struct DelayProcessor {
49 delay_seconds: f32,
50 max_delay_seconds: f32,
51 feedback: f32,
52 wet: f32,
53 dry: f32,
54 sample_rate_hz: f32,
55 lines: Vec<DelayLine>,
56}
57
58impl DelayProcessor {
59 pub fn new(delay_seconds: f32, max_delay_seconds: f32) -> Self {
62 Self {
63 delay_seconds: delay_seconds.max(0.0),
64 max_delay_seconds: max_delay_seconds.max(delay_seconds).max(0.001),
65 feedback: 0.0,
66 wet: 1.0,
67 dry: 0.0,
68 sample_rate_hz: 48_000.0,
69 lines: Vec::new(),
70 }
71 }
72
73 pub fn milliseconds(delay_ms: f32, max_delay_ms: f32) -> Self {
75 Self::new(delay_ms / 1000.0, max_delay_ms / 1000.0)
76 }
77
78 pub fn with_mix(mut self, dry: f32, wet: f32) -> Self {
80 self.dry = dry;
81 self.wet = wet;
82 self
83 }
84
85 pub fn with_feedback(mut self, feedback: f32) -> Self {
87 self.feedback = feedback.clamp(-0.99, 0.99);
88 self
89 }
90
91 fn delay_samples(&self) -> f32 {
92 self.delay_seconds * self.sample_rate_hz
93 }
94
95 fn max_delay_samples(&self) -> usize {
96 (self.max_delay_seconds * self.sample_rate_hz).ceil() as usize
97 }
98}
99
100impl Processor for DelayProcessor {
101 fn prepare(&mut self, cfg: PrepareConfig) {
102 self.sample_rate_hz = cfg.sample_rate_hz as f32;
103 let line = DelayLine::new(self.max_delay_samples());
104 prepare_channels(&mut self.lines, cfg.out_channels as usize, line);
105 }
106
107 fn reset(&mut self) {
108 for line in &mut self.lines {
109 line.reset();
110 }
111 }
112
113 fn process(&mut self, block: &mut ProcessBlock<'_>) {
114 let prepared = self.lines.len();
117 debug_assert!(
118 output_channels(block) <= prepared,
119 "DelayProcessor::process received more channels than prepare configured"
120 );
121 let channels = output_channels(block).min(prepared);
122 let delay = self.delay_samples();
123 let frames = block.frames as usize;
124 for channel in 0..channels {
125 let line = &mut self.lines[channel];
126 for frame in 0..frames {
127 let input = input_sample(block, channel, frame);
128 let delayed = line.read(delay);
129 line.push(input + delayed * self.feedback);
130 block.out_audio[channel][frame] = input * self.dry + delayed * self.wet;
131 }
132 }
133 }
134
135 fn tail_frames(&self) -> u64 {
136 self.delay_samples().ceil() as u64
137 }
138}
139
140#[derive(Clone, Debug, PartialEq)]
142pub struct FractionalDelay {
143 inner: DelayProcessor,
144}
145
146impl FractionalDelay {
147 pub fn milliseconds(delay_ms: f32, max_delay_ms: f32) -> Self {
149 Self {
150 inner: DelayProcessor::milliseconds(delay_ms, max_delay_ms),
151 }
152 }
153}
154
155impl Processor for FractionalDelay {
156 fn prepare(&mut self, cfg: PrepareConfig) {
157 self.inner.prepare(cfg);
158 }
159
160 fn reset(&mut self) {
161 self.inner.reset();
162 }
163
164 fn process(&mut self, block: &mut ProcessBlock<'_>) {
165 self.inner.process(block);
166 }
167
168 fn tail_frames(&self) -> u64 {
169 self.inner.tail_frames()
170 }
171}
172
173#[derive(Clone, Debug, PartialEq)]
175pub struct CombFilter {
176 delay: DelayProcessor,
177}
178
179impl CombFilter {
180 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
182 Self {
183 delay: DelayProcessor::milliseconds(delay_ms, delay_ms.max(1.0))
184 .with_feedback(feedback)
185 .with_mix(0.0, 1.0),
186 }
187 }
188}
189
190impl Processor for CombFilter {
191 fn prepare(&mut self, cfg: PrepareConfig) {
192 self.delay.prepare(cfg);
193 }
194
195 fn reset(&mut self) {
196 self.delay.reset();
197 }
198
199 fn process(&mut self, block: &mut ProcessBlock<'_>) {
200 self.delay.process(block);
201 }
202
203 fn tail_frames(&self) -> u64 {
204 self.delay.tail_frames()
205 }
206}
207
208#[derive(Clone, Debug, PartialEq)]
210pub struct AllPassFilter {
211 delay_seconds: f32,
212 feedback: f32,
213 sample_rate_hz: f32,
214 lines: Vec<DelayLine>,
215}
216
217impl AllPassFilter {
218 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
220 Self {
221 delay_seconds: (delay_ms / 1000.0).max(0.0),
222 feedback: feedback.clamp(-0.99, 0.99),
223 sample_rate_hz: 48_000.0,
224 lines: Vec::new(),
225 }
226 }
227
228 fn delay_samples(&self) -> f32 {
229 self.delay_seconds * self.sample_rate_hz
230 }
231}
232
233impl Processor for AllPassFilter {
234 fn prepare(&mut self, cfg: PrepareConfig) {
235 self.sample_rate_hz = cfg.sample_rate_hz as f32;
236 let samples = self.delay_samples().ceil() as usize;
237 prepare_channels(
238 &mut self.lines,
239 cfg.out_channels as usize,
240 DelayLine::new(samples),
241 );
242 }
243
244 fn reset(&mut self) {
245 for line in &mut self.lines {
246 line.reset();
247 }
248 }
249
250 fn process(&mut self, block: &mut ProcessBlock<'_>) {
251 let prepared = self.lines.len();
254 debug_assert!(
255 output_channels(block) <= prepared,
256 "AllPassFilter::process received more channels than prepare configured"
257 );
258 let channels = output_channels(block).min(prepared);
259 let delay = self.delay_samples();
260 let frames = block.frames as usize;
261 for channel in 0..channels {
262 let line = &mut self.lines[channel];
263 for frame in 0..frames {
264 let input = input_sample(block, channel, frame);
265 let delayed = line.read(delay);
266 let output = delayed - self.feedback * input;
267 line.push(input + self.feedback * output);
268 block.out_audio[channel][frame] = output;
269 }
270 }
271 }
272
273 fn tail_frames(&self) -> u64 {
274 self.delay_samples().ceil() as u64
275 }
276}