Skip to main content

rill_core_dsp/macros/
effect.rs

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