idsp/iir/
repr.rs

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