sirena/
ring_buffer.rs

1//! Ring buffer is useful for storing of last N samples.
2
3#[allow(unused_imports)]
4use micromath::F32Ext;
5
6/// Write to and read from a ring buffer.
7pub struct RingBuffer<const N: usize> {
8    buffer: [f32; N],
9    write_index: usize,
10}
11
12impl<const N: usize> RingBuffer<N> {
13    pub fn new() -> Self {
14        let buffer = {
15            let mut data: [core::mem::MaybeUninit<f32>; N] =
16                unsafe { core::mem::MaybeUninit::uninit().assume_init() };
17            for elem in &mut data[..] {
18                unsafe {
19                    core::ptr::write(elem.as_mut_ptr(), 0.0);
20                }
21            }
22            unsafe { core::mem::transmute_copy(&data) }
23        };
24
25        Self {
26            buffer,
27            write_index: 0,
28        }
29    }
30
31    pub fn write(&mut self, value: f32) {
32        self.write_index %= N;
33        self.buffer[self.write_index] = value;
34        self.write_index += 1;
35    }
36
37    pub fn peek(&self, relative_index: i32) -> f32 {
38        let index =
39            (self.write_index as i32 + relative_index - 1).wrapping_rem_euclid(N as i32) as usize;
40        self.buffer[index]
41    }
42
43    pub fn peek_interpolated(&self, relative_index: f32) -> f32 {
44        let index_a = relative_index.floor() as i32;
45        let a = self.peek(index_a);
46
47        let index_b = relative_index.ceil() as i32;
48        let b = self.peek(index_b);
49
50        let diff = b - a;
51        let root = if relative_index < 0.0 { b } else { a };
52
53        root + diff * relative_index.fract()
54    }
55}
56
57impl<const N: usize> Default for RingBuffer<N> {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn initialize_buffer() {
69        let _buffer = RingBuffer::<2>::new();
70    }
71
72    #[test]
73    fn write_to_buffer() {
74        let mut buffer = RingBuffer::<2>::new();
75
76        buffer.write(1.0);
77    }
78
79    #[test]
80    fn read_from_buffer() {
81        let mut buffer = RingBuffer::<3>::new();
82        buffer.write(1.0);
83        buffer.write(2.0);
84        buffer.write(3.0);
85
86        assert_eq!(buffer.peek(0), 3.0);
87        assert_eq!(buffer.peek(-1), 2.0);
88        assert_eq!(buffer.peek(-2), 1.0);
89    }
90
91    #[test]
92    fn read_interpolated_from_buffer_with_positive_index() {
93        let mut buffer = RingBuffer::<3>::new();
94        buffer.write(1.0);
95        buffer.write(10.0);
96        buffer.write(0.0);
97
98        assert_relative_eq!(buffer.peek_interpolated(0.6), 0.6);
99    }
100
101    #[test]
102    fn read_interpolated_from_buffer_with_negative_index() {
103        let mut buffer = RingBuffer::<3>::new();
104        buffer.write(10.0);
105        buffer.write(1.0);
106        buffer.write(0.0);
107
108        assert_relative_eq!(buffer.peek_interpolated(-0.6), 0.6);
109    }
110
111    #[test]
112    fn cross_buffer_end_while_reading() {
113        let mut buffer = RingBuffer::<101>::new();
114        for x in 0..=100 {
115            buffer.write(x as f32);
116        }
117
118        assert_eq!(buffer.peek(0) as usize, 100);
119        assert_eq!(buffer.peek(-1) as usize, 100 - 1);
120    }
121}