bbx_dsp/blocks/effectors/
channel_router.rs

1//! Channel routing and manipulation for stereo signals.
2
3use std::marker::PhantomData;
4
5use crate::{block::Block, channel::ChannelConfig, context::DspContext, parameter::ModulationOutput, sample::Sample};
6
7/// Channel routing mode for stereo signals.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9#[repr(u8)]
10pub enum ChannelMode {
11    /// Normal stereo (L -> L, R -> R)
12    #[default]
13    Stereo = 0,
14    /// Left only (L -> L, L -> R)
15    Left = 1,
16    /// Right only (R -> L, R -> R)
17    Right = 2,
18    /// Swap channels (L -> R, R -> L)
19    Swap = 3,
20}
21
22impl From<i32> for ChannelMode {
23    fn from(value: i32) -> Self {
24        match value {
25            0 => Self::Stereo,
26            1 => Self::Left,
27            2 => Self::Right,
28            3 => Self::Swap,
29            _ => Self::Stereo,
30        }
31    }
32}
33
34impl From<f32> for ChannelMode {
35    fn from(value: f32) -> Self {
36        Self::from(value as i32)
37    }
38}
39
40/// A channel router block for stereo signal manipulation.
41///
42/// Supports channel selection, mono summing, and phase inversion.
43pub struct ChannelRouterBlock<S: Sample> {
44    /// Channel routing mode.
45    pub mode: ChannelMode,
46    /// Sum to mono (L+R)/2 on both channels.
47    pub mono: bool,
48    /// Invert left channel phase.
49    pub invert_left: bool,
50    /// Invert right channel phase.
51    pub invert_right: bool,
52
53    _phantom: PhantomData<S>,
54}
55
56impl<S: Sample> ChannelRouterBlock<S> {
57    /// Create a new `ChannelRouterBlock`.
58    pub fn new(mode: ChannelMode, mono: bool, invert_left: bool, invert_right: bool) -> Self {
59        Self {
60            mode,
61            mono,
62            invert_left,
63            invert_right,
64            _phantom: PhantomData,
65        }
66    }
67
68    /// Create with default settings (stereo passthrough).
69    pub fn default_new() -> Self {
70        Self::new(ChannelMode::Stereo, false, false, false)
71    }
72}
73
74impl<S: Sample> Block<S> for ChannelRouterBlock<S> {
75    fn process(&mut self, inputs: &[&[S]], outputs: &mut [&mut [S]], _modulation_values: &[S], _context: &DspContext) {
76        // Handle mono input
77        if inputs.is_empty() {
78            return;
79        }
80
81        let left_in = inputs.first().copied();
82        let right_in = inputs.get(1).copied().or(left_in);
83
84        let (left_in, right_in) = match (left_in, right_in) {
85            (Some(l), Some(r)) => (l, r),
86            _ => return,
87        };
88
89        let num_samples = left_in.len().min(outputs.first().map(|o| o.len()).unwrap_or(0));
90
91        if num_samples == 0 {
92            return;
93        }
94
95        let half = S::from_f64(0.5);
96
97        for i in 0..num_samples {
98            let l_sample = left_in[i];
99            let r_sample = if inputs.len() > 1 { right_in[i] } else { l_sample };
100
101            let (mut l_out, mut r_out) = match self.mode {
102                ChannelMode::Stereo => (l_sample, r_sample),
103                ChannelMode::Left => (l_sample, l_sample),
104                ChannelMode::Right => (r_sample, r_sample),
105                ChannelMode::Swap => (r_sample, l_sample),
106            };
107
108            if self.mono {
109                let mono = (l_out + r_out) * half;
110                l_out = mono;
111                r_out = mono;
112            }
113
114            if self.invert_left {
115                l_out = -l_out;
116            }
117            if self.invert_right {
118                r_out = -r_out;
119            }
120
121            if !outputs.is_empty() {
122                outputs[0][i] = l_out;
123            }
124            if outputs.len() > 1 {
125                outputs[1][i] = r_out;
126            }
127        }
128    }
129
130    #[inline]
131    fn input_count(&self) -> usize {
132        2 // Stereo input
133    }
134
135    #[inline]
136    fn output_count(&self) -> usize {
137        2 // Stereo output
138    }
139
140    #[inline]
141    fn modulation_outputs(&self) -> &[ModulationOutput] {
142        &[]
143    }
144
145    #[inline]
146    fn channel_config(&self) -> ChannelConfig {
147        ChannelConfig::Explicit
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154    use crate::channel::ChannelLayout;
155
156    fn test_context(buffer_size: usize) -> DspContext {
157        DspContext {
158            sample_rate: 44100.0,
159            num_channels: 2,
160            buffer_size,
161            current_sample: 0,
162            channel_layout: ChannelLayout::Stereo,
163        }
164    }
165
166    #[test]
167    fn test_channel_router_input_output_counts_f32() {
168        let router = ChannelRouterBlock::<f32>::default_new();
169        assert_eq!(router.input_count(), 2);
170        assert_eq!(router.output_count(), 2);
171    }
172
173    #[test]
174    fn test_channel_router_input_output_counts_f64() {
175        let router = ChannelRouterBlock::<f64>::default_new();
176        assert_eq!(router.input_count(), 2);
177        assert_eq!(router.output_count(), 2);
178    }
179
180    #[test]
181    fn test_channel_router_returns_explicit_config() {
182        let router = ChannelRouterBlock::<f32>::default_new();
183        assert_eq!(router.channel_config(), ChannelConfig::Explicit);
184    }
185
186    #[test]
187    fn test_channel_router_stereo_passthrough_f32() {
188        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Stereo, false, false, false);
189        let context = test_context(4);
190
191        let left_in = [1.0f32, 2.0, 3.0, 4.0];
192        let right_in = [5.0f32, 6.0, 7.0, 8.0];
193        let mut left_out = [0.0f32; 4];
194        let mut right_out = [0.0f32; 4];
195
196        let inputs: [&[f32]; 2] = [&left_in, &right_in];
197        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
198
199        router.process(&inputs, &mut outputs, &[], &context);
200
201        assert_eq!(left_out, left_in);
202        assert_eq!(right_out, right_in);
203    }
204
205    #[test]
206    fn test_channel_router_stereo_passthrough_f64() {
207        let mut router = ChannelRouterBlock::<f64>::new(ChannelMode::Stereo, false, false, false);
208        let context = test_context(4);
209
210        let left_in = [1.0f64, 2.0, 3.0, 4.0];
211        let right_in = [5.0f64, 6.0, 7.0, 8.0];
212        let mut left_out = [0.0f64; 4];
213        let mut right_out = [0.0f64; 4];
214
215        let inputs: [&[f64]; 2] = [&left_in, &right_in];
216        let mut outputs: [&mut [f64]; 2] = [&mut left_out, &mut right_out];
217
218        router.process(&inputs, &mut outputs, &[], &context);
219
220        assert_eq!(left_out, left_in);
221        assert_eq!(right_out, right_in);
222    }
223
224    #[test]
225    fn test_channel_router_left_mode_f32() {
226        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Left, false, false, false);
227        let context = test_context(4);
228
229        let left_in = [1.0f32, 2.0, 3.0, 4.0];
230        let right_in = [5.0f32, 6.0, 7.0, 8.0];
231        let mut left_out = [0.0f32; 4];
232        let mut right_out = [0.0f32; 4];
233
234        let inputs: [&[f32]; 2] = [&left_in, &right_in];
235        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
236
237        router.process(&inputs, &mut outputs, &[], &context);
238
239        assert_eq!(left_out, left_in);
240        assert_eq!(right_out, left_in);
241    }
242
243    #[test]
244    fn test_channel_router_right_mode_f32() {
245        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Right, false, false, false);
246        let context = test_context(4);
247
248        let left_in = [1.0f32, 2.0, 3.0, 4.0];
249        let right_in = [5.0f32, 6.0, 7.0, 8.0];
250        let mut left_out = [0.0f32; 4];
251        let mut right_out = [0.0f32; 4];
252
253        let inputs: [&[f32]; 2] = [&left_in, &right_in];
254        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
255
256        router.process(&inputs, &mut outputs, &[], &context);
257
258        assert_eq!(left_out, right_in);
259        assert_eq!(right_out, right_in);
260    }
261
262    #[test]
263    fn test_channel_router_swap_mode_f32() {
264        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Swap, false, false, false);
265        let context = test_context(4);
266
267        let left_in = [1.0f32, 2.0, 3.0, 4.0];
268        let right_in = [5.0f32, 6.0, 7.0, 8.0];
269        let mut left_out = [0.0f32; 4];
270        let mut right_out = [0.0f32; 4];
271
272        let inputs: [&[f32]; 2] = [&left_in, &right_in];
273        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
274
275        router.process(&inputs, &mut outputs, &[], &context);
276
277        assert_eq!(left_out, right_in);
278        assert_eq!(right_out, left_in);
279    }
280
281    #[test]
282    fn test_channel_router_mono_sum_f32() {
283        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Stereo, true, false, false);
284        let context = test_context(4);
285
286        let left_in = [2.0f32, 4.0, 6.0, 8.0];
287        let right_in = [4.0f32, 6.0, 8.0, 10.0];
288        let mut left_out = [0.0f32; 4];
289        let mut right_out = [0.0f32; 4];
290
291        let inputs: [&[f32]; 2] = [&left_in, &right_in];
292        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
293
294        router.process(&inputs, &mut outputs, &[], &context);
295
296        let expected = [3.0f32, 5.0, 7.0, 9.0];
297        for i in 0..4 {
298            assert!((left_out[i] - expected[i]).abs() < 1e-6);
299            assert!((right_out[i] - expected[i]).abs() < 1e-6);
300        }
301    }
302
303    #[test]
304    fn test_channel_router_invert_left_f32() {
305        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Stereo, false, true, false);
306        let context = test_context(4);
307
308        let left_in = [1.0f32, 2.0, 3.0, 4.0];
309        let right_in = [5.0f32, 6.0, 7.0, 8.0];
310        let mut left_out = [0.0f32; 4];
311        let mut right_out = [0.0f32; 4];
312
313        let inputs: [&[f32]; 2] = [&left_in, &right_in];
314        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
315
316        router.process(&inputs, &mut outputs, &[], &context);
317
318        let expected_left = [-1.0f32, -2.0, -3.0, -4.0];
319        assert_eq!(left_out, expected_left);
320        assert_eq!(right_out, right_in);
321    }
322
323    #[test]
324    fn test_channel_router_invert_right_f32() {
325        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Stereo, false, false, true);
326        let context = test_context(4);
327
328        let left_in = [1.0f32, 2.0, 3.0, 4.0];
329        let right_in = [5.0f32, 6.0, 7.0, 8.0];
330        let mut left_out = [0.0f32; 4];
331        let mut right_out = [0.0f32; 4];
332
333        let inputs: [&[f32]; 2] = [&left_in, &right_in];
334        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
335
336        router.process(&inputs, &mut outputs, &[], &context);
337
338        let expected_right = [-5.0f32, -6.0, -7.0, -8.0];
339        assert_eq!(left_out, left_in);
340        assert_eq!(right_out, expected_right);
341    }
342
343    #[test]
344    fn test_channel_router_invert_both_f32() {
345        let mut router = ChannelRouterBlock::<f32>::new(ChannelMode::Stereo, false, true, true);
346        let context = test_context(4);
347
348        let left_in = [1.0f32, 2.0, 3.0, 4.0];
349        let right_in = [5.0f32, 6.0, 7.0, 8.0];
350        let mut left_out = [0.0f32; 4];
351        let mut right_out = [0.0f32; 4];
352
353        let inputs: [&[f32]; 2] = [&left_in, &right_in];
354        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
355
356        router.process(&inputs, &mut outputs, &[], &context);
357
358        let expected_left = [-1.0f32, -2.0, -3.0, -4.0];
359        let expected_right = [-5.0f32, -6.0, -7.0, -8.0];
360        assert_eq!(left_out, expected_left);
361        assert_eq!(right_out, expected_right);
362    }
363
364    #[test]
365    fn test_channel_mode_from_i32() {
366        assert_eq!(ChannelMode::from(0), ChannelMode::Stereo);
367        assert_eq!(ChannelMode::from(1), ChannelMode::Left);
368        assert_eq!(ChannelMode::from(2), ChannelMode::Right);
369        assert_eq!(ChannelMode::from(3), ChannelMode::Swap);
370        assert_eq!(ChannelMode::from(999), ChannelMode::Stereo);
371    }
372
373    #[test]
374    fn test_channel_mode_from_f32() {
375        assert_eq!(ChannelMode::from(0.0f32), ChannelMode::Stereo);
376        assert_eq!(ChannelMode::from(1.0f32), ChannelMode::Left);
377        assert_eq!(ChannelMode::from(2.0f32), ChannelMode::Right);
378        assert_eq!(ChannelMode::from(3.0f32), ChannelMode::Swap);
379    }
380}