idsp/iir/
repr.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use miniconf::{Leaf, Tree};
use num_traits::{AsPrimitive, Float, FloatConst};
use serde::{Deserialize, Serialize};

use crate::{
    iir::{Biquad, Pid, Shape},
    Coefficient,
};

/// Floating point BA coefficients before quantization
#[derive(Debug, Clone, Tree)]
pub struct Ba<T> {
    /// Coefficient array: [[b0, b1, b2], [a0, a1, a2]]
    pub ba: Leaf<[[T; 3]; 2]>,
    /// Summing junction offset
    pub u: Leaf<T>,
    /// Output lower limit
    pub min: Leaf<T>,
    /// Output upper limit
    pub max: Leaf<T>,
}

impl<T> Default for Ba<T>
where
    T: Float,
{
    fn default() -> Self {
        Self {
            ba: Leaf([[T::zero(); 3], [T::one(), T::zero(), T::zero()]]),
            u: Leaf(T::zero()),
            min: Leaf(T::neg_infinity()),
            max: Leaf(T::infinity()),
        }
    }
}

/// Filter type
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, PartialEq, PartialOrd)]
pub enum Typ {
    /// A lowpass
    #[default]
    Lowpass,
    /// A highpass
    Highpass,
    /// A bandpass
    Bandpass,
    /// An allpass
    Allpass,
    /// A notch
    Notch,
    /// A peaking filter
    Peaking,
    /// A low shelf
    Lowshelf,
    /// A high shelf
    Highshelf,
    /// Integrator over harmonic oscillator
    IHo,
}

/// Standard biquad parametrizations
#[derive(Clone, Debug, Tree)]
pub struct FilterRepr<T> {
    /// Filter style
    typ: Leaf<Typ>,
    /// Angular critical frequency (in units of sampling frequency)
    /// Corner frequency, or 3dB cutoff frequency,
    frequency: Leaf<T>,
    /// Passband gain
    gain: Leaf<T>,
    /// Shelf gain (only for peaking, lowshelf, highshelf)
    /// Relative to passband gain
    shelf: Leaf<T>,
    /// Q/Bandwidth/Slope
    shape: Leaf<Shape<T>>,
    /// Summing junction offset
    offset: Leaf<T>,
    /// Lower output limit
    min: Leaf<T>,
    /// Upper output limit
    max: Leaf<T>,
}

impl<T: Float + FloatConst> Default for FilterRepr<T> {
    fn default() -> Self {
        Self {
            typ: Leaf(Typ::default()),
            frequency: Leaf(T::zero()),
            gain: Leaf(T::one()),
            shelf: Leaf(T::one()),
            shape: Leaf(Shape::default()),
            offset: Leaf(T::zero()),
            min: Leaf(T::neg_infinity()),
            max: Leaf(T::infinity()),
        }
    }
}

/// Representation of Biquad
#[derive(Debug, Clone, Tree, strum::EnumString, strum::AsRefStr)]
pub enum BiquadRepr<T, C>
where
    C: Coefficient,
    T: Float + FloatConst,
{
    /// Normalized SI unit coefficients
    Ba(Ba<T>),
    /// Raw, unscaled, possibly fixed point machine unit coefficients
    Raw(Leaf<Biquad<C>>),
    /// A PID
    Pid(Pid<T>),
    /// Standard biquad filters: Notch, Lowpass, Highpass, Shelf etc
    Filter(FilterRepr<T>),
}

impl<T, C> Default for BiquadRepr<T, C>
where
    C: Coefficient,
    T: Float + FloatConst,
{
    fn default() -> Self {
        Self::Ba(Default::default())
    }
}

impl<T, C> BiquadRepr<T, C>
where
    C: Coefficient + AsPrimitive<C> + AsPrimitive<T>,
    T: AsPrimitive<C> + Float + FloatConst,
{
    /// Build a biquad
    ///
    /// # Args:
    /// * `period`: The sample period in desired units (e.g. SI seconds)
    /// * `b_scale`: The feed forward (`b` coefficient) conversion scale from
    ///   desired units to machine units.
    ///   An identity (`gain=1`) filter a `x` input in machine units
    ///   will lead to a `y=b_scale*x` filter output in machine units.
    /// * `y_scale`: The y output scale from desired units to machine units.
    ///   E.g. a `max` setting will lead to a `y=y_scale*max` upper limit
    ///   of the filter in machine units.
    pub fn build<I>(&self, period: T, b_scale: T, y_scale: T) -> Biquad<C>
    where
        T: AsPrimitive<I>,
        I: Float + 'static + AsPrimitive<C>,
        C: AsPrimitive<I>,
        f32: AsPrimitive<T>,
    {
        match self {
            Self::Ba(ba) => {
                let mut b = Biquad::from(&[ba.ba[0].map(|b| b * b_scale), ba.ba[1]]);
                b.set_u((*ba.u * y_scale).as_());
                b.set_min((*ba.min * y_scale).as_());
                b.set_max((*ba.max * y_scale).as_());
                b
            }
            Self::Raw(Leaf(raw)) => raw.clone(),
            Self::Pid(pid) => pid.build::<_, I>(period, b_scale, y_scale),
            Self::Filter(filter) => {
                let mut f = crate::iir::Filter::default();
                f.gain_db(*filter.gain);
                f.critical_frequency(*filter.frequency * period);
                f.shelf_db(*filter.shelf);
                f.set_shape(*filter.shape);
                let mut ba = match *filter.typ {
                    Typ::Lowpass => f.lowpass(),
                    Typ::Highpass => f.highpass(),
                    Typ::Allpass => f.allpass(),
                    Typ::Bandpass => f.bandpass(),
                    Typ::Highshelf => f.highshelf(),
                    Typ::Lowshelf => f.lowshelf(),
                    Typ::IHo => f.iho(),
                    Typ::Notch => f.notch(),
                    Typ::Peaking => f.peaking(),
                };
                ba[0] = ba[0].map(|b| b * b_scale);
                let mut b = Biquad::from(&ba);
                b.set_u((*filter.offset * y_scale).as_());
                b.set_min((*filter.min * y_scale).as_());
                b.set_max((*filter.max * y_scale).as_());
                b
            }
        }
    }
}