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 channels = output_channels(block);
73 if self.states.len() < channels {
74 self.states.resize(channels, OnePoleState::default());
75 }
76 let alpha = self.alpha();
77 let frames = block.frames as usize;
78 for channel in 0..channels {
79 let state = &mut self.states[channel];
80 for frame in 0..frames {
81 let input = input_sample(block, channel, frame);
82 state.z1 += alpha * (input - state.z1);
83 block.out_audio[channel][frame] = match self.mode {
84 OnePoleMode::LowPass => state.z1,
85 OnePoleMode::HighPass => input - state.z1,
86 };
87 }
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, PartialEq)]
94pub enum BiquadKind {
95 LowPass,
97 HighPass,
99 BandPass,
101 Notch,
103 Peaking {
105 gain_db: f32,
107 },
108}
109
110#[derive(Clone, Copy, Debug, PartialEq)]
111struct Coefficients {
112 b0: f32,
113 b1: f32,
114 b2: f32,
115 a1: f32,
116 a2: f32,
117}
118
119impl Default for Coefficients {
120 fn default() -> Self {
121 Self {
122 b0: 1.0,
123 b1: 0.0,
124 b2: 0.0,
125 a1: 0.0,
126 a2: 0.0,
127 }
128 }
129}
130
131#[derive(Clone, Copy, Debug, Default, PartialEq)]
132struct BiquadState {
133 z1: f32,
134 z2: f32,
135}
136
137#[derive(Clone, Debug, PartialEq)]
139pub struct BiquadFilter {
140 kind: BiquadKind,
141 frequency_hz: f32,
142 q: f32,
143 sample_rate_hz: f32,
144 coefficients: Coefficients,
145 states: Vec<BiquadState>,
146}
147
148impl BiquadFilter {
149 pub fn new(kind: BiquadKind, frequency_hz: f32, q: f32) -> Self {
152 let mut filter = Self {
153 kind,
154 frequency_hz,
155 q: q.max(0.05),
156 sample_rate_hz: 48_000.0,
157 coefficients: Coefficients::default(),
158 states: Vec::new(),
159 };
160 filter.update_coefficients();
161 filter
162 }
163
164 pub fn low_pass(frequency_hz: f32, q: f32) -> Self {
166 Self::new(BiquadKind::LowPass, frequency_hz, q)
167 }
168
169 pub fn high_pass(frequency_hz: f32, q: f32) -> Self {
171 Self::new(BiquadKind::HighPass, frequency_hz, q)
172 }
173
174 pub fn band_pass(frequency_hz: f32, q: f32) -> Self {
176 Self::new(BiquadKind::BandPass, frequency_hz, q)
177 }
178
179 pub fn notch(frequency_hz: f32, q: f32) -> Self {
181 Self::new(BiquadKind::Notch, frequency_hz, q)
182 }
183
184 fn update_coefficients(&mut self) {
185 let frequency = clamp_cutoff(self.frequency_hz, self.sample_rate_hz);
186 let omega = 2.0 * PI * frequency / self.sample_rate_hz;
187 let sin = omega.sin();
188 let cos = omega.cos();
189 let alpha = sin / (2.0 * self.q.max(0.05));
190 let (b0, b1, b2, a0, a1, a2) = match self.kind {
191 BiquadKind::LowPass => (
192 (1.0 - cos) * 0.5,
193 1.0 - cos,
194 (1.0 - cos) * 0.5,
195 1.0 + alpha,
196 -2.0 * cos,
197 1.0 - alpha,
198 ),
199 BiquadKind::HighPass => (
200 (1.0 + cos) * 0.5,
201 -(1.0 + cos),
202 (1.0 + cos) * 0.5,
203 1.0 + alpha,
204 -2.0 * cos,
205 1.0 - alpha,
206 ),
207 BiquadKind::BandPass => (alpha, 0.0, -alpha, 1.0 + alpha, -2.0 * cos, 1.0 - alpha),
208 BiquadKind::Notch => (1.0, -2.0 * cos, 1.0, 1.0 + alpha, -2.0 * cos, 1.0 - alpha),
209 BiquadKind::Peaking { gain_db } => {
210 let amp = db_to_gain(gain_db).sqrt();
211 (
212 1.0 + alpha * amp,
213 -2.0 * cos,
214 1.0 - alpha * amp,
215 1.0 + alpha / amp,
216 -2.0 * cos,
217 1.0 - alpha / amp,
218 )
219 }
220 };
221 self.coefficients = Coefficients {
222 b0: b0 / a0,
223 b1: b1 / a0,
224 b2: b2 / a0,
225 a1: a1 / a0,
226 a2: a2 / a0,
227 };
228 }
229}
230
231impl Processor for BiquadFilter {
232 fn prepare(&mut self, cfg: PrepareConfig) {
233 self.sample_rate_hz = cfg.sample_rate_hz as f32;
234 self.update_coefficients();
235 prepare_channels(
236 &mut self.states,
237 cfg.out_channels as usize,
238 BiquadState::default(),
239 );
240 }
241
242 fn reset(&mut self) {
243 self.states.fill(BiquadState::default());
244 }
245
246 fn process(&mut self, block: &mut ProcessBlock<'_>) {
247 let channels = output_channels(block);
248 if self.states.len() < channels {
249 self.states.resize(channels, BiquadState::default());
250 }
251 let c = self.coefficients;
252 let frames = block.frames as usize;
253 for channel in 0..channels {
254 let state = &mut self.states[channel];
255 for frame in 0..frames {
256 let input = input_sample(block, channel, frame);
257 let output = c.b0 * input + state.z1;
258 state.z1 = c.b1 * input - c.a1 * output + state.z2;
259 state.z2 = c.b2 * input - c.a2 * output;
260 block.out_audio[channel][frame] = output;
261 }
262 }
263 }
264}
265
266#[derive(Clone, Copy, Debug, PartialEq, Eq)]
268pub enum StateVariableMode {
269 LowPass,
271 HighPass,
273 BandPass,
275 Notch,
277}
278
279#[derive(Clone, Copy, Debug, Default, PartialEq)]
280struct SvfState {
281 ic1eq: f32,
282 ic2eq: f32,
283}
284
285#[derive(Clone, Debug, PartialEq)]
287pub struct StateVariableFilter {
288 mode: StateVariableMode,
289 frequency_hz: f32,
290 q: f32,
291 sample_rate_hz: f32,
292 states: Vec<SvfState>,
293}
294
295impl StateVariableFilter {
296 pub fn new(mode: StateVariableMode, frequency_hz: f32, q: f32) -> Self {
299 Self {
300 mode,
301 frequency_hz,
302 q: q.max(0.05),
303 sample_rate_hz: 48_000.0,
304 states: Vec::new(),
305 }
306 }
307
308 fn process_sample(&self, state: &mut SvfState, input: f32) -> f32 {
309 let frequency = clamp_cutoff(self.frequency_hz, self.sample_rate_hz);
310 let g = (PI * frequency / self.sample_rate_hz).tan();
311 let k = 1.0 / self.q.max(0.05);
312 let a1 = 1.0 / (1.0 + g * (g + k));
313 let a2 = g * a1;
314 let a3 = g * a2;
315 let v3 = input - state.ic2eq;
316 let v1 = a1 * state.ic1eq + a2 * v3;
317 let v2 = state.ic2eq + a2 * state.ic1eq + a3 * v3;
318 state.ic1eq = 2.0 * v1 - state.ic1eq;
319 state.ic2eq = 2.0 * v2 - state.ic2eq;
320 let low = v2;
321 let high = input - k * v1 - v2;
322 match self.mode {
323 StateVariableMode::LowPass => low,
324 StateVariableMode::HighPass => high,
325 StateVariableMode::BandPass => v1,
326 StateVariableMode::Notch => low + high,
327 }
328 }
329}
330
331impl Processor for StateVariableFilter {
332 fn prepare(&mut self, cfg: PrepareConfig) {
333 self.sample_rate_hz = cfg.sample_rate_hz as f32;
334 prepare_channels(
335 &mut self.states,
336 cfg.out_channels as usize,
337 SvfState::default(),
338 );
339 }
340
341 fn reset(&mut self) {
342 self.states.fill(SvfState::default());
343 }
344
345 fn process(&mut self, block: &mut ProcessBlock<'_>) {
346 let channels = output_channels(block);
347 if self.states.len() < channels {
348 self.states.resize(channels, SvfState::default());
349 }
350 let frames = block.frames as usize;
351 for channel in 0..channels {
352 for frame in 0..frames {
353 let input = input_sample(block, channel, frame);
354 let mut state = self.states[channel];
355 let output = self.process_sample(&mut state, input);
356 self.states[channel] = state;
357 block.out_audio[channel][frame] = output;
358 }
359 }
360 }
361}