mod builder;
mod handle;
pub use builder::*;
pub use handle::*;
use std::time::Duration;
use crate::{
Decibels, Mix, Parameter,
command::{ValueChangeCommand, read_commands_into_parameters},
command_writers_and_readers,
frame::Frame,
info::Info,
};
use super::Effect;
struct Delay {
command_readers: CommandReaders,
delay_time: Duration,
feedback: Parameter<Decibels>,
mix: Parameter<Mix>,
buffer: Vec<Frame>,
feedback_effects: Vec<Box<dyn Effect>>,
temp_buffer: Vec<Frame>,
}
impl Delay {
#[must_use]
fn new(builder: DelayBuilder, command_readers: CommandReaders) -> Self {
Self {
command_readers,
delay_time: builder.delay_time,
feedback: Parameter::new(builder.feedback, Decibels(-6.0)),
mix: Parameter::new(builder.mix, Mix(0.5)),
buffer: Vec::with_capacity(0),
feedback_effects: builder.feedback_effects,
temp_buffer: vec![],
}
}
}
impl Effect for Delay {
fn init(&mut self, sample_rate: u32, internal_buffer_size: usize) {
let delay_time_frames = (self.delay_time.as_secs_f64() * sample_rate as f64) as usize;
self.buffer = vec![Frame::ZERO; delay_time_frames];
self.temp_buffer = vec![Frame::ZERO; internal_buffer_size];
for effect in &mut self.feedback_effects {
effect.init(sample_rate, internal_buffer_size);
}
}
fn on_change_sample_rate(&mut self, sample_rate: u32) {
let delay_time_frames = (self.delay_time.as_secs_f64() * sample_rate as f64) as usize;
self.buffer = vec![Frame::ZERO; delay_time_frames];
for effect in &mut self.feedback_effects {
effect.on_change_sample_rate(sample_rate);
}
}
fn on_start_processing(&mut self) {
read_commands_into_parameters!(self, feedback, mix);
for effect in &mut self.feedback_effects {
effect.on_start_processing();
}
}
fn process(&mut self, input: &mut [Frame], dt: f64, info: &Info) {
self.feedback.update(dt * input.len() as f64, info);
self.mix.update(dt * input.len() as f64, info);
for input in input.chunks_mut(self.buffer.len()) {
let num_frames = input.len();
self.temp_buffer[..input.len()].copy_from_slice(&self.buffer[..input.len()]);
for effect in &mut self.feedback_effects {
effect.process(&mut self.temp_buffer[..input.len()], dt, info);
}
for (i, frame) in self.temp_buffer[..input.len()].iter_mut().enumerate() {
let time_in_chunk = (i + 1) as f64 / num_frames as f64;
let feedback = self.feedback.interpolated_value(time_in_chunk);
*frame *= feedback.as_amplitude();
}
self.buffer.copy_within(input.len().., 0);
let write_range = self.buffer.len() - input.len()..;
for ((out, input), read) in self.buffer[write_range]
.iter_mut()
.zip(input.iter())
.zip(&mut self.temp_buffer[..input.len()])
{
*out = *input + *read;
}
for (i, frame) in input.iter_mut().enumerate() {
let time_in_chunk = (i + 1) as f64 / num_frames as f64;
let mix = self.mix.interpolated_value(time_in_chunk).0.clamp(0.0, 1.0);
*frame = self.temp_buffer[i] * mix.sqrt() + *frame * (1.0 - mix).sqrt()
}
}
}
}
command_writers_and_readers! {
set_feedback: ValueChangeCommand<Decibels>,
set_mix: ValueChangeCommand<Mix>,
}