idsp/iir/
repr.rs

1use miniconf::{Leaf, Tree};
2use num_traits::{AsPrimitive, Float, FloatConst};
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    iir::{Biquad, Pid, Shape},
7    Coefficient,
8};
9
10/// Floating point BA coefficients before quantization
11#[derive(Debug, Clone, Tree)]
12pub struct Ba<T> {
13    /// Coefficient array: [[b0, b1, b2], [a0, a1, a2]]
14    pub ba: Leaf<[[T; 3]; 2]>,
15    /// Summing junction offset
16    pub u: Leaf<T>,
17    /// Output lower limit
18    pub min: Leaf<T>,
19    /// Output upper limit
20    pub max: Leaf<T>,
21}
22
23impl<T> Default for Ba<T>
24where
25    T: Float,
26{
27    fn default() -> Self {
28        Self {
29            ba: Leaf([[T::zero(); 3], [T::one(), T::zero(), T::zero()]]),
30            u: Leaf(T::zero()),
31            min: Leaf(T::neg_infinity()),
32            max: Leaf(T::infinity()),
33        }
34    }
35}
36
37/// Filter type
38#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, PartialEq, PartialOrd)]
39pub enum Typ {
40    /// A lowpass
41    #[default]
42    Lowpass,
43    /// A highpass
44    Highpass,
45    /// A bandpass
46    Bandpass,
47    /// An allpass
48    Allpass,
49    /// A notch
50    Notch,
51    /// A peaking filter
52    Peaking,
53    /// A low shelf
54    Lowshelf,
55    /// A high shelf
56    Highshelf,
57    /// Integrator over harmonic oscillator
58    IHo,
59}
60
61/// Standard biquad parametrizations
62#[derive(Clone, Debug, Tree)]
63pub struct FilterRepr<T> {
64    /// Filter style
65    typ: Leaf<Typ>,
66    /// Angular critical frequency (in units of sampling frequency)
67    /// Corner frequency, or 3dB cutoff frequency,
68    frequency: Leaf<T>,
69    /// Passband gain
70    gain: Leaf<T>,
71    /// Shelf gain (only for peaking, lowshelf, highshelf)
72    /// Relative to passband gain
73    shelf: Leaf<T>,
74    /// Q/Bandwidth/Slope
75    shape: Leaf<Shape<T>>,
76    /// Summing junction offset
77    offset: Leaf<T>,
78    /// Lower output limit
79    min: Leaf<T>,
80    /// Upper output limit
81    max: Leaf<T>,
82}
83
84impl<T: Float + FloatConst> Default for FilterRepr<T> {
85    fn default() -> Self {
86        Self {
87            typ: Leaf(Typ::default()),
88            frequency: Leaf(T::zero()),
89            gain: Leaf(T::one()),
90            shelf: Leaf(T::one()),
91            shape: Leaf(Shape::default()),
92            offset: Leaf(T::zero()),
93            min: Leaf(T::neg_infinity()),
94            max: Leaf(T::infinity()),
95        }
96    }
97}
98
99/// Representation of Biquad
100#[derive(
101    Debug,
102    Clone,
103    Tree,
104    strum::EnumString,
105    strum::AsRefStr,
106    strum::FromRepr,
107    strum::EnumDiscriminants,
108)]
109#[strum_discriminants(derive(serde::Serialize, serde::Deserialize))]
110pub enum BiquadRepr<T, C>
111where
112    C: Coefficient,
113    T: Float + FloatConst,
114{
115    /// Normalized SI unit coefficients
116    Ba(Ba<T>),
117    /// Raw, unscaled, possibly fixed point machine unit coefficients
118    Raw(Leaf<Biquad<C>>),
119    /// A PID
120    Pid(Pid<T>),
121    /// Standard biquad filters: Notch, Lowpass, Highpass, Shelf etc
122    Filter(FilterRepr<T>),
123}
124
125impl<T, C> BiquadRepr<T, C>
126where
127    C: Coefficient,
128    T: Float + FloatConst,
129{
130    /// `TreeSerialize` for the discriminant
131    ///
132    /// Use this through a leaf node:
133    ///
134    /// ```ignore
135    /// #[tree(typ="Leaf<iir::BiquadReprDiscriminants>", rename="typ",
136    ///     with(serialize=self.repr.tag_serialize, deserialize=self.repr.tag_deserialize),
137    ///     deny(ref_any="deny", mut_any="deny"))]
138    /// _tag: (),
139    /// repr: iir::BiquadRepr<f32, f32>,
140    /// ```
141    pub fn tag_serialize<K: miniconf::Keys, S: serde::Serializer>(
142        &self,
143        keys: K,
144        ser: S,
145    ) -> Result<S::Ok, miniconf::Error<S::Error>> {
146        miniconf::TreeSerialize::serialize_by_key(
147            &Leaf(BiquadReprDiscriminants::from(self)),
148            keys,
149            ser,
150        )
151    }
152
153    /// `TreeDeserialize` for the discriminant
154    pub fn tag_deserialize<'de, K: miniconf::Keys, D: serde::Deserializer<'de>>(
155        &mut self,
156        keys: K,
157        de: D,
158    ) -> Result<(), miniconf::Error<D::Error>> {
159        let mut v = Leaf(BiquadReprDiscriminants::from(&*self));
160        miniconf::TreeDeserialize::deserialize_by_key(&mut v, keys, de)?;
161        *self = BiquadRepr::from_repr(*v as _).unwrap();
162        Ok(())
163    }
164}
165
166impl<T, C> Default for BiquadRepr<T, C>
167where
168    C: Coefficient,
169    T: Float + FloatConst,
170{
171    fn default() -> Self {
172        Self::Ba(Default::default())
173    }
174}
175
176impl<T, C> BiquadRepr<T, C>
177where
178    C: Coefficient + AsPrimitive<C> + AsPrimitive<T>,
179    T: AsPrimitive<C> + Float + FloatConst,
180{
181    /// Build a biquad
182    ///
183    /// # Args:
184    /// * `period`: The sample period in desired units (e.g. SI seconds)
185    /// * `b_scale`: The feed forward (`b` coefficient) conversion scale from
186    ///   desired units to machine units.
187    ///   An identity (`gain=1`) filter a `x` input in machine units
188    ///   will lead to a `y=b_scale*x` filter output in machine units.
189    /// * `y_scale`: The y output scale from desired units to machine units.
190    ///   E.g. a `max` setting will lead to a `y=y_scale*max` upper limit
191    ///   of the filter in machine units.
192    pub fn build<I>(&self, period: T, b_scale: T, y_scale: T) -> Biquad<C>
193    where
194        T: AsPrimitive<I>,
195        I: Float + 'static + AsPrimitive<C>,
196        C: AsPrimitive<I>,
197        f32: AsPrimitive<T>,
198    {
199        match self {
200            Self::Ba(ba) => {
201                let mut b = Biquad::from(&[ba.ba[0].map(|b| b * b_scale), ba.ba[1]]);
202                b.set_u((*ba.u * y_scale).as_());
203                b.set_min((*ba.min * y_scale).as_());
204                b.set_max((*ba.max * y_scale).as_());
205                b
206            }
207            Self::Raw(Leaf(raw)) => raw.clone(),
208            Self::Pid(pid) => pid.build::<_, I>(period, b_scale, y_scale),
209            Self::Filter(filter) => {
210                let mut f = crate::iir::Filter::default();
211                f.gain_db(*filter.gain);
212                f.critical_frequency(*filter.frequency * period);
213                f.shelf_db(*filter.shelf);
214                f.set_shape(*filter.shape);
215                let mut ba = match *filter.typ {
216                    Typ::Lowpass => f.lowpass(),
217                    Typ::Highpass => f.highpass(),
218                    Typ::Allpass => f.allpass(),
219                    Typ::Bandpass => f.bandpass(),
220                    Typ::Highshelf => f.highshelf(),
221                    Typ::Lowshelf => f.lowshelf(),
222                    Typ::IHo => f.iho(),
223                    Typ::Notch => f.notch(),
224                    Typ::Peaking => f.peaking(),
225                };
226                ba[0] = ba[0].map(|b| b * b_scale);
227                let mut b = Biquad::from(&ba);
228                b.set_u((*filter.offset * y_scale).as_());
229                b.set_min((*filter.min * y_scale).as_());
230                b.set_max((*filter.max * y_scale).as_());
231                b
232            }
233        }
234    }
235}