mod builder;
mod handle;
pub use builder::*;
pub use handle::*;
use crate::{
Mix, Parameter,
command::{ValueChangeCommand, read_commands_into_parameters},
command_writers_and_readers,
effect::Effect,
frame::Frame,
info::Info,
};
use all_pass::AllPassFilter;
use comb::CombFilter;
mod all_pass;
mod comb;
const NUM_COMB_FILTERS: usize = 8;
const NUM_ALL_PASS_FILTERS: usize = 4;
const GAIN: f32 = 0.015;
const STEREO_SPREAD: usize = 23;
#[derive(Debug)]
enum ReverbState {
Uninitialized,
Initialized {
comb_filters: [(CombFilter, CombFilter); NUM_COMB_FILTERS],
all_pass_filters: [(AllPassFilter, AllPassFilter); NUM_ALL_PASS_FILTERS],
},
}
struct Reverb {
command_readers: CommandReaders,
feedback: Parameter,
damping: Parameter,
stereo_width: Parameter,
mix: Parameter<Mix>,
state: ReverbState,
}
impl Reverb {
#[must_use]
fn new(settings: ReverbBuilder, command_readers: CommandReaders) -> Self {
Self {
command_readers,
feedback: Parameter::new(settings.feedback, 0.9),
damping: Parameter::new(settings.damping, 0.1),
stereo_width: Parameter::new(settings.stereo_width, 1.0),
mix: Parameter::new(settings.mix, Mix(0.5)),
state: ReverbState::Uninitialized,
}
}
fn init_filters(&mut self, sample_rate: u32) {
const REFERENCE_SAMPLE_RATE: u32 = 44100;
let adjust_buffer_size = |buffer_size: usize| -> usize {
let sample_rate_factor = (sample_rate as f64) / (REFERENCE_SAMPLE_RATE as f64);
((buffer_size as f64) * sample_rate_factor) as usize
};
self.state = ReverbState::Initialized {
comb_filters: [
(
CombFilter::new(adjust_buffer_size(1116)),
CombFilter::new(adjust_buffer_size(1116 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1188)),
CombFilter::new(adjust_buffer_size(1188 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1277)),
CombFilter::new(adjust_buffer_size(1277 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1356)),
CombFilter::new(adjust_buffer_size(1356 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1422)),
CombFilter::new(adjust_buffer_size(1422 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1491)),
CombFilter::new(adjust_buffer_size(1491 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1557)),
CombFilter::new(adjust_buffer_size(1557 + STEREO_SPREAD)),
),
(
CombFilter::new(adjust_buffer_size(1617)),
CombFilter::new(adjust_buffer_size(1617 + STEREO_SPREAD)),
),
],
all_pass_filters: [
(
AllPassFilter::new(adjust_buffer_size(556)),
AllPassFilter::new(adjust_buffer_size(556 + STEREO_SPREAD)),
),
(
AllPassFilter::new(adjust_buffer_size(441)),
AllPassFilter::new(adjust_buffer_size(441 + STEREO_SPREAD)),
),
(
AllPassFilter::new(adjust_buffer_size(341)),
AllPassFilter::new(adjust_buffer_size(341 + STEREO_SPREAD)),
),
(
AllPassFilter::new(adjust_buffer_size(225)),
AllPassFilter::new(adjust_buffer_size(225 + STEREO_SPREAD)),
),
],
}
}
}
impl Effect for Reverb {
fn init(&mut self, sample_rate: u32, _internal_buffer_size: usize) {
self.init_filters(sample_rate);
}
fn on_change_sample_rate(&mut self, sample_rate: u32) {
self.init_filters(sample_rate);
}
fn on_start_processing(&mut self) {
read_commands_into_parameters!(self, feedback, damping, stereo_width, mix);
}
fn process(&mut self, input: &mut [Frame], dt: f64, info: &Info) {
if let ReverbState::Initialized {
comb_filters,
all_pass_filters,
} = &mut self.state
{
self.feedback.update(dt * input.len() as f64, info);
self.damping.update(dt * input.len() as f64, info);
self.stereo_width.update(dt * input.len() as f64, info);
self.mix.update(dt * input.len() as f64, info);
let feedback = self.feedback.value() as f32;
let damping = self.damping.value() as f32;
let num_frames = input.len();
for (i, frame) in input.iter_mut().enumerate() {
let time_in_chunk = (i + 1) as f64 / num_frames as f64;
let stereo_width = self.stereo_width.interpolated_value(time_in_chunk) as f32;
let mix = self.mix.interpolated_value(time_in_chunk).0.clamp(0.0, 1.0);
let mut output = Frame::ZERO;
let mono_input = (frame.left + frame.right) * GAIN;
for comb_filter in comb_filters.iter_mut() {
output.left += comb_filter.0.process(mono_input, feedback, damping);
output.right += comb_filter.1.process(mono_input, feedback, damping);
}
for all_pass_filter in all_pass_filters.iter_mut() {
output.left = all_pass_filter.0.process(output.left);
output.right = all_pass_filter.1.process(output.right);
}
let wet_1 = stereo_width / 2.0 + 0.5;
let wet_2 = (1.0 - stereo_width) / 2.0;
let output = Frame::new(
output.left * wet_1 + output.right * wet_2,
output.right * wet_1 + output.left * wet_2,
);
*frame = output * mix.sqrt() + *frame * (1.0 - mix).sqrt()
}
} else {
panic!("Reverb should be initialized before the first process call")
}
}
}
command_writers_and_readers! {
set_feedback: ValueChangeCommand<f64>,
set_damping: ValueChangeCommand<f64>,
set_stereo_width: ValueChangeCommand<f64>,
set_mix: ValueChangeCommand<Mix>,
}