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
104impl<A, B> Processor for Chain<A, B>
105where
106 A: Processor,
107 B: Processor,
108{
109 type Params = ChainParams<A::Params, B::Params>;
110
111 fn process(&mut self, buffer: &mut [&mut [f32]], transport: &Transport, params: &Self::Params) {
112 self.first.process(buffer, transport, ¶ms.first);
114 self.second.process(buffer, transport, ¶ms.second);
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::builtins::{GainDsp, GainParams, PassthroughDsp, PassthroughParams};
122
123 #[test]
124 fn test_chain_processes_in_order() {
125 let mut chain = Chain {
126 first: GainDsp::default(),
127 second: GainDsp::default(),
128 };
129
130 let mut left = [1.0_f32, 1.0_f32];
131 let mut right = [1.0_f32, 1.0_f32];
132 let mut buffer = [&mut left[..], &mut right[..]];
133
134 let transport = Transport::default();
135 let params = ChainParams {
136 first: GainParams { level: 0.5 },
137 second: GainParams { level: 2.0 },
138 };
139
140 chain.process(&mut buffer, &transport, ¶ms);
141
142 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
144 assert!((buffer[1][0] - 1.0_f32).abs() < 1e-6);
145 }
146
147 #[test]
148 fn test_chain_with_passthrough() {
149 let mut chain = Chain {
150 first: PassthroughDsp,
151 second: GainDsp::default(),
152 };
153
154 let mut left = [2.0_f32, 2.0_f32];
155 let mut right = [2.0_f32, 2.0_f32];
156 let mut buffer = [&mut left[..], &mut right[..]];
157
158 let transport = Transport::default();
159 let params = ChainParams {
160 first: PassthroughParams,
161 second: GainParams { level: 0.5 },
162 };
163
164 chain.process(&mut buffer, &transport, ¶ms);
165
166 assert!((buffer[0][0] - 1.0_f32).abs() < 1e-6);
168 }
169
170 #[test]
171 fn test_chain_params_merge() {
172 let specs = <ChainParams<GainParams, GainParams>>::param_specs();
173 assert_eq!(specs.len(), 2); assert_eq!(specs[0].name, "Level");
177 assert_eq!(specs[1].name, "Level");
178 }
179
180 #[test]
181 fn test_chain_default() {
182 let _chain: Chain<GainDsp, PassthroughDsp> = Chain::default();
183 let _params: ChainParams<GainParams, PassthroughParams> = ChainParams::default();
184 }
185}