extern crate alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU32, Ordering};
#[derive(Debug)]
pub struct DeviceRing {
cells: Box<[AtomicU32]>,
capacity_frames: usize,
channels: usize,
}
impl DeviceRing {
#[must_use]
pub fn new(capacity_frames: usize, channels: usize) -> Self {
assert!(
capacity_frames > 0,
"DeviceRing capacity_frames must be non-zero"
);
assert!(channels > 0, "DeviceRing channels must be non-zero");
let cell_count = capacity_frames * channels;
let mut cells = Vec::with_capacity(cell_count);
for _ in 0..cell_count {
cells.push(AtomicU32::new(0));
}
Self {
cells: cells.into_boxed_slice(),
capacity_frames,
channels,
}
}
#[inline]
#[must_use]
pub fn capacity_frames(&self) -> usize {
self.capacity_frames
}
#[inline]
#[must_use]
pub fn channels(&self) -> usize {
self.channels
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.cells.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
false
}
#[inline]
fn frame_offset(&self, start_frame: f64) -> usize {
let frame = start_frame.floor() as i64;
frame.rem_euclid(self.capacity_frames as i64) as usize
}
pub fn write_from(&self, start_frame: f64, src: &[f32]) {
if self.cells.is_empty() {
return;
}
let base = self.frame_offset(start_frame) * self.channels;
for (i, &sample) in src.iter().enumerate() {
let idx = (base + i) % self.cells.len();
self.cells[idx].store(sample.to_bits(), Ordering::Relaxed);
}
}
pub fn read_into(&self, start_frame: f64, dst: &mut [f32]) {
if self.cells.is_empty() {
dst.fill(0.0);
return;
}
let base = self.frame_offset(start_frame) * self.channels;
for (i, slot) in dst.iter_mut().enumerate() {
let idx = (base + i) % self.cells.len();
*slot = f32::from_bits(self.cells[idx].load(Ordering::Relaxed));
}
}
pub fn clear(&self) {
for cell in &self.cells {
cell.store(0, Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use static_assertions::assert_impl_all;
assert_impl_all!(DeviceRing: Send, Sync);
#[test]
fn new_ring_has_the_requested_geometry_and_is_silent() {
let ring = DeviceRing::new(8, 2);
assert_eq!(ring.capacity_frames(), 8);
assert_eq!(ring.channels(), 2);
assert_eq!(ring.len(), 16);
assert!(!ring.is_empty());
let mut out = [9.0_f32; 16];
ring.read_into(0.0, &mut out);
assert!(out.iter().all(|&s| s == 0.0), "a fresh ring is silent");
}
#[test]
#[should_panic(expected = "capacity_frames must be non-zero")]
fn zero_capacity_panics() {
let _ = DeviceRing::new(0, 2);
}
#[test]
#[should_panic(expected = "channels must be non-zero")]
fn zero_channels_panics() {
let _ = DeviceRing::new(8, 0);
}
#[test]
fn write_then_read_round_trips_at_the_same_sample_time() {
let ring = DeviceRing::new(16, 2);
let written = [0.1_f32, -0.2, 0.3, -0.4, 0.5, -0.6];
ring.write_from(4.0, &written);
let mut read = [0.0_f32; 6];
ring.read_into(4.0, &mut read);
assert_eq!(read, written);
}
#[test]
fn writes_wrap_at_the_ring_boundary() {
let ring = DeviceRing::new(4, 1);
ring.write_from(3.0, &[10.0, 11.0, 12.0, 13.0, 14.0, 15.0]);
let mut read = [0.0_f32; 4];
ring.read_into(0.0, &mut read);
assert_eq!(read, [15.0, 12.0, 13.0, 14.0]);
}
#[test]
fn sample_time_reduces_modulo_capacity() {
let ring = DeviceRing::new(8, 1);
ring.write_from(2.0, &[42.0]);
let mut read = [0.0_f32; 1];
ring.read_into(10.0, &mut read);
assert_eq!(read[0], 42.0);
ring.read_into(2.0 + 8.0 * 8.0, &mut read);
assert_eq!(read[0], 42.0);
}
#[test]
fn negative_sample_time_is_floored_into_range() {
let ring = DeviceRing::new(8, 1);
ring.write_from(-1.0, &[7.0]);
let mut read = [0.0_f32; 1];
ring.read_into(7.0, &mut read);
assert_eq!(read[0], 7.0);
}
#[test]
fn fractional_sample_time_floors_to_the_frame() {
let ring = DeviceRing::new(8, 1);
ring.write_from(3.0, &[3.0]);
let mut read = [0.0_f32; 1];
ring.read_into(3.9, &mut read);
assert_eq!(read[0], 3.0);
}
#[test]
fn clear_silences_the_whole_ring() {
let ring = DeviceRing::new(4, 2);
ring.write_from(0.0, &[1.0; 8]);
ring.clear();
let mut read = [9.0_f32; 8];
ring.read_into(0.0, &mut read);
assert!(read.iter().all(|&s| s == 0.0));
}
#[test]
fn channels_interleave_correctly() {
let ring = DeviceRing::new(4, 2);
ring.write_from(1.0, &[1.0, 2.0]);
let mut read = [0.0_f32; 8];
ring.read_into(0.0, &mut read);
assert_eq!(read, [0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 0.0]);
}
#[test]
fn a_loopback_round_trip_across_threads() {
use std::sync::Arc;
use std::thread;
let ring = Arc::new(DeviceRing::new(1024, 1));
let writer_ring = Arc::clone(&ring);
let writer = thread::spawn(move || {
for frame in 0..1024 {
writer_ring.write_from(frame as f64, &[frame as f32]);
}
});
writer.join().unwrap();
let mut read = [0.0_f32; 1024];
ring.read_into(0.0, &mut read);
for (frame, &sample) in read.iter().enumerate() {
assert_eq!(sample, frame as f32);
}
}
}