use crate::buffer::Buffer;
use crate::math::Transcendental;
#[derive(Debug)]
pub struct TapeLoop<T> {
buffer: Box<[T]>,
capacity: usize,
write_pos: usize,
}
impl<T: Transcendental> TapeLoop<T> {
pub fn new(capacity: usize) -> Option<Self> {
if capacity == 0 {
return None;
}
let mut vec = Vec::with_capacity(capacity);
for _ in 0..capacity {
vec.push(T::ZERO);
}
Some(Self {
buffer: vec.into_boxed_slice(),
capacity,
write_pos: 0,
})
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn write_pos(&self) -> usize {
self.write_pos
}
#[inline(always)]
pub fn write(&mut self, sample: T) {
self.buffer[self.write_pos] = sample;
self.write_pos = (self.write_pos + 1) % self.capacity;
}
#[inline(always)]
pub fn read(&self, delay: usize) -> T {
let d = delay.min(self.capacity - 1);
let read_pos = if self.write_pos > d {
self.write_pos - 1 - d
} else {
self.capacity + self.write_pos - 1 - d
};
self.buffer[read_pos]
}
#[inline(always)]
pub fn read_interpolated(&self, delay: f64) -> T {
let d = delay as usize;
let frac = T::from_f64(delay.fract());
let s1 = self.read(d);
let s2 = self.read(d + 1);
s1 + (s2 - s1) * frac
}
#[inline(always)]
pub fn write_block(&mut self, block: &[T]) {
let len = block.len().min(self.capacity);
for (i, &b) in block.iter().enumerate().take(len) {
self.buffer[(self.write_pos + i) % self.capacity] = b;
}
self.write_pos = (self.write_pos + len) % self.capacity;
}
#[inline(always)]
pub fn read_block(&self, delay: usize, output: &mut [T]) {
let len = output.len().min(self.capacity);
let d = delay.min(self.capacity - 1);
for (i, out) in output.iter_mut().enumerate().take(len) {
*out = self.read(d + len - 1 - i);
}
}
pub fn fill(&mut self, value: T) {
for slot in self.buffer.iter_mut() {
*slot = value;
}
}
pub fn clear(&mut self) {
for slot in self.buffer.iter_mut() {
*slot = T::ZERO;
}
self.write_pos = 0;
}
}
impl<T: Transcendental> Buffer<T> for TapeLoop<T> {
fn capacity(&self) -> usize {
self.capacity
}
fn len(&self) -> usize {
self.capacity
}
fn as_slice(&self) -> &[T] {
&self.buffer
}
fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.buffer
}
fn fill(&mut self, value: T) {
for slot in self.buffer.iter_mut() {
*slot = value;
}
}
fn copy_from(&mut self, src: &[T]) {
let len = src.len().min(self.capacity);
self.buffer[..len].copy_from_slice(&src[..len]);
}
fn clear(&mut self) {
for slot in self.buffer.iter_mut() {
*slot = T::ZERO;
}
self.write_pos = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tape_basic_write_read() {
let mut tape = TapeLoop::<f32>::new(1024).unwrap();
tape.write(1.0);
tape.write(2.0);
tape.write(3.0);
assert_eq!(tape.read(0), 3.0);
assert_eq!(tape.read(1), 2.0);
assert_eq!(tape.read(2), 1.0);
}
#[test]
fn test_tape_wraparound() {
let mut tape = TapeLoop::<f32>::new(4).unwrap();
for i in 0..10 {
tape.write(i as f32);
}
assert_eq!(tape.read(0), 9.0);
assert_eq!(tape.read(1), 8.0);
assert_eq!(tape.read(2), 7.0);
assert_eq!(tape.read(3), 6.0);
}
#[test]
fn test_tape_block_ops() {
let mut tape = TapeLoop::<f32>::new(64).unwrap();
let block = [1.0f32; 64];
tape.write_block(&block);
let mut out = [0.0f32; 64];
tape.read_block(63, &mut out);
assert_eq!(out[0], 1.0);
}
#[test]
fn test_tape_large_capacity() {
let tape = TapeLoop::<f32>::new(1_000_000).unwrap();
assert_eq!(tape.capacity(), 1_000_000);
}
#[test]
fn test_tape_zero_capacity() {
assert!(TapeLoop::<f32>::new(0).is_none());
}
#[test]
fn test_read_interpolated() {
let mut tape = TapeLoop::<f32>::new(1024).unwrap();
tape.write(0.0);
tape.write(1.0);
let v = tape.read_interpolated(0.5);
assert!((v - 0.5).abs() < 0.01);
}
}