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;