Skip to main content

rill_core_model/macros/
cascade.rs

1/// Generate a cascaded WDF filter from identical sections.
2///
3/// Creates a struct with an array of `count` WDF sections connected in series,
4/// along with parameter and state fields. Provides methods for sample processing,
5/// cutoff/resonance control, and reset.
6#[macro_export]
7macro_rules! wdf_cascade {
8    (
9        name: $name:ident<T>,
10        section: $section:ty,
11        count: $count:expr,
12        params: { $($pname:ident: $ptype:ty),* },
13        state: { $($sname:ident: $stype:ty),* },
14        feedback: |$f_self:ident, $f_in:ident, $f_out:ident| $feedback:tt,
15        update: |$u_self:ident| $update:tt,
16    ) => {
17        /// Cascaded WDF filter made of identical sections.
18        ///
19        /// The `params` fields are filter coefficients; `state` fields hold
20        /// per-sample persistent state (e.g. feedback_prev).
21        #[derive(Debug, Clone)]
22        pub struct $name<T: $crate::Transcendental> {
23            /// Array of WDF sections connected in series.
24            pub poles: [$section; $count],
25            $($pname: $ptype,)*
26            $($sname: $stype,)*
27        }
28
29        impl<T: $crate::Transcendental> $name<T> {
30            /// Create a new cascaded filter from a prototype section and initial parameters.
31            pub fn new(section: $section, $($pname: $ptype),*) -> Self {
32                Self {
33                    poles: [section; $count],
34                    $($pname,)*
35                    $($sname: T::ZERO,)*
36                }
37            }
38
39            /// Recalculate internal coefficients after parameter changes.
40            pub fn update_coeffs(&mut self) { let $u_self = self; $update }
41
42            /// Process one input sample and return the output sample.
43            ///
44            /// The signal passes through each WDF section in sequence.
45            pub fn process_sample(&mut self, input: T) -> T {
46                let a0 = {
47                    let $f_self = &self;
48                    let $f_in = input;
49                    let $f_out = $f_self.feedback_prev;
50                    $feedback
51                };
52                let mut a = a0;
53                a = $crate::WdfElement::process_incident(&mut self.poles[0], a);
54                a = $crate::WdfElement::process_incident(&mut self.poles[1], a);
55                a = $crate::WdfElement::process_incident(&mut self.poles[2], a);
56                a = $crate::WdfElement::process_incident(&mut self.poles[3], a);
57                self.feedback_prev = a;
58                a
59            }
60
61            /// Return the current cutoff frequency.
62            pub fn cutoff(&self) -> T { self.cutoff }
63            /// Set the cutoff frequency, clamped to `[20, sample_rate / 2]`.
64            pub fn set_cutoff(&mut self, cutoff: T) {
65                let half_sr = self.sample_rate / T::from_f32(2.0);
66                self.cutoff = cutoff.clamp(T::from_f32(20.0), half_sr);
67                self.update_coeffs();
68            }
69            /// Return the current resonance factor.
70            pub fn resonance(&self) -> T { self.resonance }
71            /// Set the resonance factor, clamped to `[0, 1]`.
72            pub fn set_resonance(&mut self, resonance: T) {
73                self.resonance = resonance.clamp(T::ZERO, T::ONE);
74            }
75            /// Set the sample rate and recalculate coefficients.
76            pub fn set_sample_rate(&mut self, sample_rate: T) {
77                self.sample_rate = sample_rate;
78                self.update_coeffs();
79            }
80
81            /// Reset all WDF section state and the feedback sample.
82            pub fn reset(&mut self) {
83                for p in &mut self.poles { $crate::WdfElement::reset(p); }
84                self.feedback_prev = T::ZERO;
85            }
86        }
87    };
88}