firefly_audio/
manager.rs

1use crate::*;
2use alloc::boxed::Box;
3use alloc::vec;
4use alloc::vec::Vec;
5
6pub struct Manager {
7    root: Node,
8    paths: Vec<Box<[u8]>>,
9    prev: Option<Frame>,
10    consumed: usize,
11}
12
13impl Manager {
14    #[must_use]
15    pub fn new() -> Self {
16        Self {
17            root: Node::new_root(),
18            paths: vec![Box::new([])],
19            prev: None,
20            consumed: 0,
21        }
22    }
23
24    /// Find the node with the given ID in the graph.
25    ///
26    /// ## Errors
27    ///
28    /// If the node is not present, returns [`NodeError::UnknownID`].
29    pub fn get_node(&mut self, id: u32) -> Result<&mut Node, NodeError> {
30        let Some(path) = self.paths.get(id as usize) else {
31            return Err(NodeError::UnknownID(id));
32        };
33        Ok(self.root.get_node(path))
34    }
35
36    /// Find a child node for the node with the given ID. Returns the new node ID.
37    ///
38    /// ## Errors
39    ///
40    /// If the parent node is not present, returns [`NodeError::UnknownID`].
41    /// If there are too many nodes, returns [`NodeError::TooManyChildren`]
42    /// or [`NodeError::TooManyNodes`].
43    pub fn add_node(&mut self, parent_id: u32, b: Box<dyn Processor>) -> Result<u32, NodeError> {
44        const MAX_NODES: usize = 32;
45        if self.paths.len() >= MAX_NODES {
46            return Err(NodeError::TooManyNodes);
47        }
48        let Some(parent_path) = self.paths.get(parent_id as usize) else {
49            return Err(NodeError::UnknownID(parent_id));
50        };
51        let parent_node = self.root.get_node(parent_path);
52        let sub_id = parent_node.add(b)?;
53        #[expect(clippy::cast_possible_truncation)]
54        let id = self.paths.len() as u32;
55        let mut path = Vec::new();
56        path.extend_from_slice(parent_path);
57        path.push(sub_id);
58        self.paths.push(path.into_boxed_slice());
59        Ok(id)
60    }
61
62    /// Remove all child nodes from the node with the given ID.
63    ///
64    /// ## Errors
65    ///
66    /// If the node is not present, returns [`NodeError::UnknownID`].
67    pub fn clear(&mut self, id: u32) -> Result<(), NodeError> {
68        let Some(path) = self.paths.get(id as usize) else {
69            return Err(NodeError::UnknownID(id));
70        };
71        let node = self.root.get_node(path);
72        node.clear();
73        let mut paths = Vec::new();
74        for p in &self.paths {
75            if p.len() > path.len() && p.starts_with(path) {
76                continue;
77            }
78            paths.push(p.clone());
79        }
80        self.paths = paths;
81        Ok(())
82    }
83
84    /// Fill the given buffer with PCM sound values.
85    pub fn write(&mut self, buf: &mut [i16]) {
86        // If there is partially emitted frame left from the previous write iteration,
87        // write it to the buffer.
88        let mut buf = self.write_prev(buf);
89
90        while buf.len() >= 16 {
91            let Some(frame) = self.root.next_frame() else {
92                break;
93            };
94            let written = fill_buf(buf, &frame, 0);
95            buf = &mut buf[written..];
96        }
97
98        // If the buffer is not completely filled yet, partially write
99        // the next frame into it. The rest of the frame will be written
100        // on the next write loop.
101        if !buf.is_empty() {
102            if let Some(frame) = self.root.next_frame() {
103                self.prev = Some(frame);
104                self.consumed = 0;
105                buf = self.write_prev(buf);
106                debug_assert!(buf.is_empty());
107            } else {
108                // fill the remainder of the buffer with zeros
109                // to avoid playing old values
110                buf.fill(0);
111            }
112        }
113    }
114
115    #[must_use]
116    fn write_prev<'a>(&mut self, buf: &'a mut [i16]) -> &'a mut [i16] {
117        debug_assert!(self.consumed < 16);
118        let Some(frame) = &mut self.prev else {
119            return buf;
120        };
121        let written = fill_buf(buf, frame, self.consumed);
122        let consumed = self.consumed + written;
123        if consumed >= 16 {
124            debug_assert_eq!(consumed, 16);
125            self.prev = None;
126            self.consumed = 0;
127        } else {
128            self.consumed = consumed;
129        }
130        &mut buf[written..]
131    }
132}
133
134/// Write the given frame (starting from skip index) into the beginning of the buffer.
135fn fill_buf(buf: &mut [i16], frame: &Frame, skip: usize) -> usize {
136    // make iterators over left and right channels
137    let right = frame.right.unwrap_or(frame.left);
138    let mut left = frame.left.as_array().iter();
139    let mut right = right.as_array().iter();
140
141    // skip the given number of samples
142    let mut even = true;
143    for _ in 0..skip {
144        if even {
145            left.next()
146        } else {
147            right.next()
148        };
149        even = !even;
150    }
151
152    let mut written = 0;
153    #[expect(clippy::cast_possible_truncation)]
154    for tar in buf.iter_mut() {
155        let chan = if even { &mut left } else { &mut right };
156        let Some(s) = chan.next() else { break };
157        even = !even;
158        written += 1;
159        let s = s.clamp(-1., 1.);
160        *tar = (s * f32::from(i16::MAX)) as i16;
161    }
162    written
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn test_fill_buf_stereo() {
171        let frame = Frame::stereo(
172            Sample::new([
173                u2f(11),
174                u2f(13),
175                u2f(15),
176                u2f(17),
177                u2f(19),
178                u2f(21),
179                u2f(23),
180                u2f(25),
181            ]),
182            Sample::new([
183                u2f(12),
184                u2f(14),
185                u2f(16),
186                u2f(18),
187                u2f(20),
188                u2f(22),
189                u2f(24),
190                u2f(26),
191            ]),
192        );
193        let mut buf = [0i16; 20];
194        fill_buf(&mut buf[..], &frame, 0);
195        for (a, b) in buf[..15].iter().zip(&buf[1..]) {
196            assert!(a < b, "{a} < {b}");
197        }
198        assert!(buf[0] != 0);
199        assert_eq!(&buf[16..], &[0, 0, 0, 0]);
200    }
201
202    #[expect(clippy::cast_lossless)]
203    fn u2f(u: u16) -> f32 {
204        u as f32 / 100.
205    }
206}