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 channels = output_channels(block);
115 if self.lines.len() < channels {
116 let max_delay_samples = self.max_delay_samples();
117 self.lines
118 .resize_with(channels, || DelayLine::new(max_delay_samples));
119 }
120 let delay = self.delay_samples();
121 let frames = block.frames as usize;
122 for channel in 0..channels {
123 let line = &mut self.lines[channel];
124 for frame in 0..frames {
125 let input = input_sample(block, channel, frame);
126 let delayed = line.read(delay);
127 line.push(input + delayed * self.feedback);
128 block.out_audio[channel][frame] = input * self.dry + delayed * self.wet;
129 }
130 }
131 }
132
133 fn tail_frames(&self) -> u64 {
134 self.delay_samples().ceil() as u64
135 }
136}
137
138#[derive(Clone, Debug, PartialEq)]
140pub struct FractionalDelay {
141 inner: DelayProcessor,
142}
143
144impl FractionalDelay {
145 pub fn milliseconds(delay_ms: f32, max_delay_ms: f32) -> Self {
147 Self {
148 inner: DelayProcessor::milliseconds(delay_ms, max_delay_ms),
149 }
150 }
151}
152
153impl Processor for FractionalDelay {
154 fn prepare(&mut self, cfg: PrepareConfig) {
155 self.inner.prepare(cfg);
156 }
157
158 fn reset(&mut self) {
159 self.inner.reset();
160 }
161
162 fn process(&mut self, block: &mut ProcessBlock<'_>) {
163 self.inner.process(block);
164 }
165
166 fn tail_frames(&self) -> u64 {
167 self.inner.tail_frames()
168 }
169}
170
171#[derive(Clone, Debug, PartialEq)]
173pub struct CombFilter {
174 delay: DelayProcessor,
175}
176
177impl CombFilter {
178 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
180 Self {
181 delay: DelayProcessor::milliseconds(delay_ms, delay_ms.max(1.0))
182 .with_feedback(feedback)
183 .with_mix(0.0, 1.0),
184 }
185 }
186}
187
188impl Processor for CombFilter {
189 fn prepare(&mut self, cfg: PrepareConfig) {
190 self.delay.prepare(cfg);
191 }
192
193 fn reset(&mut self) {
194 self.delay.reset();
195 }
196
197 fn process(&mut self, block: &mut ProcessBlock<'_>) {
198 self.delay.process(block);
199 }
200
201 fn tail_frames(&self) -> u64 {
202 self.delay.tail_frames()
203 }
204}
205
206#[derive(Clone, Debug, PartialEq)]
208pub struct AllPassFilter {
209 delay_seconds: f32,
210 feedback: f32,
211 sample_rate_hz: f32,
212 lines: Vec<DelayLine>,
213}
214
215impl AllPassFilter {
216 pub fn milliseconds(delay_ms: f32, feedback: f32) -> Self {
218 Self {
219 delay_seconds: (delay_ms / 1000.0).max(0.0),
220 feedback: feedback.clamp(-0.99, 0.99),
221 sample_rate_hz: 48_000.0,
222 lines: Vec::new(),
223 }
224 }
225
226 fn delay_samples(&self) -> f32 {
227 self.delay_seconds * self.sample_rate_hz
228 }
229}
230
231impl Processor for AllPassFilter {
232 fn prepare(&mut self, cfg: PrepareConfig) {
233 self.sample_rate_hz = cfg.sample_rate_hz as f32;
234 let samples = self.delay_samples().ceil() as usize;
235 prepare_channels(
236 &mut self.lines,
237 cfg.out_channels as usize,
238 DelayLine::new(samples),
239 );
240 }
241
242 fn reset(&mut self) {
243 for line in &mut self.lines {
244 line.reset();
245 }
246 }
247
248 fn process(&mut self, block: &mut ProcessBlock<'_>) {
249 let channels = output_channels(block);
250 if self.lines.len() < channels {
251 let delay_samples = self.delay_samples() as usize;
252 self.lines
253 .resize_with(channels, || DelayLine::new(delay_samples));
254 }
255 let delay = self.delay_samples();
256 let frames = block.frames as usize;
257 for channel in 0..channels {
258 let line = &mut self.lines[channel];
259 for frame in 0..frames {
260 let input = input_sample(block, channel, frame);
261 let delayed = line.read(delay);
262 let output = delayed - self.feedback * input;
263 line.push(input + self.feedback * output);
264 block.out_audio[channel][frame] = output;
265 }
266 }
267 }
268
269 fn tail_frames(&self) -> u64 {
270 self.delay_samples().ceil() as u64
271 }
272}