// 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) } }