wavecraft_dsp/combinators/
chain.rs1use crate::traits::{ParamSpec, Processor, ProcessorParams, Transport};
4
5pub struct Chain<A, B> {
10 pub first: A,
11 pub second: B,
12}
13
14impl<A, B> Default for Chain<A, B>
15where
16 A: Default,
17 B: Default,
18{
19 fn default() -> Self {
20 Self {
21 first: A::default(),
22 second: B::default(),
23 }
24 }
25}
26
27pub struct ChainParams<PA, PB> {
31 pub first: PA,
32 pub second: PB,
33}
34
35impl<PA, PB> Default for ChainParams<PA, PB>
36where
37 PA: Default,
38 PB: Default,
39{
40 fn default() -> Self {
41 Self {
42 first: PA::default(),
43 second: PB::default(),
44 }
45 }
46}
47
48impl<PA, PB> ProcessorParams for ChainParams<PA, PB>
49where
50 PA: ProcessorParams,
51 PB: ProcessorParams,
52{
53 fn param_specs() -> &'static [ParamSpec] {
54 let first_specs = PA::param_specs();
71 let second_specs = PB::param_specs();
72
73 let mut merged = Vec::with_capacity(first_specs.len() + second_specs.len());
74
75 for spec in first_specs {
77 merged.push(ParamSpec {
78 name: spec.name,
79 id_suffix: spec.id_suffix,
80 range: spec.range.clone(),
81 default: spec.default,
82 unit: spec.unit,
83 group: spec.group,
84 });
85 }
86
87 for spec in second_specs {
89 merged.push(ParamSpec {
90 name: spec.name,
91 id_suffix: spec.id_suffix,
92 range: spec.range.clone(),
93 default: spec.default,
94 unit: spec.unit,
95 group: spec.group,
96 });
97 }
98
99 Box::leak(merged.into_boxed_slice())
101 }
102
103 fn from_param_defaults() -> Self {
104 Self {
105 first: PA::from_param_defaults(),
106 second: PB::from_param_defaults(),
107 }
108 }
109
110 fn apply_plain_values(&mut self, values: &[f32]) {
111 let first_count = PA::param_specs().len();
112 let split_at = first_count.min(values.len());
113 let (first_values, second_values) = values.split_at(split_at);
114
115 self.first.apply_plain_values(first_values);
116 self.second.apply_plain_values(second_values);
117 }
118}
119
120impl<A, B> Processor for Chain<A, B>
121where
122 A: Processor,
123 B: Processor,
124{
125 type Params = ChainParams<A::Params, B::Params>;
126
127 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
128 self.first.process(buffer, transport, ¶ms.first);
130 self.second.process(buffer, transport, ¶ms.second);
131 }
132
133 fn set_sample_rate(&mut self, sample_rate: f32) {
134 self.first.set_sample_rate(sample_rate);
135 self.second.set_sample_rate(sample_rate);
136 }
137
138 fn reset(&mut self) {
139 self.first.reset();
140 self.second.reset();
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::builtins::{GainDsp, GainParams, PassthroughDsp, PassthroughParams};
148 use std::sync::{
149 Arc,
150 atomic::{AtomicBool, AtomicU32, Ordering},
151 };
152
153 #[derive(Clone)]
154 struct TestParams;
155
156 impl Default for TestParams {
157 fn default() -> Self {
158 Self
159 }
160 }
161
162 impl ProcessorParams for TestParams {
163 fn param_specs() -> &'static [ParamSpec] {
164 &[]
165 }
166 }
167
168 struct LifecycleProbe {
169 set_sample_rate_calls: Arc<AtomicU32>,
170 reset_calls: Arc<AtomicU32>,
171 last_sample_rate_bits: Arc<AtomicU32>,
172 touched_process: Arc<AtomicBool>,
173 }
174
175 impl LifecycleProbe {
176 fn new() -> Self {
177 Self {
178 set_sample_rate_calls: Arc::new(AtomicU32::new(0)),
179 reset_calls: Arc::new(AtomicU32::new(0)),
180 last_sample_rate_bits: Arc::new(AtomicU32::new(0)),
181 touched_process: Arc::new(AtomicBool::new(false)),
182 }
183 }
184 }
185
186 impl Processor for LifecycleProbe {
187 type Params = TestParams;
188
189 fn process(
190 &mut self,
191 _buffer: &mut [&mut [f32]],
192 _transport: &Transport,
193 _params: &Self::Params,
194 ) {
195 self.touched_process.store(true, Ordering::SeqCst);
196 }
197
198 fn set_sample_rate(&mut self, sample_rate: f32) {
199 self.set_sample_rate_calls.fetch_add(1, Ordering::SeqCst);
200 self.last_sample_rate_bits
201 .store(sample_rate.to_bits(), Ordering::SeqCst);
202 }
203
204 fn reset(&mut self) {
205 self.reset_calls.fetch_add(1, Ordering::SeqCst);
206 }
207 }
208
209 #[test]
210 fn test_chain_processes_in_order() {
211 let mut chain = Chain {
212 first: GainDsp::default(),
213 second: GainDsp::default(),
214 };
215
216 let mut left = [1.0_f32, 1.0_f32];
217 let mut right = [1.0_f32, 1.0_f32];
218 let mut buffer = [&mut left[..], &mut right[..]];
219
220 let transport = Transport::default();
221 let params = ChainParams {
222 first: GainParams { level: 0.5 },
223 second: GainParams { level: 2.0 },
224 };
225
226 chain.process(&mut buffer, &transport, ¶ms);
227
228 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
230 assert!((buffer[1][0] - 1.0_f32).abs() < 1e-6);
231 }
232
233 #[test]
234 fn test_chain_with_passthrough() {
235 let mut chain = Chain {
236 first: PassthroughDsp,
237 second: GainDsp::default(),
238 };
239
240 let mut left = [2.0_f32, 2.0_f32];
241 let mut right = [2.0_f32, 2.0_f32];
242 let mut buffer = [&mut left[..], &mut right[..]];
243
244 let transport = Transport::default();
245 let params = ChainParams {
246 first: PassthroughParams,
247 second: GainParams { level: 0.5 },
248 };
249
250 chain.process(&mut buffer, &transport, ¶ms);
251
252 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
254 }
255
256 #[test]
257 fn test_chain_params_merge() {
258 let specs = <ChainParams<GainParams, GainParams>>::param_specs();
259 assert_eq!(specs.len(), 2); assert_eq!(specs[0].name, "Level");
263 assert_eq!(specs[1].name, "Level");
264 }
265
266 #[test]
267 fn test_chain_default() {
268 let _chain: Chain<GainDsp, PassthroughDsp> = Chain::default();
269 let _params: ChainParams<GainParams, PassthroughParams> = ChainParams::default();
270 }
271
272 #[test]
273 fn test_chain_from_param_defaults_uses_children_spec_defaults() {
274 let defaults = <ChainParams<GainParams, GainParams>>::from_param_defaults();
275 assert!((defaults.first.level - 1.0).abs() < 1e-6);
276 assert!((defaults.second.level - 1.0).abs() < 1e-6);
277 }
278
279 #[test]
280 fn test_chain_apply_plain_values_splits_by_child_param_count() {
281 let mut params = <ChainParams<GainParams, GainParams>>::from_param_defaults();
282 params.apply_plain_values(&[0.25, 1.75]);
283
284 assert!((params.first.level - 0.25).abs() < 1e-6);
285 assert!((params.second.level - 1.75).abs() < 1e-6);
286 }
287
288 #[test]
289 fn test_chain_propagates_set_sample_rate_to_both_processors() {
290 let first = LifecycleProbe::new();
291 let second = LifecycleProbe::new();
292
293 let first_calls = Arc::clone(&first.set_sample_rate_calls);
294 let second_calls = Arc::clone(&second.set_sample_rate_calls);
295 let first_sr = Arc::clone(&first.last_sample_rate_bits);
296 let second_sr = Arc::clone(&second.last_sample_rate_bits);
297
298 let mut chain = Chain { first, second };
299 chain.set_sample_rate(48_000.0);
300
301 assert_eq!(first_calls.load(Ordering::SeqCst), 1);
302 assert_eq!(second_calls.load(Ordering::SeqCst), 1);
303 assert_eq!(f32::from_bits(first_sr.load(Ordering::SeqCst)), 48_000.0);
304 assert_eq!(f32::from_bits(second_sr.load(Ordering::SeqCst)), 48_000.0);
305 }
306
307 #[test]
308 fn test_chain_propagates_reset_to_both_processors() {
309 let first = LifecycleProbe::new();
310 let second = LifecycleProbe::new();
311
312 let first_resets = Arc::clone(&first.reset_calls);
313 let second_resets = Arc::clone(&second.reset_calls);
314
315 let mut chain = Chain { first, second };
316 chain.reset();
317
318 assert_eq!(first_resets.load(Ordering::SeqCst), 1);
319 assert_eq!(second_resets.load(Ordering::SeqCst), 1);
320 }
321}