1use std::f32::consts::PI;
2
3pub enum ShelfType {
5 Low,
7 High,
9 Peak,
11}
12
13pub struct Biquad {
21 b0: f32,
23 b1: f32,
25 b2: f32,
27 a1: f32,
29 a2: f32,
31 x1: f32,
33 x2: f32,
35 y1: f32,
37 y2: f32,
39 sample_rate: f32,
41 freq: f32,
43 shelf_type: ShelfType,
45}
46
47impl Biquad {
50 pub fn new_shelf(shelf: ShelfType, sample_rate: f32, freq: f32, gain_db: f32) -> Self {
63 let (b0, b1, b2, _a0, a1, a2) =
64 Self::calculate_coefficients(&shelf, sample_rate, freq, gain_db);
65
66 Self {
67 b0,
68 b1,
69 b2,
70 a1,
71 a2,
72 x1: 0.0,
73 x2: 0.0,
74 y1: 0.0,
75 y2: 0.0,
76 sample_rate,
77 freq,
78 shelf_type: shelf,
79 }
80 }
81
82 pub fn process(&mut self, x: f32) -> f32 {
95 let y = self.b0 * x + self.b1 * self.x1 + self.b2 * self.x2
96 - self.a1 * self.y1
97 - self.a2 * self.y2;
98 self.x2 = self.x1;
99 self.x1 = x;
100 self.y2 = self.y1;
101 self.y1 = y;
102 y
103 }
104
105 pub fn set_gain_db(&mut self, gain_db: f32) {
114 let (b0, b1, b2, _a0, a1, a2) =
115 Self::calculate_coefficients(&self.shelf_type, self.sample_rate, self.freq, gain_db);
116
117 self.b0 = b0;
118 self.b1 = b1;
119 self.b2 = b2;
120 self.a1 = a1;
121 self.a2 = a2;
122 }
129
130 fn calculate_coefficients(
148 shelf: &ShelfType,
149 sample_rate: f32,
150 freq: f32,
151 gain_db: f32,
152 ) -> (f32, f32, f32, f32, f32, f32) {
153 let a = 10.0_f32.powf(gain_db / 40.0);
154 let w0 = 2.0 * PI * freq / sample_rate;
155 let cos = w0.cos();
156 let sin = w0.sin();
157
158 let (b0, b1, b2, a0, a1, a2) = match shelf {
159 ShelfType::Low | ShelfType::High => {
160 let alpha = sin / 16.0 * (2.0 * (a + 1.0 / a)).sqrt();
161 let sqrt_a = a.sqrt();
162 if matches!(shelf, ShelfType::Low) {
163 (
164 a * ((a + 1.0) - (a - 1.0) * cos + 2.0 * sqrt_a * alpha),
165 2.0 * a * ((a - 1.0) - (a + 1.0) * cos),
166 a * ((a + 1.0) - (a - 1.0) * cos - 2.0 * sqrt_a * alpha),
167 (a + 1.0) + (a - 1.0) * cos + 2.0 * sqrt_a * alpha,
168 -2.0 * ((a - 1.0) + (a + 1.0) * cos),
169 (a + 1.0) + (a - 1.0) * cos - 2.0 * sqrt_a * alpha,
170 )
171 } else {
172 (
173 a * ((a + 1.0) + (a - 1.0) * cos + 2.0 * sqrt_a * alpha),
174 -2.0 * a * ((a - 1.0) + (a + 1.0) * cos),
175 a * ((a + 1.0) + (a - 1.0) * cos - 2.0 * sqrt_a * alpha),
176 (a + 1.0) - (a - 1.0) * cos + 2.0 * sqrt_a * alpha,
177 2.0 * ((a - 1.0) - (a + 1.0) * cos),
178 (a + 1.0) - (a - 1.0) * cos - 2.0 * sqrt_a * alpha,
179 )
180 }
181 }
182 ShelfType::Peak => {
183 let alpha = sin / 8.0;
184 (
185 1.0 + alpha * a,
186 -2.0 * cos,
187 1.0 - alpha * a,
188 1.0 + alpha / a,
189 -2.0 * cos,
190 1.0 - alpha / a,
191 )
192 }
193 };
194 (b0 / a0, b1 / a0, b2 / a0, 1.0, a1 / a0, a2 / a0)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[cfg(test)]
204 mod success_path {
205 use super::*;
206
207 #[test]
208 fn test_process_single_sample() {
209 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 0.0);
210 let input = 0.5;
211 let output = biquad.process(input);
212 assert!(!output.is_nan());
213 assert!(!output.is_infinite());
214 }
215
216 #[test]
217 fn test_process_multiple_samples() {
218 let mut biquad = Biquad::new_shelf(ShelfType::Low, 44100.0, 200.0, 6.0);
219 let samples = vec![0.1, 0.2, 0.3, -0.1, -0.2];
220
221 for sample in samples {
222 let output = biquad.process(sample);
223 assert!(!output.is_nan());
224 assert!(!output.is_infinite());
225 assert_eq!(biquad.x1, sample);
227 }
228 }
229
230 #[test]
231 fn test_set_gain_db() {
232 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 0.0);
233 let initial_b0 = biquad.b0;
234
235 biquad.set_gain_db(6.0);
236 assert_ne!(biquad.b0, initial_b0);
237 assert!(!biquad.b0.is_nan());
238 assert!(!biquad.b0.is_infinite());
239 }
240
241 #[test]
242 fn test_gain_zero_db_peak_filter() {
243 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 0.0);
244 let input = 1.0;
245 let output = biquad.process(input);
246 assert!(output.abs() < 1.5);
248 }
249
250 #[test]
251 fn test_positive_gain_modification() {
252 let mut biquad = Biquad::new_shelf(ShelfType::Low, 44100.0, 100.0, 0.0);
253 biquad.set_gain_db(12.0);
254 let input = 1.0;
255
256 let output = biquad.process(input);
257 assert!(!output.is_nan());
258 assert!(!output.is_infinite());
259 assert!(output.abs() > input);
260 }
261
262 #[test]
263 fn test_negative_gain_modification() {
264 let mut biquad = Biquad::new_shelf(ShelfType::High, 44100.0, 8000.0, 0.0);
265 biquad.set_gain_db(-12.0);
266 let input = 1.0;
267
268 let output = biquad.process(input);
269 assert!(!output.is_nan());
270 assert!(!output.is_infinite());
271 assert!(output.abs() < input);
272 }
273
274 #[test]
275 fn test_state_update_after_process() {
276 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 6.0);
277
278 biquad.process(0.5);
279 assert_eq!(biquad.x1, 0.5);
280 assert_eq!(biquad.x2, 0.0);
281
282 biquad.process(0.3);
283 assert_eq!(biquad.x1, 0.3);
284 assert_eq!(biquad.x2, 0.5);
285 }
286
287 #[test]
288 fn test_different_sample_rates() {
289 let sample_rates = vec![22050.0, 44100.0, 48000.0, 96000.0];
290
291 for sr in sample_rates {
292 let biquad = Biquad::new_shelf(ShelfType::Peak, sr, 1000.0, 6.0);
293 assert_eq!(biquad.sample_rate, sr);
294 }
295 }
296
297 #[test]
298 fn test_different_frequencies() {
299 let frequencies = vec![20.0, 100.0, 1000.0, 10000.0, 20000.0];
300
301 for freq in frequencies {
302 let biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, freq, 6.0);
303 assert_eq!(biquad.freq, freq);
304 }
305 }
306 }
307 #[cfg(test)]
308 mod failure_path {
309 use super::*;
310
311 #[test]
312 fn test_extreme_frequency_high() {
313 let biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 22000.0, 6.0);
314 assert!(!biquad.b0.is_nan());
315 assert!(!biquad.b0.is_infinite());
316 }
317
318 #[test]
319 fn test_extreme_frequency_low() {
320 let biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1.0, 6.0);
321 assert!(!biquad.b0.is_nan());
322 assert!(!biquad.b0.is_infinite());
323 }
324
325 #[test]
326 fn test_very_high_gain() {
327 let biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 48.0);
328 assert!(!biquad.b0.is_nan());
329 assert!(!biquad.b0.is_infinite());
330 }
331
332 #[test]
333 fn test_very_negative_gain() {
334 let biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, -48.0);
335 assert!(!biquad.b0.is_nan());
336 assert!(!biquad.b0.is_infinite());
337 }
338
339 #[test]
340 fn test_process_zero_input() {
341 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 6.0);
342 let output = biquad.process(0.0);
343 assert!(!output.is_nan());
344 assert!(!output.is_infinite());
345 }
346
347 #[test]
348 fn test_process_very_small_input() {
349 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 6.0);
350 let output = biquad.process(1e-6);
351 assert!(!output.is_nan());
352 assert!(!output.is_infinite());
353 }
354
355 #[test]
356 fn test_process_large_input() {
357 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 6.0);
358 let output = biquad.process(100.0);
359 assert!(!output.is_nan());
360 assert!(!output.is_infinite());
361 }
362
363 #[test]
364 fn test_all_shelf_types_with_extreme_values() {
365 let shelf_types = vec![ShelfType::Low, ShelfType::High, ShelfType::Peak];
366
367 for shelf_type in shelf_types {
368 let biquad = Biquad::new_shelf(shelf_type, 44100.0, 5000.0, 24.0);
369 assert!(!biquad.b0.is_nan());
370 assert!(!biquad.b1.is_nan());
371 assert!(!biquad.b2.is_nan());
372 assert!(!biquad.a1.is_nan());
373 assert!(!biquad.a2.is_nan());
374 }
375 }
376
377 #[test]
378 fn test_set_gain_with_extreme_values() {
379 let mut biquad = Biquad::new_shelf(ShelfType::Peak, 44100.0, 1000.0, 0.0);
380
381 biquad.set_gain_db(36.0);
382 assert!(!biquad.b0.is_nan());
383
384 biquad.set_gain_db(-36.0);
385 assert!(!biquad.b0.is_nan());
386 }
387
388 #[test]
389 fn test_process_after_multiple_gain_changes() {
390 let mut biquad = Biquad::new_shelf(ShelfType::Low, 44100.0, 100.0, 0.0);
391
392 biquad.set_gain_db(6.0);
393 let out1 = biquad.process(0.1);
394
395 biquad.set_gain_db(-6.0);
396 let out2 = biquad.process(0.1);
397
398 biquad.set_gain_db(12.0);
399 let out3 = biquad.process(0.1);
400
401 assert!(!out1.is_nan() && !out2.is_nan() && !out3.is_nan());
402 }
403
404 #[test]
405 fn test_low_sample_rate() {
406 let biquad = Biquad::new_shelf(ShelfType::Peak, 8000.0, 500.0, 6.0);
407 assert!(!biquad.b0.is_nan());
408 assert!(!biquad.b0.is_infinite());
409 }
410
411 #[test]
412 fn test_high_sample_rate() {
413 let biquad = Biquad::new_shelf(ShelfType::Peak, 192000.0, 50000.0, 6.0);
414 assert!(!biquad.b0.is_nan());
415 assert!(!biquad.b0.is_infinite());
416 }
417 }
418}