idsp/iir/
repr.rs

1use core::any::Any;
2use miniconf::Tree;
3use num_traits::{AsPrimitive, Float, FloatConst};
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    Coefficient,
8    iir::{Biquad, Pid, Shape},
9};
10
11/// Floating point BA coefficients before quantization
12#[derive(Debug, Clone, Tree)]
13#[tree(meta(doc, typename))]
14pub struct Ba<T> {
15    /// Coefficient array: [[b0, b1, b2], [a0, a1, a2]]
16    #[tree(with=miniconf::leaf, bounds(serialize="T: Serialize", deserialize="T: Deserialize<'de>", any="T: Any"))]
17    pub ba: [[T; 3]; 2],
18    /// Summing junction offset
19    pub u: T,
20    /// Output lower limit
21    pub min: T,
22    /// Output upper limit
23    pub max: T,
24}
25
26impl<T: Float> Default for Ba<T> {
27    fn default() -> Self {
28        Self {
29            ba: [[T::zero(); 3], [T::one(), T::zero(), T::zero()]],
30            u: T::zero(),
31            min: T::neg_infinity(),
32            max: 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)]
63#[tree(meta(doc, typename))]
64pub struct FilterRepr<T> {
65    /// Filter style
66    #[tree(with=miniconf::leaf)]
67    typ: Typ,
68    /// Angular critical frequency (in units of sampling frequency)
69    /// Corner frequency, or 3dB cutoff frequency,
70    frequency: T,
71    /// Passband gain
72    gain: T,
73    /// Shelf gain (only for peaking, lowshelf, highshelf)
74    /// Relative to passband gain
75    shelf: T,
76    /// Q/Bandwidth/Slope
77    #[tree(with=miniconf::leaf, bounds(serialize="T: Serialize", deserialize="T: Deserialize<'de>", any="T: Any"))]
78    shape: Shape<T>,
79    /// Summing junction offset
80    offset: T,
81    /// Lower output limit
82    min: T,
83    /// Upper output limit
84    max: T,
85}
86
87impl<T: Float + FloatConst> Default for FilterRepr<T> {
88    fn default() -> Self {
89        Self {
90            typ: Typ::default(),
91            frequency: T::zero(),
92            gain: T::one(),
93            shelf: T::one(),
94            shape: Shape::default(),
95            offset: T::zero(),
96            min: T::neg_infinity(),
97            max: T::infinity(),
98        }
99    }
100}
101
102/// Representation of Biquad
103///
104/// `miniconf::Tree` can be used like this:
105///
106/// ```
107/// use miniconf::{Tree, str_leaf};
108/// #[derive(Tree)]
109/// struct Foo {
110///     #[tree(typ="&str", with=str_leaf, defer=self.repr)]
111///     typ: (),
112///     repr: idsp::iir::BiquadRepr<f32, f32>,
113/// }
114/// ```
115#[derive(
116    Debug,
117    Clone,
118    Tree,
119    strum::EnumString,
120    strum::AsRefStr,
121    strum::FromRepr,
122    strum::EnumDiscriminants,
123    strum::IntoStaticStr,
124)]
125#[strum_discriminants(derive(serde::Serialize, serde::Deserialize), allow(missing_docs))]
126#[tree(meta(doc = "Representation of Biquad", typename))]
127pub enum BiquadRepr<T, C>
128where
129    C: Coefficient,
130    T: Float + FloatConst,
131{
132    /// Normalized SI unit coefficients
133    Ba(Ba<T>),
134    /// Raw, unscaled, possibly fixed point machine unit coefficients
135    Raw(
136        #[tree(with=miniconf::leaf, bounds(serialize="C: Serialize", deserialize="C: Deserialize<'de>", any="C: Any"))]
137         Biquad<C>,
138    ),
139    /// A PID
140    Pid(Pid<T>),
141    /// Standard biquad filters: Notch, Lowpass, Highpass, Shelf etc
142    Filter(FilterRepr<T>),
143}
144
145impl<T, C> Default for BiquadRepr<T, C>
146where
147    C: Coefficient,
148    T: Float + FloatConst,
149{
150    fn default() -> Self {
151        Self::Ba(Default::default())
152    }
153}
154
155impl<T, C> BiquadRepr<T, C>
156where
157    C: Coefficient + AsPrimitive<C> + AsPrimitive<T>,
158    T: AsPrimitive<C> + Float + FloatConst,
159{
160    /// Build a biquad
161    ///
162    /// # Args:
163    /// * `period`: The sample period in desired units (e.g. SI seconds)
164    /// * `b_scale`: The feed forward (`b` coefficient) conversion scale from
165    ///   desired units to machine units.
166    ///   An identity (`gain=1`) filter a `x` input in machine units
167    ///   will lead to a `y=b_scale*x` filter output in machine units.
168    /// * `y_scale`: The y output scale from desired units to machine units.
169    ///   E.g. a `max` setting will lead to a `y=y_scale*max` upper limit
170    ///   of the filter in machine units.
171    pub fn build<I>(&self, period: T, b_scale: T, y_scale: T) -> Biquad<C>
172    where
173        T: AsPrimitive<I>,
174        I: Float + AsPrimitive<C>,
175        C: AsPrimitive<I>,
176        f32: AsPrimitive<T>,
177    {
178        match self {
179            Self::Ba(ba) => {
180                let mut b = Biquad::from(&[ba.ba[0].map(|b| b * b_scale), ba.ba[1]]);
181                b.set_u((ba.u * y_scale).as_());
182                b.set_min((ba.min * y_scale).as_());
183                b.set_max((ba.max * y_scale).as_());
184                b
185            }
186            Self::Raw(raw) => raw.clone(),
187            Self::Pid(pid) => pid.build::<_, I>(period, b_scale, y_scale),
188            Self::Filter(filter) => {
189                let mut f = crate::iir::Filter::default();
190                f.gain_db(filter.gain);
191                f.critical_frequency(filter.frequency * period);
192                f.shelf_db(filter.shelf);
193                f.set_shape(filter.shape);
194                let mut ba = match filter.typ {
195                    Typ::Lowpass => f.lowpass(),
196                    Typ::Highpass => f.highpass(),
197                    Typ::Allpass => f.allpass(),
198                    Typ::Bandpass => f.bandpass(),
199                    Typ::Highshelf => f.highshelf(),
200                    Typ::Lowshelf => f.lowshelf(),
201                    Typ::IHo => f.iho(),
202                    Typ::Notch => f.notch(),
203                    Typ::Peaking => f.peaking(),
204                };
205                ba[0] = ba[0].map(|b| b * b_scale);
206                let mut b = Biquad::from(&ba);
207                b.set_u((filter.offset * y_scale).as_());
208                b.set_min((filter.min * y_scale).as_());
209                b.set_max((filter.max * y_scale).as_());
210                b
211            }
212        }
213    }
214}