Skip to main content

sesh_sdk/
scratch.rs

1use std::cell::{Cell, UnsafeCell};
2
3/// Heap-allocated buffer pool for intermediate results during audio processing.
4///
5/// Each call to `buf(frames)` returns the next available `&mut [f32]` of the
6/// requested length. The cursor wraps around when all slots are used.
7///
8/// Uses interior mutability so multiple buffers can be live simultaneously.
9///
10/// # Example
11///
12/// ```rust
13/// use sesh_sdk::Scratch;
14///
15/// let scratch = Scratch::new();
16/// let frames = 128;
17/// let a = scratch.buf(frames);
18/// let b = scratch.buf(frames);
19/// let c = scratch.buf(frames);
20/// // a, b, c are all valid simultaneously
21/// ```
22pub struct ScratchPool {
23    bufs: UnsafeCell<Vec<f32>>,
24    frames_cap: usize,
25    n: usize,
26    cursor: Cell<usize>,
27}
28
29impl ScratchPool {
30    /// Create a new scratch pool with `n` buffers of `frames_cap` frames each.
31    pub fn with_capacity(n: usize, frames_cap: usize) -> Self {
32        Self {
33            bufs: UnsafeCell::new(vec![0.0; n * frames_cap]),
34            frames_cap,
35            n,
36            cursor: Cell::new(0),
37        }
38    }
39
40    /// Create a default scratch pool: 128 buffers of 2048 frames (1MB).
41    pub fn new() -> Self {
42        Self::with_capacity(128, 2048)
43    }
44
45    /// Get the next scratch buffer, sliced to `frames`. Wraps when all slots are used.
46    pub fn buf(&self, frames: usize) -> &mut [f32] {
47        let idx = self.cursor.get() % self.n;
48        self.cursor.set(idx + 1);
49        let start = idx * self.frames_cap;
50        unsafe {
51            let bufs = &mut *self.bufs.get();
52            &mut bufs[start..start + frames]
53        }
54    }
55}
56
57/// Default scratch pool: 128 buffers of 2048 frames each (1MB).
58pub type Scratch = ScratchPool;