fundsp/
moog.rs

1//! Moog ladder filter.
2
3use super::audionode::*;
4use super::math::*;
5use super::setting::*;
6use super::signal::*;
7use super::*;
8use numeric_array::*;
9
10/// Moog resonant lowpass filter.
11/// The number of inputs is `N`, either `U1` or `U3`.
12/// - Input 0: input signal
13/// - Input 1 (optional): cutoff frequency (Hz)
14/// - Input 2 (optional): Q
15/// - Output 0: filtered signal
16#[derive(Default, Clone)]
17pub struct Moog<F: Real, N: Size<f32>> {
18    _marker: core::marker::PhantomData<N>,
19    q: F,
20    cutoff: F,
21    sample_rate: F,
22    rez: F,
23    p: F,
24    k: F,
25    s0: F,
26    s1: F,
27    s2: F,
28    s3: F,
29    px: F,
30    ps0: F,
31    ps1: F,
32    ps2: F,
33}
34
35impl<F: Real, N: Size<f32>> Moog<F, N> {
36    pub fn new(cutoff: F, q: F) -> Self {
37        let mut node = Self {
38            sample_rate: convert(DEFAULT_SR),
39            ..Self::default()
40        };
41        node.set_cutoff_q(cutoff, q);
42        node
43    }
44
45    /// Set cutoff frequency (in Hz) and Q.
46    /// This has no effect if the filter has cutoff and Q inputs.
47    #[inline]
48    pub fn set_cutoff_q(&mut self, cutoff: F, q: F) {
49        self.cutoff = cutoff;
50        self.q = q;
51        let c = F::new(2) * cutoff / self.sample_rate;
52        self.p = c * (F::from_f64(1.8) - F::from_f64(0.8) * c);
53        self.k = F::new(2) * sin(c * F::PI * F::from_f64(0.5)) - F::one();
54        let t1 = (F::one() - self.p) * F::from_f64(1.386249);
55        let t2 = F::new(12) + t1 * t1;
56        self.rez = q * (t2 + F::new(6) * t1) / (t2 - F::new(6) * t1);
57    }
58}
59
60impl<F: Real, N: Size<f32>> AudioNode for Moog<F, N> {
61    const ID: u64 = 60;
62    type Inputs = N;
63    type Outputs = typenum::U1;
64
65    fn reset(&mut self) {
66        self.s0 = F::zero();
67        self.s1 = F::zero();
68        self.s2 = F::zero();
69        self.s3 = F::zero();
70        self.px = F::zero();
71        self.ps0 = F::zero();
72        self.ps1 = F::zero();
73        self.ps2 = F::zero();
74    }
75
76    fn set_sample_rate(&mut self, sample_rate: f64) {
77        self.sample_rate = convert(sample_rate);
78        self.set_cutoff_q(self.cutoff, self.q);
79    }
80
81    #[inline]
82    fn tick(&mut self, input: &Frame<f32, Self::Inputs>) -> Frame<f32, Self::Outputs> {
83        if N::USIZE > 1 {
84            self.set_cutoff_q(convert(input[1]), convert(input[2]));
85        }
86
87        let x = -self.rez * self.s3 + convert(input[0]);
88
89        self.s0 = (x + self.px) * self.p - self.k * self.s0;
90        self.s1 = (self.s0 + self.ps0) * self.p - self.k * self.s1;
91        self.s2 = (self.s1 + self.ps1) * self.p - self.k * self.s2;
92        self.s3 = tanh((self.s2 + self.ps2) * self.p - self.k * self.s3);
93
94        self.px = x;
95        self.ps0 = self.s0;
96        self.ps1 = self.s1;
97        self.ps2 = self.s2;
98
99        [convert(self.s3)].into()
100    }
101
102    fn set(&mut self, setting: Setting) {
103        match setting.parameter() {
104            Parameter::Center(cutoff) => self.set_cutoff_q(F::from_f32(*cutoff), self.q),
105            Parameter::CenterQ(cutoff, q) => {
106                self.set_cutoff_q(F::from_f32(*cutoff), F::from_f32(*q))
107            }
108            _ => (),
109        }
110    }
111
112    fn route(&mut self, input: &SignalFrame, _frequency: f64) -> SignalFrame {
113        let mut output = SignalFrame::new(self.outputs());
114        output.set(0, input.at(0).distort(0.0));
115        output
116    }
117}