Skip to main content

aether_core/
buffer_pool.rs

1//! Pre-allocated audio buffer pool.
2//!
3//! All buffers are allocated at startup. The RT thread only borrows slices —
4//! no allocation, no deallocation, no system calls.
5
6use crate::{BUFFER_SIZE, MAX_BUFFERS};
7
8/// Opaque handle to a buffer in the pool.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct BufferId(pub u32);
11
12impl BufferId {
13    pub const SILENCE: Self = Self(u32::MAX);
14}
15
16/// Structure-of-Arrays audio buffer pool.
17/// Each buffer is exactly BUFFER_SIZE f32 samples.
18pub struct BufferPool {
19    /// Flat storage: `buffers[id * BUFFER_SIZE .. (id+1) * BUFFER_SIZE]`
20    storage: Vec<f32>,
21    free_list: Vec<u32>,
22    capacity: usize,
23}
24
25impl BufferPool {
26    pub fn new(capacity: usize) -> Self {
27        let storage = vec![0.0f32; capacity * BUFFER_SIZE];
28        let free_list: Vec<u32> = (0..capacity as u32).rev().collect();
29        Self {
30            storage,
31            free_list,
32            capacity,
33        }
34    }
35
36    /// Acquire a zeroed buffer. O(1). Returns None if pool is exhausted.
37    pub fn acquire(&mut self) -> Option<BufferId> {
38        let id = self.free_list.pop()?;
39        // Zero the buffer before handing it out.
40        let start = id as usize * BUFFER_SIZE;
41        self.storage[start..start + BUFFER_SIZE].fill(0.0);
42        Some(BufferId(id))
43    }
44
45    /// Release a buffer back to the pool. O(1).
46    pub fn release(&mut self, id: BufferId) {
47        debug_assert!((id.0 as usize) < self.capacity, "BufferId out of range");
48        self.free_list.push(id.0);
49    }
50
51    /// Get a read-only slice for a buffer.
52    #[inline(always)]
53    pub fn get(&self, id: BufferId) -> &[f32; BUFFER_SIZE] {
54        let start = id.0 as usize * BUFFER_SIZE;
55        self.storage[start..start + BUFFER_SIZE].try_into().unwrap()
56    }
57
58    /// Get a mutable slice for a buffer.
59    #[inline(always)]
60    pub fn get_mut(&mut self, id: BufferId) -> &mut [f32; BUFFER_SIZE] {
61        let start = id.0 as usize * BUFFER_SIZE;
62        (&mut self.storage[start..start + BUFFER_SIZE])
63            .try_into()
64            .unwrap()
65    }
66
67    /// Return a zeroed silence buffer (static, no allocation).
68    pub fn silence() -> &'static [f32; BUFFER_SIZE] {
69        static SILENCE: [f32; BUFFER_SIZE] = [0.0f32; BUFFER_SIZE];
70        &SILENCE
71    }
72
73    pub fn available(&self) -> usize {
74        self.free_list.len()
75    }
76    pub fn capacity(&self) -> usize {
77        self.capacity
78    }
79}
80
81impl Default for BufferPool {
82    fn default() -> Self {
83        Self::new(MAX_BUFFERS)
84    }
85}