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}