bbx_player/
signal.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicBool, Ordering},
4};
5
6use bbx_core::StackVec;
7use bbx_dsp::{
8    buffer::{AudioBuffer, Buffer},
9    graph::{Graph, MAX_BLOCK_OUTPUTS},
10    sample::Sample,
11};
12
13/// Wraps a DSP graph as an iterator of interleaved samples.
14///
15/// The iterator yields samples in interleaved format (L, R, L, R, ...)
16/// which is the format expected by audio backends.
17pub struct Signal<S: Sample> {
18    graph: Graph<S>,
19    output_buffers: Vec<AudioBuffer<S>>,
20    sample_rate: u32,
21    num_channels: usize,
22    buffer_size: usize,
23    channel_index: usize,
24    sample_index: usize,
25    stop_flag: Arc<AtomicBool>,
26}
27
28impl<S: Sample> Signal<S> {
29    /// Create a `Signal` from a DSP `Graph`.
30    pub fn new(graph: Graph<S>, stop_flag: Arc<AtomicBool>) -> Self {
31        let channels = graph.context().num_channels;
32        let buffer_size = graph.context().buffer_size;
33        let sample_rate = graph.context().sample_rate as u32;
34
35        let mut output_buffers = Vec::with_capacity(channels);
36        for _ in 0..channels {
37            output_buffers.push(AudioBuffer::new(buffer_size));
38        }
39
40        Self {
41            graph,
42            output_buffers,
43            channel_index: 0,
44            sample_index: 0,
45            num_channels: channels,
46            buffer_size,
47            sample_rate,
48            stop_flag,
49        }
50    }
51
52    /// Get the sample rate of the signal.
53    pub fn sample_rate(&self) -> u32 {
54        self.sample_rate
55    }
56
57    /// Get the number of channels.
58    pub fn num_channels(&self) -> usize {
59        self.num_channels
60    }
61
62    fn process(&mut self) -> S {
63        if self.channel_index == 0 && self.sample_index == 0 {
64            debug_assert!(
65                self.num_channels <= MAX_BLOCK_OUTPUTS,
66                "Channel count {} exceeds MAX_BLOCK_OUTPUTS {}",
67                self.num_channels,
68                MAX_BLOCK_OUTPUTS
69            );
70            let mut output_refs: StackVec<&mut [S], MAX_BLOCK_OUTPUTS> = StackVec::new();
71            for buf in self.output_buffers.iter_mut() {
72                let _ = output_refs.push(buf.as_mut_slice());
73            }
74            self.graph.process_buffers(output_refs.as_mut_slice());
75        }
76
77        // SAFETY: Bounds guaranteed by construction:
78        // - channel_index: starts at 0, reset to 0 when >= num_channels (line 80)
79        // - sample_index: wraps via modulo % buffer_size (line 83)
80        debug_assert!(
81            self.channel_index < self.num_channels,
82            "channel_index {} exceeds num_channels {}",
83            self.channel_index,
84            self.num_channels
85        );
86        debug_assert!(
87            self.sample_index < self.buffer_size,
88            "sample_index {} exceeds buffer_size {}",
89            self.sample_index,
90            self.buffer_size
91        );
92        let sample = self.output_buffers[self.channel_index][self.sample_index];
93
94        self.channel_index += 1;
95        if self.channel_index >= self.num_channels {
96            self.channel_index = 0;
97            self.sample_index += 1;
98            self.sample_index %= self.buffer_size;
99        }
100
101        sample
102    }
103}
104
105impl<S: Sample> Iterator for Signal<S> {
106    type Item = S;
107
108    fn next(&mut self) -> Option<Self::Item> {
109        if self.stop_flag.load(Ordering::SeqCst) {
110            return None;
111        }
112        Some(self.process())
113    }
114}