1use std::f32::consts::PI;
2
3use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
4
5use crate::common::{clamp_cutoff, db_to_gain, input_sample, output_channels, prepare_channels};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum OnePoleMode {
10 LowPass,
12 HighPass,
14}
15
16#[derive(Clone, Copy, Debug, Default, PartialEq)]
17struct OnePoleState {
18 z1: f32,
19}
20
21#[derive(Clone, Debug, PartialEq)]
23pub struct OnePoleFilter {
24 mode: OnePoleMode,
25 cutoff_hz: f32,
26 sample_rate_hz: f32,
27 states: Vec<OnePoleState>,
28}
29
30impl OnePoleFilter {
31 pub fn low_pass(cutoff_hz: f32) -> Self {
33 Self::new(OnePoleMode::LowPass, cutoff_hz)
34 }
35
36 pub fn high_pass(cutoff_hz: f32) -> Self {
38 Self::new(OnePoleMode::HighPass, cutoff_hz)
39 }
40
41 pub fn new(mode: OnePoleMode, cutoff_hz: f32) -> Self {
43 Self {
44 mode,
45 cutoff_hz,
46 sample_rate_hz: 48_000.0,
47 states: Vec::new(),
48 }
49 }
50
51 fn alpha(&self) -> f32 {
52 let cutoff = clamp_cutoff(self.cutoff_hz, self.sample_rate_hz);
53 1.0 - (-2.0 * PI * cutoff / self.sample_rate_hz).exp()
54 }
55}
56
57impl Processor for OnePoleFilter {
58 fn prepare(&mut self, cfg: PrepareConfig) {
59 self.sample_rate_hz = cfg.sample_rate_hz as f32;
60 prepare_channels(
61 &mut self.states,
62 cfg.out_channels as usize,
63 OnePoleState::default(),
64 );
65 }
66
67 fn reset(&mut self) {
68 self.states.fill(OnePoleState::default());
69 }
70
71 fn process(&mut self, block: &mut ProcessBlock<'_>) {
72 let prepared = self.states.len();
75 debug_assert!(
76 output_channels(block) <= prepared,
77 "OnePoleFilter::process received more channels than prepare configured"
78 );
79 let channels = output_channels(block).min(prepared);
80 let alpha = self.alpha();
81 let frames = block.frames as usize;
82 for channel in 0..channels {
83 let state = &mut self.states[channel];
84 for frame in 0..frames {
85 let input = input_sample(block, channel, frame);
86 state.z1 += alpha * (input - state.z1);
87 block.out_audio[channel][frame] = match self.mode {
88 OnePoleMode::LowPass => state.z1,
89 OnePoleMode::HighPass => input - state.z1,
90 };
91 }
92 }
93 }
94}
95
96#[derive(Clone, Copy, Debug, PartialEq)]
98pub enum BiquadKind {
99 LowPass,
101 HighPass,
103 BandPass,
105 Notch,
107 Peaking {
109 gain_db: f32,
111 },
112}
113
114#[derive(Clone, Copy, Debug, PartialEq)]
115struct Coefficients {
116 b0: f32,
117 b1: f32,
118 b2: f32,
119 a1: f32,
120 a2: f32,
121}
122
123impl Default for Coefficients {
124 fn default() -> Self {
125 Self {
126 b0: 1.0,
127 b1: 0.0,
128 b2: 0.0,
129 a1: 0.0,
130 a2: 0.0,
131 }
132 }
133}
134
135#[derive(Clone, Copy, Debug, Default, PartialEq)]
136struct BiquadState {
137 z1: f32,
138 z2: f32,
139}
140
141#[derive(Clone, Debug, PartialEq)]
143pub struct BiquadFilter {
144 kind: BiquadKind,
145 frequency_hz: f32,
146 q: f32,
147 sample_rate_hz: f32,
148 coefficients: Coefficients,
149 states: Vec<BiquadState>,
150}
151
152impl BiquadFilter {
153 pub fn new(kind: BiquadKind, frequency_hz: f32, q: f32) -> Self {
156 let mut filter = Self {
157 kind,
158 frequency_hz,
159 q: q.max(0.05),
160 sample_rate_hz: 48_000.0,
161 coefficients: Coefficients::default(),
162 states: Vec::new(),
163 };
164 filter.update_coefficients();
165 filter
166 }
167
168 pub fn low_pass(frequency_hz: f32, q: f32) -> Self {
170 Self::new(BiquadKind::LowPass, frequency_hz, q)
171 }
172
173 pub fn high_pass(frequency_hz: f32, q: f32) -> Self {
175 Self::new(BiquadKind::HighPass, frequency_hz, q)
176 }
177
178 pub fn band_pass(frequency_hz: f32, q: f32) -> Self {
180 Self::new(BiquadKind::BandPass, frequency_hz, q)
181 }
182
183 pub fn notch(frequency_hz: f32, q: f32) -> Self {
185 Self::new(BiquadKind::Notch, frequency_hz, q)
186 }
187
188 fn update_coefficients(&mut self) {
189 let frequency = clamp_cutoff(self.frequency_hz, self.sample_rate_hz);
190 let omega = 2.0 * PI * frequency / self.sample_rate_hz;
191 let sin = omega.sin();
192 let cos = omega.cos();
193 let alpha = sin / (2.0 * self.q.max(0.05));
194 let (b0, b1, b2, a0, a1, a2) = match self.kind {
195 BiquadKind::LowPass => (
196 (1.0 - cos) * 0.5,
197 1.0 - cos,
198 (1.0 - cos) * 0.5,
199 1.0 + alpha,
200 -2.0 * cos,
201 1.0 - alpha,
202 ),
203 BiquadKind::HighPass => (
204 (1.0 + cos) * 0.5,
205 -(1.0 + cos),
206 (1.0 + cos) * 0.5,
207 1.0 + alpha,
208 -2.0 * cos,
209 1.0 - alpha,
210 ),
211 BiquadKind::BandPass => (alpha, 0.0, -alpha, 1.0 + alpha, -2.0 * cos, 1.0 - alpha),
212 BiquadKind::Notch => (1.0, -2.0 * cos, 1.0, 1.0 + alpha, -2.0 * cos, 1.0 - alpha),
213 BiquadKind::Peaking { gain_db } => {
214 let amp = db_to_gain(gain_db).sqrt();
215 (
216 1.0 + alpha * amp,
217 -2.0 * cos,
218 1.0 - alpha * amp,
219 1.0 + alpha / amp,
220 -2.0 * cos,
221 1.0 - alpha / amp,
222 )
223 }
224 };
225 self.coefficients = Coefficients {
226 b0: b0 / a0,
227 b1: b1 / a0,
228 b2: b2 / a0,
229 a1: a1 / a0,
230 a2: a2 / a0,
231 };
232 }
233}
234
235impl Processor for BiquadFilter {
236 fn prepare(&mut self, cfg: PrepareConfig) {
237 self.sample_rate_hz = cfg.sample_rate_hz as f32;
238 self.update_coefficients();
239 prepare_channels(
240 &mut self.states,
241 cfg.out_channels as usize,
242 BiquadState::default(),
243 );
244 }
245
246 fn reset(&mut self) {
247 self.states.fill(BiquadState::default());
248 }
249
250 fn process(&mut self, block: &mut ProcessBlock<'_>) {
251 let prepared = self.states.len();
254 debug_assert!(
255 output_channels(block) <= prepared,
256 "BiquadFilter::process received more channels than prepare configured"
257 );
258 let channels = output_channels(block).min(prepared);
259 let c = self.coefficients;
260 let frames = block.frames as usize;
261 for channel in 0..channels {
262 let state = &mut self.states[channel];
263 for frame in 0..frames {
264 let input = input_sample(block, channel, frame);
265 let output = c.b0 * input + state.z1;
266 state.z1 = c.b1 * input - c.a1 * output + state.z2;
267 state.z2 = c.b2 * input - c.a2 * output;
268 block.out_audio[channel][frame] = output;
269 }
270 }
271 }
272}
273
274#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub enum StateVariableMode {
277 LowPass,
279 HighPass,
281 BandPass,
283 Notch,
285}
286
287#[derive(Clone, Copy, Debug, Default, PartialEq)]
288struct SvfState {
289 ic1eq: f32,
290 ic2eq: f32,
291}
292
293#[derive(Clone, Debug, PartialEq)]
295pub struct StateVariableFilter {
296 mode: StateVariableMode,
297 frequency_hz: f32,
298 q: f32,
299 sample_rate_hz: f32,
300 states: Vec<SvfState>,
301}
302
303impl StateVariableFilter {
304 pub fn new(mode: StateVariableMode, frequency_hz: f32, q: f32) -> Self {
307 Self {
308 mode,
309 frequency_hz,
310 q: q.max(0.05),
311 sample_rate_hz: 48_000.0,
312 states: Vec::new(),
313 }
314 }
315
316 fn process_sample(&self, state: &mut SvfState, input: f32) -> f32 {
317 let frequency = clamp_cutoff(self.frequency_hz, self.sample_rate_hz);
318 let g = (PI * frequency / self.sample_rate_hz).tan();
319 let k = 1.0 / self.q.max(0.05);
320 let a1 = 1.0 / (1.0 + g * (g + k));
321 let a2 = g * a1;
322 let a3 = g * a2;
323 let v3 = input - state.ic2eq;
324 let v1 = a1 * state.ic1eq + a2 * v3;
325 let v2 = state.ic2eq + a2 * state.ic1eq + a3 * v3;
326 state.ic1eq = 2.0 * v1 - state.ic1eq;
327 state.ic2eq = 2.0 * v2 - state.ic2eq;
328 let low = v2;
329 let high = input - k * v1 - v2;
330 match self.mode {
331 StateVariableMode::LowPass => low,
332 StateVariableMode::HighPass => high,
333 StateVariableMode::BandPass => v1,
334 StateVariableMode::Notch => low + high,
335 }
336 }
337}
338
339impl Processor for StateVariableFilter {
340 fn prepare(&mut self, cfg: PrepareConfig) {
341 self.sample_rate_hz = cfg.sample_rate_hz as f32;
342 prepare_channels(
343 &mut self.states,
344 cfg.out_channels as usize,
345 SvfState::default(),
346 );
347 }
348
349 fn reset(&mut self) {
350 self.states.fill(SvfState::default());
351 }
352
353 fn process(&mut self, block: &mut ProcessBlock<'_>) {
354 let prepared = self.states.len();
357 debug_assert!(
358 output_channels(block) <= prepared,
359 "StateVariableFilter::process received more channels than prepare configured"
360 );
361 let channels = output_channels(block).min(prepared);
362 let frames = block.frames as usize;
363 for channel in 0..channels {
364 for frame in 0..frames {
365 let input = input_sample(block, channel, frame);
366 let mut state = self.states[channel];
367 let output = self.process_sample(&mut state, input);
368 self.states[channel] = state;
369 block.out_audio[channel][frame] = output;
370 }
371 }
372 }
373}