⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/audio/
buffer.rs

1//! Ring buffer for audio data transport
2//!
3//! Provides lock-free single-producer single-consumer ring buffers for
4//! efficient audio data transfer between producer (client) and consumer
5//! (mixer/pipeline) threads.
6//!
7//! ## Design
8//!
9//! `AudioRingBuffer` is a byte-level ring buffer using atomic read/write
10//! positions for lock-free operation in the SPSC case. `SharedAudioBuffer`
11//! wraps it with a `Mutex` to support multi-producer scenarios and provides
12//! a typed i16 sample interface.
13
14#![allow(dead_code)]
15
16use alloc::vec::Vec;
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use crate::audio::AudioConfig;
20
21// ============================================================================
22// Audio Ring Buffer (byte-level, SPSC lock-free)
23// ============================================================================
24
25/// Lock-free single-producer single-consumer ring buffer for audio data
26///
27/// Uses atomic read/write positions to allow concurrent access from one
28/// producer and one consumer without locking.
29pub struct AudioRingBuffer {
30    /// Backing storage for audio data
31    data: Vec<u8>,
32    /// Current read position (byte offset, wraps around capacity)
33    read_pos: AtomicU32,
34    /// Current write position (byte offset, wraps around capacity)
35    write_pos: AtomicU32,
36    /// Total capacity in bytes
37    capacity: u32,
38    /// Size of one audio frame in bytes (channels * bytes_per_sample)
39    frame_size: u16,
40}
41
42impl AudioRingBuffer {
43    /// Create a new ring buffer with the given capacity in frames
44    ///
45    /// The actual byte capacity is `capacity_frames * frame_size`.
46    pub fn new(capacity_frames: u32, frame_size: u16) -> Self {
47        let byte_capacity = capacity_frames * frame_size as u32;
48        Self {
49            data: alloc::vec![0u8; byte_capacity as usize],
50            read_pos: AtomicU32::new(0),
51            write_pos: AtomicU32::new(0),
52            capacity: byte_capacity,
53            frame_size,
54        }
55    }
56
57    /// Write data into the ring buffer
58    ///
59    /// Returns the number of bytes actually written. May be less than
60    /// `data.len()` if the buffer is nearly full.
61    pub fn write(&self, data: &[u8]) -> usize {
62        let write = self.write_pos.load(Ordering::Acquire);
63        let read = self.read_pos.load(Ordering::Acquire);
64        let cap = self.capacity;
65
66        // Available space: capacity - 1 - used (reserve 1 byte to distinguish
67        // full from empty)
68        let used = if write >= read {
69            write - read
70        } else {
71            cap - read + write
72        };
73        let available = (cap - 1 - used) as usize;
74        let to_write = data.len().min(available);
75
76        if to_write == 0 {
77            return 0;
78        }
79
80        let w = write as usize;
81        let c = cap as usize;
82
83        // Write in one or two chunks depending on wrap-around
84        let first_chunk = (c - w).min(to_write);
85        let second_chunk = to_write - first_chunk;
86
87        // SAFETY: We only write within bounds of self.data, and the atomic
88        // positions ensure the reader does not access these regions until
89        // write_pos is updated.
90        let data_ptr = self.data.as_ptr() as *mut u8;
91        unsafe {
92            core::ptr::copy_nonoverlapping(data.as_ptr(), data_ptr.add(w), first_chunk);
93            if second_chunk > 0 {
94                core::ptr::copy_nonoverlapping(
95                    data.as_ptr().add(first_chunk),
96                    data_ptr,
97                    second_chunk,
98                );
99            }
100        }
101
102        let new_write = ((w + to_write) % c) as u32;
103        self.write_pos.store(new_write, Ordering::Release);
104
105        to_write
106    }
107
108    /// Read data from the ring buffer
109    ///
110    /// Returns the number of bytes actually read. May be less than
111    /// `output.len()` if the buffer does not contain enough data.
112    pub fn read(&self, output: &mut [u8]) -> usize {
113        let write = self.write_pos.load(Ordering::Acquire);
114        let read = self.read_pos.load(Ordering::Acquire);
115        let cap = self.capacity;
116
117        let used = if write >= read {
118            (write - read) as usize
119        } else {
120            (cap - read + write) as usize
121        };
122        let to_read = output.len().min(used);
123
124        if to_read == 0 {
125            return 0;
126        }
127
128        let r = read as usize;
129        let c = cap as usize;
130
131        let first_chunk = (c - r).min(to_read);
132        let second_chunk = to_read - first_chunk;
133
134        // SAFETY: We only read within bounds of self.data, and the atomic
135        // positions ensure the writer does not overwrite these regions until
136        // read_pos is updated.
137        unsafe {
138            core::ptr::copy_nonoverlapping(
139                self.data.as_ptr().add(r),
140                output.as_mut_ptr(),
141                first_chunk,
142            );
143            if second_chunk > 0 {
144                core::ptr::copy_nonoverlapping(
145                    self.data.as_ptr(),
146                    output.as_mut_ptr().add(first_chunk),
147                    second_chunk,
148                );
149            }
150        }
151
152        let new_read = ((r + to_read) % c) as u32;
153        self.read_pos.store(new_read, Ordering::Release);
154
155        to_read
156    }
157
158    /// Number of frames available to read
159    pub fn available_read(&self) -> u32 {
160        let write = self.write_pos.load(Ordering::Acquire);
161        let read = self.read_pos.load(Ordering::Acquire);
162        let cap = self.capacity;
163
164        let used_bytes = if write >= read {
165            write - read
166        } else {
167            cap - read + write
168        };
169
170        if self.frame_size > 0 {
171            used_bytes / self.frame_size as u32
172        } else {
173            0
174        }
175    }
176
177    /// Number of frames available to write
178    pub fn available_write(&self) -> u32 {
179        let write = self.write_pos.load(Ordering::Acquire);
180        let read = self.read_pos.load(Ordering::Acquire);
181        let cap = self.capacity;
182
183        let used_bytes = if write >= read {
184            write - read
185        } else {
186            cap - read + write
187        };
188        let available_bytes = cap - 1 - used_bytes;
189
190        if self.frame_size > 0 {
191            available_bytes / self.frame_size as u32
192        } else {
193            0
194        }
195    }
196
197    /// Clear the buffer (reset read/write positions)
198    pub fn clear(&mut self) {
199        self.read_pos.store(0, Ordering::Release);
200        self.write_pos.store(0, Ordering::Release);
201    }
202
203    /// Check if the buffer is empty
204    pub fn is_empty(&self) -> bool {
205        self.read_pos.load(Ordering::Acquire) == self.write_pos.load(Ordering::Acquire)
206    }
207
208    /// Check if the buffer is full (only 1 byte of slack remaining)
209    pub fn is_full(&self) -> bool {
210        let write = self.write_pos.load(Ordering::Acquire);
211        let read = self.read_pos.load(Ordering::Acquire);
212        let cap = self.capacity;
213        let used = if write >= read {
214            write - read
215        } else {
216            cap - read + write
217        };
218        used >= cap - 1
219    }
220}
221
222// ============================================================================
223// Shared Audio Buffer (mutex-protected, typed i16 interface)
224// ============================================================================
225
226/// Mutex-protected audio ring buffer with typed sample interface
227///
228/// Wraps an `AudioRingBuffer` with a spin mutex to support multiple
229/// producers writing interleaved i16 samples.
230pub struct SharedAudioBuffer {
231    /// Inner ring buffer, protected by a spin mutex
232    inner: spin::Mutex<AudioRingBuffer>,
233    /// Audio configuration for this buffer
234    config: AudioConfig,
235}
236
237impl SharedAudioBuffer {
238    /// Create a new shared audio buffer
239    ///
240    /// # Arguments
241    /// * `capacity_frames` - Number of audio frames the buffer can hold
242    /// * `config` - Audio configuration (determines frame size)
243    pub fn new(capacity_frames: u32, config: AudioConfig) -> Self {
244        let frame_size = config.frame_size();
245        Self {
246            inner: spin::Mutex::new(AudioRingBuffer::new(capacity_frames, frame_size)),
247            config,
248        }
249    }
250
251    /// Write i16 samples into the buffer
252    ///
253    /// Converts the sample slice to bytes and writes into the ring buffer.
254    /// Returns the number of samples actually written.
255    pub fn write_samples(&self, samples: &[i16]) -> usize {
256        // SAFETY: Reinterpreting &[i16] as &[u8]; i16 alignment >= u8, length scaled by
257        // 2.
258        let bytes: &[u8] = unsafe {
259            core::slice::from_raw_parts(samples.as_ptr() as *const u8, samples.len() * 2)
260        };
261        let ring = self.inner.lock();
262        let bytes_written = ring.write(bytes);
263        bytes_written / 2
264    }
265
266    /// Read i16 samples from the buffer
267    ///
268    /// Reads bytes from the ring buffer and reinterprets as i16 samples.
269    /// Returns the number of samples actually read.
270    pub fn read_samples(&self, output: &mut [i16]) -> usize {
271        // SAFETY: Reinterpreting &mut [i16] as &mut [u8]; i16 alignment >= u8, length
272        // scaled by 2.
273        let bytes: &mut [u8] = unsafe {
274            core::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut u8, output.len() * 2)
275        };
276        let ring = self.inner.lock();
277        let bytes_read = ring.read(bytes);
278        bytes_read / 2
279    }
280
281    /// Number of frames available to read
282    pub fn available_read_frames(&self) -> u32 {
283        self.inner.lock().available_read()
284    }
285
286    /// Number of frames available to write
287    pub fn available_write_frames(&self) -> u32 {
288        self.inner.lock().available_write()
289    }
290
291    /// Check if the buffer is empty
292    pub fn is_empty(&self) -> bool {
293        self.inner.lock().is_empty()
294    }
295
296    /// Get the audio configuration for this buffer
297    pub fn config(&self) -> &AudioConfig {
298        &self.config
299    }
300}
301
302// ============================================================================
303// Tests
304// ============================================================================
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309    use crate::audio::SampleFormat;
310
311    fn test_config() -> AudioConfig {
312        AudioConfig {
313            sample_rate: 48000,
314            channels: 1,
315            format: SampleFormat::S16Le,
316            buffer_frames: 256,
317        }
318    }
319
320    #[test]
321    fn test_ring_buffer_write_read() {
322        let ring = AudioRingBuffer::new(64, 2);
323        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
324
325        let written = ring.write(&data);
326        assert_eq!(written, 8);
327
328        let mut output = [0u8; 8];
329        let read = ring.read(&mut output);
330        assert_eq!(read, 8);
331        assert_eq!(output, data);
332    }
333
334    #[test]
335    fn test_ring_buffer_wrap_around() {
336        // Small buffer to force wrap-around
337        let ring = AudioRingBuffer::new(4, 1); // 4 bytes capacity
338
339        // Write 3 bytes (leaving 1 byte slack)
340        let data1 = [10u8, 20, 30];
341        let written1 = ring.write(&data1);
342        assert_eq!(written1, 3);
343
344        // Read 2 bytes to free space
345        let mut out1 = [0u8; 2];
346        let read1 = ring.read(&mut out1);
347        assert_eq!(read1, 2);
348        assert_eq!(out1, [10, 20]);
349
350        // Write 2 more bytes (should wrap around)
351        let data2 = [40u8, 50];
352        let written2 = ring.write(&data2);
353        assert_eq!(written2, 2);
354
355        // Read remaining 3 bytes
356        let mut out2 = [0u8; 3];
357        let read2 = ring.read(&mut out2);
358        assert_eq!(read2, 3);
359        assert_eq!(out2, [30, 40, 50]);
360    }
361
362    #[test]
363    fn test_ring_buffer_overflow() {
364        let ring = AudioRingBuffer::new(4, 1); // 4 bytes capacity
365
366        // Try to write more than capacity
367        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
368        let written = ring.write(&data);
369        // Only 3 bytes should fit (capacity - 1 for full/empty distinction)
370        assert_eq!(written, 3);
371
372        assert!(ring.is_full());
373    }
374
375    #[test]
376    fn test_ring_buffer_empty_read() {
377        let ring = AudioRingBuffer::new(32, 2);
378
379        assert!(ring.is_empty());
380
381        let mut output = [0u8; 16];
382        let read = ring.read(&mut output);
383        assert_eq!(read, 0);
384    }
385
386    #[test]
387    fn test_ring_buffer_available() {
388        let ring = AudioRingBuffer::new(8, 2); // 8 frames * 2 bytes = 16 bytes capacity
389
390        assert_eq!(ring.available_read(), 0);
391        assert_eq!(ring.available_write(), 7); // capacity - 1 = 15 bytes = 7 frames
392
393        let data = [0u8; 6]; // 3 frames
394        ring.write(&data);
395
396        assert_eq!(ring.available_read(), 3);
397        assert_eq!(ring.available_write(), 4); // 15-6=9 bytes = 4 frames
398    }
399
400    #[test]
401    fn test_shared_buffer_samples() {
402        let config = test_config();
403        let buf = SharedAudioBuffer::new(64, config);
404
405        let samples = [100i16, 200, 300, 400];
406        let written = buf.write_samples(&samples);
407        assert_eq!(written, 4);
408
409        let mut output = [0i16; 4];
410        let read = buf.read_samples(&mut output);
411        assert_eq!(read, 4);
412        assert_eq!(output, samples);
413    }
414
415    #[test]
416    fn test_shared_buffer_partial_read() {
417        let config = test_config();
418        let buf = SharedAudioBuffer::new(64, config);
419
420        let samples = [10i16, 20, 30];
421        buf.write_samples(&samples);
422
423        // Read only 2 samples
424        let mut output = [0i16; 2];
425        let read = buf.read_samples(&mut output);
426        assert_eq!(read, 2);
427        assert_eq!(output, [10, 20]);
428
429        // Read the remaining 1 sample
430        let mut output2 = [0i16; 4];
431        let read2 = buf.read_samples(&mut output2);
432        assert_eq!(read2, 1);
433        assert_eq!(output2[0], 30);
434    }
435
436    #[test]
437    fn test_shared_buffer_empty() {
438        let config = test_config();
439        let buf = SharedAudioBuffer::new(64, config);
440
441        assert!(buf.is_empty());
442        assert_eq!(buf.available_read_frames(), 0);
443    }
444}