bbx_dsp/blocks/io/
output.rs

1//! Terminal output block for DSP graphs.
2
3use std::marker::PhantomData;
4
5use crate::{block::Block, context::DspContext, parameter::ModulationOutput, sample::Sample};
6
7/// The terminal output block for a DSP graph.
8///
9/// Collects final audio from upstream blocks and makes it available
10/// for playback or further processing outside the graph.
11pub struct OutputBlock<S: Sample> {
12    num_channels: usize,
13    _phantom: PhantomData<S>,
14}
15
16impl<S: Sample> OutputBlock<S> {
17    /// Create an `OutputBlock` with a given number of channels.
18    pub fn new(num_channels: usize) -> Self {
19        Self {
20            num_channels,
21            _phantom: PhantomData,
22        }
23    }
24}
25
26impl<S: Sample> Block<S> for OutputBlock<S> {
27    fn process(&mut self, inputs: &[&[S]], outputs: &mut [&mut [S]], _modulation_values: &[S], _context: &DspContext) {
28        for (input, output) in inputs.iter().zip(outputs.iter_mut()) {
29            output.copy_from_slice(input);
30        }
31    }
32
33    #[inline]
34    fn input_count(&self) -> usize {
35        self.num_channels
36    }
37
38    #[inline]
39    fn output_count(&self) -> usize {
40        self.num_channels
41    }
42
43    #[inline]
44    fn modulation_outputs(&self) -> &[ModulationOutput] {
45        &[]
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use crate::channel::ChannelLayout;
53
54    fn test_context(buffer_size: usize) -> DspContext {
55        DspContext {
56            sample_rate: 44100.0,
57            num_channels: 2,
58            buffer_size,
59            current_sample: 0,
60            channel_layout: ChannelLayout::Stereo,
61        }
62    }
63
64    #[test]
65    fn test_output_block_input_output_counts_f32() {
66        let block = OutputBlock::<f32>::new(2);
67        assert_eq!(block.input_count(), 2);
68        assert_eq!(block.output_count(), 2);
69    }
70
71    #[test]
72    fn test_output_block_input_output_counts_f64() {
73        let block = OutputBlock::<f64>::new(2);
74        assert_eq!(block.input_count(), 2);
75        assert_eq!(block.output_count(), 2);
76    }
77
78    #[test]
79    fn test_output_block_mono_f32() {
80        let block = OutputBlock::<f32>::new(1);
81        assert_eq!(block.input_count(), 1);
82        assert_eq!(block.output_count(), 1);
83    }
84
85    #[test]
86    fn test_output_block_multichannel_f32() {
87        let block = OutputBlock::<f32>::new(6);
88        assert_eq!(block.input_count(), 6);
89        assert_eq!(block.output_count(), 6);
90    }
91
92    #[test]
93    fn test_output_block_passthrough_f32() {
94        let mut block = OutputBlock::<f32>::new(2);
95        let context = test_context(4);
96
97        let left_in = [1.0f32, 2.0, 3.0, 4.0];
98        let right_in = [5.0f32, 6.0, 7.0, 8.0];
99        let mut left_out = [0.0f32; 4];
100        let mut right_out = [0.0f32; 4];
101
102        let inputs: [&[f32]; 2] = [&left_in, &right_in];
103        let mut outputs: [&mut [f32]; 2] = [&mut left_out, &mut right_out];
104
105        block.process(&inputs, &mut outputs, &[], &context);
106
107        assert_eq!(left_out, left_in);
108        assert_eq!(right_out, right_in);
109    }
110
111    #[test]
112    fn test_output_block_passthrough_f64() {
113        let mut block = OutputBlock::<f64>::new(2);
114        let context = test_context(4);
115
116        let left_in = [1.0f64, 2.0, 3.0, 4.0];
117        let right_in = [5.0f64, 6.0, 7.0, 8.0];
118        let mut left_out = [0.0f64; 4];
119        let mut right_out = [0.0f64; 4];
120
121        let inputs: [&[f64]; 2] = [&left_in, &right_in];
122        let mut outputs: [&mut [f64]; 2] = [&mut left_out, &mut right_out];
123
124        block.process(&inputs, &mut outputs, &[], &context);
125
126        assert_eq!(left_out, left_in);
127        assert_eq!(right_out, right_in);
128    }
129
130    #[test]
131    fn test_output_block_modulation_outputs_empty() {
132        let block = OutputBlock::<f32>::new(2);
133        assert!(block.modulation_outputs().is_empty());
134    }
135}