// This is a tapped delay line delay meant to be simple for Actuate!
// Stock synth delays are pretty ok :)
// Ardura 2023
use nih_plug::params::enums::Enum;
use serde::{Deserialize, Serialize};
#[derive(Clone, Enum, PartialEq, Serialize, Deserialize)]
pub enum DelaySnapValues {
Whole,
WholeD,
WholeT,
Half,
HalfD,
HalfT,
Quarter,
QuarterD,
QuarterT,
Eighth,
EighthD,
EighthT,
Sixteen,
SixteenD,
SixteenT,
ThirtySecond,
ThirtySecondD,
ThirtySecondT,
}
#[derive(Clone, Enum, PartialEq, Serialize, Deserialize)]
pub enum DelayType {
Stereo,
PingPongL,
PingPongR,
}
#[derive(Clone)]
pub(crate) struct Delay {
sample_rate: f32,
bpm: f32,
length: DelaySnapValues,
delay_buffer_l: Vec<f32>,
delay_buffer_r: Vec<f32>,
delay_length: usize,
delay_type: DelayType,
feedback: f32,
current_index: usize,
}
impl Delay {
pub fn new(sample_rate: f32, bpm: f32, length: DelaySnapValues, feedback: f32) -> Self {
// Recalculate delay length based on the new size
let divisor: f32 = match length {
DelaySnapValues::Whole => 1.0,
DelaySnapValues::WholeD => 1.0 * 1.5,
DelaySnapValues::WholeT => 1.0 / 3.0,
DelaySnapValues::Half => 2.0,
DelaySnapValues::HalfD => 2.0 * 1.5,
DelaySnapValues::HalfT => 2.0 / 3.0,
DelaySnapValues::Quarter => 4.0,
DelaySnapValues::QuarterD => 4.0 * 1.5,
DelaySnapValues::QuarterT => 4.0 / 3.0,
DelaySnapValues::Eighth => 8.0,
DelaySnapValues::EighthD => 8.0 * 1.5,
DelaySnapValues::EighthT => 8.0 / 3.0,
DelaySnapValues::Sixteen => 16.0,
DelaySnapValues::SixteenD => 16.0 * 1.5,
DelaySnapValues::SixteenT => 16.0 / 3.0,
DelaySnapValues::ThirtySecond => 32.0,
DelaySnapValues::ThirtySecondD => 32.0 * 1.5,
DelaySnapValues::ThirtySecondT => 32.0 / 3.0,
};
// Calculate beats per second
let bps = bpm / 60.0;
// Calculate samples per beat
let samples_per_beat = sample_rate / bps;
// Calculate samples per note type
let samples_per_note_type = samples_per_beat * (4.0 / divisor);
let delay_length = samples_per_note_type as usize;
// Create delay buffers for left and right channels initialized with zeros
let delay_buffer_l = vec![0.0; delay_length];
let delay_buffer_r = vec![0.0; delay_length];
Delay {
sample_rate,
bpm: 138.0,
length,
delay_buffer_l,
delay_buffer_r,
delay_length,
delay_type: DelayType::Stereo,
feedback,
current_index: 0,
}
}
pub fn set_sample_rate(&mut self, sample_rate: f32, bpm: f32) {
if self.bpm != bpm {
self.bpm = bpm;
}
if self.sample_rate != sample_rate {
self.sample_rate = sample_rate;
// Recalculate delay length based on the new sample rate
let length =
self.calculate_samples_per_note_type(Self::get_divisor(self.length.clone()));
// Recalculate delay length based on the new size
self.delay_length = length as usize;
// Resize and reset the delay buffers
self.delay_buffer_l = vec![0.0; self.delay_length];
self.delay_buffer_r = vec![0.0; self.delay_length];
self.current_index = 0;
}
}
fn calculate_samples_per_note_type(&mut self, note_type_value: f32) -> f32 {
// Calculate beats per second
let bps = self.bpm / 60.0;
// Calculate samples per beat
let samples_per_beat = self.sample_rate / bps;
// Calculate samples per note type
let samples_per_note_type = samples_per_beat * (4.0 / note_type_value);
samples_per_note_type
}
fn get_divisor(length: DelaySnapValues) -> f32 {
let divisor: f32 = match length {
DelaySnapValues::Whole => 1.0,
DelaySnapValues::WholeD => 1.0 * 1.5,
DelaySnapValues::WholeT => 1.0 / 3.0,
DelaySnapValues::Half => 2.0,
DelaySnapValues::HalfD => 2.0 * 1.5,
DelaySnapValues::HalfT => 2.0 / 3.0,
DelaySnapValues::Quarter => 4.0,
DelaySnapValues::QuarterD => 4.0 * 1.5,
DelaySnapValues::QuarterT => 4.0 / 3.0,
DelaySnapValues::Eighth => 8.0,
DelaySnapValues::EighthD => 8.0 * 1.5,
DelaySnapValues::EighthT => 8.0 / 3.0,
DelaySnapValues::Sixteen => 16.0,
DelaySnapValues::SixteenD => 16.0 * 1.5,
DelaySnapValues::SixteenT => 16.0 / 3.0,
DelaySnapValues::ThirtySecond => 32.0,
DelaySnapValues::ThirtySecondD => 32.0 * 1.5,
DelaySnapValues::ThirtySecondT => 32.0 / 3.0,
};
divisor
}
pub fn set_length(&mut self, length: DelaySnapValues) {
if self.length != length {
let new_length =
self.calculate_samples_per_note_type(Self::get_divisor(self.length.clone()));
// Recalculate delay length based on the new size
self.delay_length = new_length as usize;
// Resize and reset the delay buffers
self.delay_buffer_l = vec![0.0; self.delay_length];
self.delay_buffer_r = vec![0.0; self.delay_length];
self.current_index = 0;
//Reassign
self.length = length;
}
}
pub fn set_type(&mut self, delay_type: DelayType) {
self.delay_type = delay_type;
}
pub fn set_feedback(&mut self, feedback: f32) {
self.feedback = feedback;
}
pub fn process(&mut self, input_l: f32, input_r: f32, amount: f32) -> (f32, f32) {
// Get the current values from the delay lines
let delayed_sample_l: f32 = self.delay_buffer_l[self.current_index];
let delayed_sample_r: f32 = self.delay_buffer_r[self.current_index];
// Calculate the left and right outputs
let mut output_l: f32;
let mut output_r: f32;
output_l = input_l + self.feedback * delayed_sample_l;
output_r = input_r + self.feedback * delayed_sample_r;
let delay_shift_l: usize;
let delay_shift_r: usize;
match self.delay_type {
DelayType::Stereo => {
delay_shift_l = 0;
delay_shift_r = 0;
}
DelayType::PingPongL => {
delay_shift_l = self.delay_length / 2;
delay_shift_r = 0;
}
DelayType::PingPongR => {
delay_shift_r = self.delay_length / 2;
delay_shift_l = 0;
}
}
// Store the outputs in the delay lines
if self.delay_buffer_l.get(self.current_index + delay_shift_l) != None {
self.delay_buffer_l[self.current_index + delay_shift_l] = output_l;
} else {
self.delay_buffer_l[(self.current_index + delay_shift_l) % self.delay_length] = output_l;
}
if self.delay_buffer_r.get(self.current_index + delay_shift_r) != None {
self.delay_buffer_r[self.current_index + delay_shift_r] = output_r;
} else {
self.delay_buffer_r[(self.current_index + delay_shift_r) % self.delay_length] = output_r;
}
// Move the index to the next position in the delay lines
self.current_index = (self.current_index + 1) % self.delay_length;
// Return the left and right outputs
output_l = input_l * (1.0 - amount) + output_l * amount;
output_r = input_r * (1.0 - amount) + output_r * amount;
(output_l, output_r)
}
}