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 use std::sync::OnceLock;
56
57 static MERGED_SPECS: OnceLock<Vec<ParamSpec>> = OnceLock::new();
58
59 MERGED_SPECS.get_or_init(|| {
60 let first_specs = PA::param_specs();
61 let second_specs = PB::param_specs();
62
63 let mut merged = Vec::with_capacity(first_specs.len() + second_specs.len());
64
65 for spec in first_specs {
67 merged.push(ParamSpec {
68 name: spec.name,
69 id_suffix: spec.id_suffix, range: spec.range.clone(),
71 default: spec.default,
72 unit: spec.unit,
73 group: spec.group,
74 });
75 }
76
77 for spec in second_specs {
79 merged.push(ParamSpec {
80 name: spec.name,
81 id_suffix: spec.id_suffix, range: spec.range.clone(),
83 default: spec.default,
84 unit: spec.unit,
85 group: spec.group,
86 });
87 }
88
89 merged
90 })
91 }
92}
93
94impl<A, B> Processor for Chain<A, B>
95where
96 A: Processor,
97 B: Processor,
98{
99 type Params = ChainParams<A::Params, B::Params>;
100
101 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
102 self.first.process(buffer, transport, ¶ms.first);
104 self.second.process(buffer, transport, ¶ms.second);
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::builtins::{GainDsp, GainParams, PassthroughDsp, PassthroughParams};
112
113 #[test]
114 fn test_chain_processes_in_order() {
115 let mut chain = Chain {
116 first: GainDsp::default(),
117 second: GainDsp::default(),
118 };
119
120 let mut left = [1.0_f32, 1.0_f32];
121 let mut right = [1.0_f32, 1.0_f32];
122 let mut buffer = [&mut left[..], &mut right[..]];
123
124 let transport = Transport::default();
125 let params = ChainParams {
126 first: GainParams { level: 0.5 },
127 second: GainParams { level: 2.0 },
128 };
129
130 chain.process(&mut buffer, &transport, ¶ms);
131
132 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
134 assert!((buffer[1][0] - 1.0_f32).abs() < 1e-6);
135 }
136
137 #[test]
138 fn test_chain_with_passthrough() {
139 let mut chain = Chain {
140 first: PassthroughDsp,
141 second: GainDsp::default(),
142 };
143
144 let mut left = [2.0_f32, 2.0_f32];
145 let mut right = [2.0_f32, 2.0_f32];
146 let mut buffer = [&mut left[..], &mut right[..]];
147
148 let transport = Transport::default();
149 let params = ChainParams {
150 first: PassthroughParams,
151 second: GainParams { level: 0.5 },
152 };
153
154 chain.process(&mut buffer, &transport, ¶ms);
155
156 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
158 }
159
160 #[test]
161 fn test_chain_params_merge() {
162 let specs = <ChainParams<GainParams, GainParams>>::param_specs();
163 assert_eq!(specs.len(), 2); assert_eq!(specs[0].name, "Level");
167 assert_eq!(specs[1].name, "Level");
168 }
169
170 #[test]
171 fn test_chain_default() {
172 let _chain: Chain<GainDsp, PassthroughDsp> = Chain::default();
173 let _params: ChainParams<GainParams, PassthroughParams> = ChainParams::default();
174 }
175}