1use std::f64::consts::TAU;
4
5use crate::closed_form::Rational;
6use crate::complex::C64;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum Shape {
11 OnePole,
12 Lowpass,
13 Highpass,
14 Bandpass,
15 Notch,
16 Peaking,
17 Lowshelf,
18 Highshelf,
19}
20
21pub const ALL_SHAPES: [Shape; 8] = [
23 Shape::OnePole,
24 Shape::Lowpass,
25 Shape::Highpass,
26 Shape::Bandpass,
27 Shape::Notch,
28 Shape::Peaking,
29 Shape::Lowshelf,
30 Shape::Highshelf,
31];
32
33impl Shape {
34 pub fn default_q(self) -> f64 {
35 match self {
36 Shape::Bandpass | Shape::Notch | Shape::Peaking => 1.0,
37 _ => std::f64::consts::FRAC_1_SQRT_2,
38 }
39 }
40
41 pub fn takes_gain(self) -> bool {
42 matches!(self, Shape::Peaking | Shape::Lowshelf | Shape::Highshelf)
43 }
44}
45
46pub fn design(shape: Shape, cutoff: f64, q: f64, gain_db: f64) -> Rational {
48 let w0 = TAU * cutoff;
49 let a = 10f64.powf(gain_db / 40.0);
50 let root_a = a.sqrt();
51 match shape {
52 Shape::OnePole => Rational {
53 zeros: Vec::new(),
54 poles: vec![C64::real(-w0)],
55 gain: C64::real(w0),
56 },
57 Shape::Lowpass => Rational {
58 zeros: Vec::new(),
59 poles: quadratic(w0 / q, w0 * w0),
60 gain: C64::real(w0 * w0),
61 },
62 Shape::Highpass => Rational {
63 zeros: vec![C64::ZERO, C64::ZERO],
64 poles: quadratic(w0 / q, w0 * w0),
65 gain: C64::ONE,
66 },
67 Shape::Bandpass => Rational {
68 zeros: vec![C64::ZERO],
69 poles: quadratic(w0 / q, w0 * w0),
70 gain: C64::real(w0 / q),
71 },
72 Shape::Notch => Rational {
73 zeros: vec![C64::new(0.0, w0), C64::new(0.0, -w0)],
74 poles: quadratic(w0 / q, w0 * w0),
75 gain: C64::ONE,
76 },
77 Shape::Peaking => Rational {
78 zeros: quadratic(w0 * a / q, w0 * w0),
79 poles: quadratic(w0 / (q * a), w0 * w0),
80 gain: C64::ONE,
81 },
82 Shape::Lowshelf => Rational {
83 zeros: quadratic(root_a * w0 / q, a * w0 * w0),
84 poles: quadratic(root_a * w0 / (q * a), w0 * w0 / a),
85 gain: C64::ONE,
86 },
87 Shape::Highshelf => Rational {
88 zeros: quadratic(root_a * w0 / (q * a), w0 * w0 / a),
89 poles: quadratic(root_a * w0 / q, a * w0 * w0),
90 gain: C64::real(a * a),
91 },
92 }
93}
94
95fn quadratic(b: f64, c: f64) -> Vec<C64> {
97 let discriminant = b * b - 4.0 * c;
98 let half = C64::real(-b / 2.0);
99 let spread = if discriminant >= 0.0 {
100 C64::real(discriminant.sqrt() / 2.0)
101 } else {
102 C64::new(0.0, (-discriminant).sqrt() / 2.0)
103 };
104 vec![half + spread, half - spread]
105}