use assume::assume;
pub struct DelayLine<const CHANNELS: usize> {
buffer: Vec<[f64; CHANNELS]>,
buffer_mask: usize,
write_pos: usize,
}
impl<const CHANNELS: usize> DelayLine<CHANNELS> {
pub fn new(max_size: usize) -> Self {
let buffer_frames = max_size.next_power_of_two();
let buffer = vec![[0.0; CHANNELS]; buffer_frames];
let buffer_mask = buffer_frames - 1;
let write_pos = 0;
Self {
buffer,
buffer_mask,
write_pos,
}
}
pub fn flush(&mut self) {
self.buffer.fill([0.0; CHANNELS]);
self.write_pos = 0;
}
pub fn process(&mut self, delay: usize, input: [f64; CHANNELS]) -> [f64; CHANNELS] {
debug_assert!(
delay < self.buffer.len() - 1,
"Delay must be < {} but is {}",
self.buffer.len() - 1,
delay
);
assume!(unsafe: self.buffer_mask < self.buffer.len());
self.write_pos &= self.buffer_mask;
self.buffer[self.write_pos] = input;
self.write_pos = (self.write_pos + 1) & self.buffer_mask;
if self.write_pos > delay {
self.write_pos = 0;
}
self.buffer[self.write_pos]
}
}
#[derive(Debug, Default)]
pub struct InterpolatedDelayLine<const CHANNELS: usize> {
buffer: Vec<[f64; CHANNELS]>,
buffer_mask: usize,
write_pos: usize,
}
impl<const CHANNELS: usize> InterpolatedDelayLine<CHANNELS> {
pub fn new(max_size: usize) -> Self {
let buffer_frames = max_size.next_power_of_two();
let buffer = vec![[0.0; CHANNELS]; buffer_frames];
let buffer_mask = buffer_frames - 1;
let write_pos = 0;
Self {
buffer,
buffer_mask,
write_pos,
}
}
pub fn flush(&mut self) {
self.buffer.fill([0.0; CHANNELS]);
self.write_pos = 0;
}
#[inline]
pub fn process(
&mut self,
input: [f32; CHANNELS],
feedback: f32,
delay: f32,
) -> [f32; CHANNELS] {
debug_assert!(
delay >= 0.0 && (delay.ceil() as usize) < self.buffer.len() - 1,
"Delay must be > 0 and < {} but is {}",
self.buffer.len() - 1,
delay
);
let read_pos = self.write_pos as f64 - delay as f64;
let read_pos_floor = read_pos.floor();
let fraction = read_pos - read_pos_floor;
let index1 = read_pos_floor as isize;
let index2 = index1 + 1;
let mut output = [0.0; CHANNELS];
assume!(unsafe: self.buffer_mask < self.buffer.len());
let read_idx1 = (index1 as usize) & self.buffer_mask;
let read_idx2 = (index2 as usize) & self.buffer_mask;
let frame1 = self.buffer[read_idx1];
let frame2 = self.buffer[read_idx2];
for ch in 0..CHANNELS {
let val1 = frame1[ch];
let val2 = frame2[ch];
output[ch] = (val1 + (val2 - val1) * fraction) as f32;
}
let write_sample_index = self.write_pos & self.buffer_mask;
let mut write_frame = [0.0; CHANNELS];
for ch in 0..CHANNELS {
write_frame[ch] = input[ch] as f64 + output[ch] as f64 * feedback as f64;
}
self.buffer[write_sample_index] = write_frame;
self.write_pos = (self.write_pos + 1) & self.buffer_mask;
output
}
}
#[derive(Debug, Default)]
pub struct LookupDelayLine<const CHANNELS: usize> {
buffer: Vec<[f64; CHANNELS]>,
write_pos: usize,
buffer_mask: usize,
delay_frames: usize,
peak_value: f64,
peak_pos: usize,
}
impl<const CHANNELS: usize> LookupDelayLine<CHANNELS> {
pub fn new(sample_rate: u32, delay_time: f32) -> Self {
let delay_frames = (delay_time * sample_rate as f32).ceil() as usize;
let (buffer, buffer_mask) = if delay_frames > 0 {
let buffer_frames = delay_frames.next_power_of_two();
(vec![[0.0; CHANNELS]; buffer_frames], buffer_frames - 1)
} else {
(Vec::new(), 0)
};
let write_pos = 0;
let peak_value = 0.0;
let peak_pos = 0;
Self {
buffer,
buffer_mask,
write_pos,
delay_frames,
peak_value,
peak_pos,
}
}
pub fn process(&mut self, input_frame: &[f32; CHANNELS]) -> [f32; CHANNELS] {
if self.delay_frames == 0 {
return *input_frame;
}
assume!(unsafe: self.buffer_mask < self.buffer.len());
let buffer_frames = self.buffer.len();
let read_frame_index =
(self.write_pos + buffer_frames - self.delay_frames) & self.buffer_mask;
let delayed_frame_f64 = self.buffer[read_frame_index];
let mut delayed_frame = [0.0; CHANNELS];
for ch in 0..CHANNELS {
delayed_frame[ch] = delayed_frame_f64[ch] as f32;
}
let write_frame_index = self.write_pos & self.buffer_mask;
let mut write_frame = [0.0; CHANNELS];
for ch in 0..CHANNELS {
write_frame[ch] = input_frame[ch] as f64;
}
self.buffer[write_frame_index] = write_frame;
let peak_expired = self.peak_pos == read_frame_index;
let new_peak = input_frame
.iter()
.fold(0.0f64, |max, &val| max.max(val.abs() as f64));
if new_peak >= self.peak_value {
self.peak_value = new_peak;
self.peak_pos = self.write_pos;
} else if peak_expired {
self.peak_value = 0.0;
let buffer_frames = self.buffer.len();
for i in 0..self.delay_frames {
let frame_index = (self.write_pos + buffer_frames - i) & self.buffer_mask;
let frame = self.buffer[frame_index];
let frame_peak = frame.iter().fold(0.0f64, |max, &val| max.max(val.abs()));
if frame_peak >= self.peak_value {
self.peak_value = frame_peak;
self.peak_pos = frame_index;
}
}
}
self.write_pos = (self.write_pos + 1) & self.buffer_mask;
delayed_frame
}
pub fn peak_value(&self) -> f32 {
self.peak_value as f32
}
}
pub struct AllpassDelayLine<const CHANNELS: usize> {
buffer: Vec<[f64; CHANNELS]>,
delay: usize,
write_pos: usize,
}
impl<const CHANNELS: usize> AllpassDelayLine<CHANNELS> {
pub fn new(max_size: usize) -> Self {
Self {
buffer: vec![[0.0; CHANNELS]; max_size],
delay: 0,
write_pos: 0,
}
}
pub fn flush(&mut self) {
self.buffer.fill([0.0; CHANNELS]);
self.write_pos = 0;
}
pub fn set_delay(&mut self, delay: usize) {
debug_assert!(
delay < self.buffer.len() - 1,
"Delay must be < {} but is {}",
self.buffer.len() - 1,
delay
);
self.delay = delay.min(self.buffer.len() - 1);
}
#[inline]
pub fn process(&mut self, input: [f64; CHANNELS]) -> [f64; CHANNELS] {
let mut read_pos = self.write_pos + 1;
if read_pos > self.delay {
read_pos = 0;
}
assume!(unsafe: read_pos < self.buffer.len());
assume!(unsafe: self.write_pos < self.buffer.len());
let delayed = self.buffer[read_pos];
let mut output = [0.0; CHANNELS];
let mut write_frame = [0.0; CHANNELS];
for ch in 0..CHANNELS {
let val_in = input[ch];
let buf = val_in - (delayed[ch] * 0.5);
write_frame[ch] = buf;
output[ch] = buf * 0.5;
}
self.buffer[self.write_pos] = write_frame;
self.write_pos += 1;
if self.write_pos > self.delay {
self.write_pos = 0;
}
let new_delayed = self.buffer[self.write_pos];
for ch in 0..CHANNELS {
output[ch] += new_delayed[ch];
}
output
}
}