rill_core_dsp/macros/parameterized.rs
1//! Macro for creating a parameterized algorithm
2//!
3//! # Example
4//! ```
5//! use rill_core_dsp::parameterized_algorithm;
6//! use rill_core::math::Transcendental;
7//!
8//! parameterized_algorithm! {
9//! /// Filter with variable cutoff frequency
10//! #[derive(Debug, Clone, Copy)]
11//! pub struct LowPass<T: Transcendental> {
12//! params: {
13//! /// Cutoff frequency in Hz
14//! cutoff: T = T::from_f32(1000.0),
15//! /// Quality factor
16//! q: T = T::from_f32(0.707),
17//! },
18//! state: {
19//! /// Internal filter state
20//! y1: T = T::ZERO,
21//! y2: T = T::ZERO,
22//! },
23//! update: |this| {
24//! // Update coefficients when parameters change
25//! },
26//! process: |this, input| {
27//! // Process with current parameters
28//! input
29//! }
30//! }
31//! }
32//! ```
33
34/// Macro for creating a parameterized algorithm
35///
36/// # Example
37/// ```
38/// use rill_core_dsp::parameterized_algorithm;
39/// use rill_core::math::Transcendental;
40///
41/// parameterized_algorithm! {
42/// /// Filter with variable cutoff frequency
43/// #[derive(Debug, Clone, Copy)]
44/// pub struct LowPass<T: Transcendental> {
45/// params: {
46/// /// Cutoff frequency in Hz
47/// cutoff: T = T::from_f32(1000.0),
48/// /// Quality factor
49/// q: T = T::from_f32(0.707),
50/// },
51/// state: {
52/// /// Internal filter state
53/// y1: T = T::ZERO,
54/// y2: T = T::ZERO,
55/// },
56/// update: |this| {
57/// // Update coefficients when parameters change
58/// },
59/// process: |this, input| {
60/// // Process with current parameters
61/// input
62/// }
63/// }
64/// }
65/// ```
66#[macro_export]
67macro_rules! parameterized_algorithm {
68 (
69 $(#[$struct_meta:meta])*
70 $vis:vis struct $name:ident<$($generic:ident: $bound:path),+> {
71 params: {
72 $(
73 $(#[$param_meta:meta])*
74 $param_name:ident : $param_type:ty = $param_default:expr
75 ),* $(,)?
76 },
77 state: {
78 $(
79 $(#[$state_meta:meta])*
80 $state_name:ident : $state_type:ty = $state_default:expr
81 ),* $(,)?
82 },
83 update: $update:expr,
84 process: $process:expr
85 }
86 ) => {
87 $(#[$struct_meta])*
88 $vis struct $name<$($generic: $bound),+> {
89 $(
90 $(#[$param_meta])*
91 pub $param_name: $param_type,
92 )*
93
94 $(
95 $(#[$state_meta])*
96 pub $state_name: $state_type,
97 )*
98
99 /// Sample rate
100 pub sample_rate: f32,
101 }
102
103 impl<$($generic: $bound),+> $name<$($generic),+> {
104 /// Create a new algorithm instance
105 pub fn new($($param_name: $param_type),*) -> Self {
106 Self {
107 $($param_name),*,
108 $($state_name: $state_default),*,
109 sample_rate: 44100.0,
110 }
111 }
112
113 /// Update internal coefficients
114 pub fn update_coeffs(&mut self) {
115 let update_fn: fn(&mut Self) = $update;
116 update_fn(self);
117 }
118 }
119
120 impl<$($generic: $bound),+> rill_core::traits::algorithm::Algorithm<T> for $name<$($generic),+>
121 where
122 T: rill_core::math::Transcendental,
123 {
124 fn init(&mut self, sample_rate: f32) {
125 self.sample_rate = sample_rate;
126 self.update_coeffs();
127 }
128
129 fn reset(&mut self) {
130 $(
131 self.$state_name = $state_default;
132 )*
133 }
134
135 fn process(
136 &mut self,
137 input: Option<&[T]>,
138 output: &mut [T],
139 ) -> rill_core::traits::ProcessResult<()> {
140 let input = input.unwrap_or(&[]);
141 let len = input.len().min(output.len());
142 let process_fn: fn(&mut Self, T) -> T = $process;
143 for i in 0..len {
144 output[i] = process_fn(self, input[i]);
145 }
146 Ok(())
147 }
148
149 fn metadata(&self) -> rill_core::traits::algorithm::AlgorithmMetadata {
150 rill_core::traits::algorithm::AlgorithmMetadata {
151 name: stringify!($name),
152 category: rill_core::traits::algorithm::AlgorithmCategory::Utility,
153 description: stringify!($name),
154 author: "Rill",
155 version: env!("CARGO_PKG_VERSION"),
156 }
157 }
158 }
159 };
160}