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}