Skip to content

Commit

Permalink
add modulation to reverb
Browse files Browse the repository at this point in the history
  • Loading branch information
russellmcc committed Feb 3, 2025
1 parent 1c14070 commit 614c4d2
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 21 deletions.
1 change: 0 additions & 1 deletion rust/reverb/component/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ mod reverb;
mod shuffler;

// TODO: Optimize Hadamard matrix
// TODO: Add modulation
// TODO: UI
// TODO: Parameters to consider:
// - feedback
Expand Down
9 changes: 3 additions & 6 deletions rust/reverb/component/src/multi_channel_feedback_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ impl MultiChannelFeedbackLoop {
///
/// So, lower values of damping mean more damping!
///
/// `modulation_depth` is in _ms_ and controls depth of modulation.
/// `modulation_rate` is in cycles per second.
/// `modulation_depth` is in samples and controls depth of modulation. 4ms is fine.
/// `modulation_rate` is in cycles per sample and controls speed of modulation. 6 hz is fine.
#[allow(clippy::cast_possible_truncation)]
pub fn process(
&mut self,
Expand All @@ -47,10 +47,7 @@ impl MultiChannelFeedbackLoop {
) -> [f32; CHANNELS] {
let delayed = {
let mut delayed = self.delay.read();
let modulated_delay = self.modulated_delay.read(
modulation_depth * self.sampling_rate,
modulation_rate / self.sampling_rate,
);
let modulated_delay = self.modulated_delay.read(modulation_depth, modulation_rate);

// We apply damping only to the last unmodulated channel
delayed[UNMODULATED_CHANNELS - 1] = self
Expand Down
16 changes: 14 additions & 2 deletions rust/reverb/component/src/multi_channel_feedback_loop/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,21 @@ fn impulse_response_for_damping(name: &str, damping: f32, depth: f32, rate: f32)
SAMPLING_RATE,
);
let mut output = vec![0.0; SNAPSHOT_LENGTH];
output[0] = feedback_loop.process([1.0; CHANNELS], FEEDBACK, damping, depth, rate)[0];
output[0] = feedback_loop.process(
[1.0; CHANNELS],
FEEDBACK,
damping,
depth * SAMPLING_RATE,
rate / SAMPLING_RATE,
)[0];
for output in output.iter_mut().skip(1) {
*output = feedback_loop.process([0.0; CHANNELS], FEEDBACK, damping, depth, rate)[0];
*output = feedback_loop.process(
[0.0; CHANNELS],
FEEDBACK,
damping,
depth * SAMPLING_RATE,
rate / SAMPLING_RATE,
)[0];
}
assert_snapshot!(name, 48000, output);
}
Expand Down
17 changes: 13 additions & 4 deletions rust/reverb/component/src/reverb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ pub struct Reverb {
shelves: [Svf; 2],
shelf_g: f64,
shelf_two_r: f64,
sampling_rate: f32,
}

pub struct Params {
pub mix: f32,
pub feedback: f32,
pub brightness: f32,
pub damping: f32,
pub modulation_depth_seconds: f32,
pub modulation_rate_hz: f32,
}

const SHELF_FREQ: f32 = 2000.0;
Expand Down Expand Up @@ -59,21 +62,27 @@ impl Reverb {
shelves: [Svf::default(), Svf::default()],
shelf_g: calc_g(f64::from((SHELF_FREQ / env.sampling_rate).min(0.45))),
shelf_two_r: calc_two_r(SHELF_Q),
sampling_rate: env.sampling_rate,
}
}

#[allow(clippy::cast_possible_truncation)]
pub fn process(&mut self, params: Params, input: &impl Buffer, output: &mut impl BufferMut) {
let modulation_depth = params.modulation_depth_seconds * self.sampling_rate;
let modulation_rate = params.modulation_rate_hz / self.sampling_rate;
if input.num_channels() == 1 {
let input = input.channel(0);
let output = output.channel_mut(0);
for (input, output) in input.iter().zip(output.iter_mut()) {
let mc_input = [*input; CHANNELS];
let (x, er) = self.diffuser.process_mono(0.5, &mc_input);
*output = self
.feedback_loop
.process(x, params.feedback, params.damping, 0.0, 0.0)[0]
+ er;
*output = self.feedback_loop.process(
x,
params.feedback,
params.damping,
modulation_depth,
modulation_rate,
)[0] + er;
*output = params.mix
* self.shelves[0]
.process_high_shelf(std::iter::once(GainInput {
Expand Down
Git LFS file not shown
38 changes: 30 additions & 8 deletions rust/reverb/component/src/reverb/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use conformal_component::{
};
use snapshots::assert_snapshot;

#[test]
fn impulse_response() {
fn impulse_response_for_params(params: Params) -> Vec<f32> {
const SNAPSHOT_LENGTH: usize = 48_000 * 2;
const SAMPLING_RATE: f32 = 48000.0;
let mut reverb = Reverb::new(&ProcessingEnvironment {
Expand All @@ -18,15 +17,38 @@ fn impulse_response() {
let mut impulse_vec = vec![0.0; SNAPSHOT_LENGTH];
impulse_vec[0] = 1.0;
let mut output = BufferData::new_mono(vec![0.0; SNAPSHOT_LENGTH]);
reverb.process(
Params {
reverb.process(params, &BufferData::new_mono(impulse_vec), &mut output);
output.channel(0).iter().copied().collect()
}

#[test]
fn impulse_response() {
assert_snapshot!(
"impulse_response",
48000,
impulse_response_for_params(Params {
feedback: 0.6,
damping: 1.0,
brightness: 1.0,
mix: 1.0,
},
&BufferData::new_mono(impulse_vec),
&mut output,
modulation_depth_seconds: 0.0,
modulation_rate_hz: 0.0,
})
);
}

#[test]
fn impulse_response_modulated_damped() {
assert_snapshot!(
"impulse_response_modulated_damped",
48000,
impulse_response_for_params(Params {
feedback: 0.6,
damping: 0.5,
brightness: 1.0,
mix: 1.0,
modulation_depth_seconds: 0.004,
modulation_rate_hz: 6.0,
})
);
assert_snapshot!("impulse_response", 48000, output.channel(0).iter().copied());
}

0 comments on commit 614c4d2

Please sign in to comment.