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 fn extend_specs(target: &mut Vec<ParamSpec>, source: &[ParamSpec]) {
55 for spec in source {
56 target.push(ParamSpec {
57 name: spec.name,
58 id_suffix: spec.id_suffix,
59 range: spec.range.clone(),
60 default: spec.default,
61 unit: spec.unit,
62 group: spec.group,
63 });
64 }
65 }
66
67 let first_specs = PA::param_specs();
84 let second_specs = PB::param_specs();
85
86 let mut merged = Vec::with_capacity(first_specs.len() + second_specs.len());
87
88 extend_specs(&mut merged, first_specs);
89 extend_specs(&mut merged, second_specs);
90
91 Box::leak(merged.into_boxed_slice())
93 }
94
95 fn from_param_defaults() -> Self {
96 Self {
97 first: PA::from_param_defaults(),
98 second: PB::from_param_defaults(),
99 }
100 }
101
102 fn apply_plain_values(&mut self, values: &[f32]) {
103 let first_count = PA::param_specs().len();
104 let split_at = first_count.min(values.len());
105 let (first_values, second_values) = values.split_at(split_at);
106
107 self.first.apply_plain_values(first_values);
108 self.second.apply_plain_values(second_values);
109 }
110}
111
112impl<A, B> Processor for Chain<A, B>
113where
114 A: Processor,
115 B: Processor,
116{
117 type Params = ChainParams<A::Params, B::Params>;
118
119 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
120 self.first.process(buffer, transport, ¶ms.first);
122 self.second.process(buffer, transport, ¶ms.second);
123 }
124
125 fn set_sample_rate(&mut self, sample_rate: f32) {
126 self.first.set_sample_rate(sample_rate);
127 self.second.set_sample_rate(sample_rate);
128 }
129
130 fn reset(&mut self) {
131 self.first.reset();
132 self.second.reset();
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use std::sync::{
140 Arc,
141 atomic::{AtomicBool, AtomicU32, Ordering},
142 };
143
144 #[derive(Clone)]
145 struct TestGainParams {
146 level: f32,
147 }
148
149 impl Default for TestGainParams {
150 fn default() -> Self {
151 Self { level: 1.0 }
152 }
153 }
154
155 impl ProcessorParams for TestGainParams {
156 fn param_specs() -> &'static [ParamSpec] {
157 static SPECS: [ParamSpec; 1] = [ParamSpec {
158 name: "Level",
159 id_suffix: "level",
160 range: crate::ParamRange::Linear { min: 0.0, max: 2.0 },
161 default: 1.0,
162 unit: "x",
163 group: None,
164 }];
165 &SPECS
166 }
167
168 fn from_param_defaults() -> Self {
169 Self { level: 1.0 }
170 }
171
172 fn apply_plain_values(&mut self, values: &[f32]) {
173 if let Some(level) = values.first() {
174 self.level = *level;
175 }
176 }
177 }
178
179 #[derive(Default)]
180 struct TestGainDsp;
181
182 impl Processor for TestGainDsp {
183 type Params = TestGainParams;
184
185 fn process(
186 &mut self,
187 buffer: &mut [&mut [f32]],
188 _transport: &Transport,
189 params: &Self::Params,
190 ) {
191 for channel in buffer.iter_mut() {
192 for sample in channel.iter_mut() {
193 *sample *= params.level;
194 }
195 }
196 }
197 }
198
199 #[derive(Clone, Default)]
200 struct TestPassthroughParams;
201
202 impl ProcessorParams for TestPassthroughParams {
203 fn param_specs() -> &'static [ParamSpec] {
204 &[]
205 }
206 }
207
208 #[derive(Default)]
209 struct TestPassthroughDsp;
210
211 impl Processor for TestPassthroughDsp {
212 type Params = TestPassthroughParams;
213
214 fn process(
215 &mut self,
216 _buffer: &mut [&mut [f32]],
217 _transport: &Transport,
218 _params: &Self::Params,
219 ) {
220 }
221 }
222
223 #[derive(Clone)]
224 struct TestParams;
225
226 impl Default for TestParams {
227 fn default() -> Self {
228 Self
229 }
230 }
231
232 impl ProcessorParams for TestParams {
233 fn param_specs() -> &'static [ParamSpec] {
234 &[]
235 }
236 }
237
238 struct LifecycleProbe {
239 set_sample_rate_calls: Arc<AtomicU32>,
240 reset_calls: Arc<AtomicU32>,
241 last_sample_rate_bits: Arc<AtomicU32>,
242 touched_process: Arc<AtomicBool>,
243 }
244
245 impl LifecycleProbe {
246 fn new() -> Self {
247 Self {
248 set_sample_rate_calls: Arc::new(AtomicU32::new(0)),
249 reset_calls: Arc::new(AtomicU32::new(0)),
250 last_sample_rate_bits: Arc::new(AtomicU32::new(0)),
251 touched_process: Arc::new(AtomicBool::new(false)),
252 }
253 }
254 }
255
256 impl Processor for LifecycleProbe {
257 type Params = TestParams;
258
259 fn process(
260 &mut self,
261 _buffer: &mut [&mut [f32]],
262 _transport: &Transport,
263 _params: &Self::Params,
264 ) {
265 self.touched_process.store(true, Ordering::SeqCst);
266 }
267
268 fn set_sample_rate(&mut self, sample_rate: f32) {
269 self.set_sample_rate_calls.fetch_add(1, Ordering::SeqCst);
270 self.last_sample_rate_bits
271 .store(sample_rate.to_bits(), Ordering::SeqCst);
272 }
273
274 fn reset(&mut self) {
275 self.reset_calls.fetch_add(1, Ordering::SeqCst);
276 }
277 }
278
279 #[test]
280 fn test_chain_processes_in_order() {
281 let mut chain = Chain {
282 first: TestGainDsp,
283 second: TestGainDsp,
284 };
285
286 let mut left = [1.0_f32, 1.0_f32];
287 let mut right = [1.0_f32, 1.0_f32];
288 let mut buffer = [&mut left[..], &mut right[..]];
289
290 let transport = Transport::default();
291 let params = ChainParams {
292 first: TestGainParams { level: 0.5 },
293 second: TestGainParams { level: 2.0 },
294 };
295
296 chain.process(&mut buffer, &transport, ¶ms);
297
298 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
300 assert!((buffer[1][0] - 1.0_f32).abs() < 1e-6);
301 }
302
303 #[test]
304 fn test_chain_with_passthrough() {
305 let mut chain = Chain {
306 first: TestPassthroughDsp,
307 second: TestGainDsp,
308 };
309
310 let mut left = [2.0_f32, 2.0_f32];
311 let mut right = [2.0_f32, 2.0_f32];
312 let mut buffer = [&mut left[..], &mut right[..]];
313
314 let transport = Transport::default();
315 let params = ChainParams {
316 first: TestPassthroughParams,
317 second: TestGainParams { level: 0.5 },
318 };
319
320 chain.process(&mut buffer, &transport, ¶ms);
321
322 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
324 }
325
326 #[test]
327 fn test_chain_params_merge() {
328 let specs = <ChainParams<TestGainParams, TestGainParams>>::param_specs();
329 assert_eq!(specs.len(), 2); assert_eq!(specs[0].name, "Level");
333 assert_eq!(specs[1].name, "Level");
334 }
335
336 #[test]
337 fn test_chain_default() {
338 let _chain: Chain<TestGainDsp, TestPassthroughDsp> = Chain::default();
339 let _params: ChainParams<TestGainParams, TestPassthroughParams> = ChainParams::default();
340 }
341
342 #[test]
343 fn test_chain_from_param_defaults_uses_children_spec_defaults() {
344 let defaults = <ChainParams<TestGainParams, TestGainParams>>::from_param_defaults();
345 assert!((defaults.first.level - 1.0).abs() < 1e-6);
346 assert!((defaults.second.level - 1.0).abs() < 1e-6);
347 }
348
349 #[test]
350 fn test_chain_apply_plain_values_splits_by_child_param_count() {
351 let mut params = <ChainParams<TestGainParams, TestGainParams>>::from_param_defaults();
352 params.apply_plain_values(&[0.25, 1.75]);
353
354 assert!((params.first.level - 0.25).abs() < 1e-6);
355 assert!((params.second.level - 1.75).abs() < 1e-6);
356 }
357
358 #[test]
359 fn test_chain_propagates_set_sample_rate_to_both_processors() {
360 let first = LifecycleProbe::new();
361 let second = LifecycleProbe::new();
362
363 let first_calls = Arc::clone(&first.set_sample_rate_calls);
364 let second_calls = Arc::clone(&second.set_sample_rate_calls);
365 let first_sr = Arc::clone(&first.last_sample_rate_bits);
366 let second_sr = Arc::clone(&second.last_sample_rate_bits);
367
368 let mut chain = Chain { first, second };
369 chain.set_sample_rate(48_000.0);
370
371 assert_eq!(first_calls.load(Ordering::SeqCst), 1);
372 assert_eq!(second_calls.load(Ordering::SeqCst), 1);
373 assert_eq!(f32::from_bits(first_sr.load(Ordering::SeqCst)), 48_000.0);
374 assert_eq!(f32::from_bits(second_sr.load(Ordering::SeqCst)), 48_000.0);
375 }
376
377 #[test]
378 fn test_chain_propagates_reset_to_both_processors() {
379 let first = LifecycleProbe::new();
380 let second = LifecycleProbe::new();
381
382 let first_resets = Arc::clone(&first.reset_calls);
383 let second_resets = Arc::clone(&second.reset_calls);
384
385 let mut chain = Chain { first, second };
386 chain.reset();
387
388 assert_eq!(first_resets.load(Ordering::SeqCst), 1);
389 assert_eq!(second_resets.load(Ordering::SeqCst), 1);
390 }
391}