-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e3304c5
commit 81d8565
Showing
16 changed files
with
375 additions
and
10 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use rand::Rng; | ||
|
||
pub use crate::shuffler::CHANNELS; | ||
use crate::{multi_channel_per_sample_delay::MultiChannelPerSampleDelay, shuffler::Shuffler}; | ||
|
||
#[derive(Debug, Clone)] | ||
struct DiffuserBlock { | ||
shuffler: Shuffler, | ||
delay: MultiChannelPerSampleDelay<CHANNELS>, | ||
} | ||
|
||
impl DiffuserBlock { | ||
fn new(rng: &mut impl Rng, max_delay: usize) -> Self { | ||
// We ensure that each max_delay / CHANNELS section gets at least one channel of delay. | ||
let mut delays = [0; CHANNELS]; | ||
for (i, delay) in delays.iter_mut().enumerate() { | ||
let range_start = i * (max_delay / CHANNELS); | ||
let range_end = range_start + (max_delay / CHANNELS); | ||
*delay = rng.gen_range(range_start..range_end); | ||
} | ||
|
||
Self { | ||
shuffler: Shuffler::new(rng), | ||
delay: MultiChannelPerSampleDelay::new(delays), | ||
} | ||
} | ||
|
||
fn process(&mut self, input: &[f32; CHANNELS]) -> [f32; CHANNELS] { | ||
let ret = self.shuffler.shuffle(&self.delay.read()); | ||
self.delay.write(input); | ||
ret | ||
} | ||
} | ||
|
||
pub const BLOCKS: usize = 4; | ||
|
||
pub struct Diffuser { | ||
blocks: [DiffuserBlock; BLOCKS], | ||
} | ||
impl Diffuser { | ||
pub fn new(rng: &mut impl Rng, max_delays: [usize; BLOCKS]) -> Self { | ||
Self { | ||
blocks: core::array::from_fn(|i| DiffuserBlock::new(rng, max_delays[i])), | ||
} | ||
} | ||
|
||
pub fn process(&mut self, input: &[f32; CHANNELS]) -> [f32; CHANNELS] { | ||
let mut output = *input; | ||
for block in &mut self.blocks { | ||
output = block.process(&output); | ||
} | ||
output | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; |
3 changes: 3 additions & 0 deletions
3
rust/reverb/component/src/diffuser/snapshots/impulse_response.snap.wav
Git LFS file not shown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#![allow(clippy::cast_possible_truncation)] | ||
|
||
use super::*; | ||
use rand::SeedableRng; | ||
use rand_xoshiro::Xoshiro256PlusPlus; | ||
use snapshots::assert_snapshot; | ||
|
||
#[test] | ||
fn impulse_response() { | ||
const SNAPSHOT_LENGTH: usize = 48_000 * 2; | ||
const SAMPLE_RATE: f32 = 48000.0; | ||
const DELAYS_MS: [f32; BLOCKS] = [20.0, 40.0, 80.0, 160.0]; | ||
let mut diffuser = Diffuser::new( | ||
&mut Xoshiro256PlusPlus::seed_from_u64(369), | ||
DELAYS_MS.map(|d| (d / 1000.0 * SAMPLE_RATE).round() as usize), | ||
); | ||
let mut output = vec![0.0; SNAPSHOT_LENGTH]; | ||
output[0] = diffuser.process(&[1.0; CHANNELS])[0]; | ||
for output in output.iter_mut().skip(1) { | ||
*output = diffuser.process(&[0.0; CHANNELS])[0]; | ||
} | ||
assert_snapshot!("impulse_response", 48000, output); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
rust/reverb/component/src/multi_channel_feedback_loop/snapshots/impulse_response.snap.wav
Git LFS file not shown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use conformal_component::audio::{Buffer, BufferMut}; | ||
use conformal_component::ProcessingEnvironment; | ||
use rand::{Rng, SeedableRng}; | ||
use rand_xoshiro::Xoshiro256PlusPlus; | ||
|
||
pub use crate::diffuser::CHANNELS; | ||
use crate::diffuser::{Diffuser, BLOCKS}; | ||
use crate::multi_channel_feedback_loop::MultiChannelFeedbackLoop; | ||
|
||
pub struct Reverb { | ||
diffuser: Diffuser, | ||
feedback_loop: MultiChannelFeedbackLoop<CHANNELS>, | ||
} | ||
|
||
impl Reverb { | ||
#[allow(clippy::cast_possible_truncation)] | ||
pub fn new(env: &ProcessingEnvironment) -> Self { | ||
const DIFFUSER_DELAYS_MS: [f32; BLOCKS] = [20.0, 40.0, 80.0, 160.0]; | ||
const FEEDBACK_LOOP_MIN_DELAY_MS: f32 = 100.0; | ||
const FEEDBACK_LOOP_MAX_DELAY_MS: f32 = 200.0; | ||
|
||
let min_feedback_delay_samples = | ||
(FEEDBACK_LOOP_MIN_DELAY_MS / 1000.0 * env.sampling_rate).round() as usize; | ||
let feedback_delay_range_samples = ((FEEDBACK_LOOP_MAX_DELAY_MS | ||
- FEEDBACK_LOOP_MIN_DELAY_MS) | ||
/ 1000.0 | ||
* env.sampling_rate) | ||
.round() as usize; | ||
|
||
let mut rng = Xoshiro256PlusPlus::seed_from_u64(369); | ||
|
||
let fdn_delays = core::array::from_fn(|i| { | ||
let min_for_block = | ||
min_feedback_delay_samples + i * feedback_delay_range_samples / CHANNELS; | ||
let max_for_block = min_for_block + feedback_delay_range_samples / CHANNELS; | ||
rng.gen_range(min_for_block..max_for_block) | ||
}); | ||
|
||
Self { | ||
diffuser: Diffuser::new( | ||
&mut rng, | ||
DIFFUSER_DELAYS_MS.map(|d| (d / 1000.0 * env.sampling_rate).round() as usize), | ||
), | ||
feedback_loop: MultiChannelFeedbackLoop::new(fdn_delays), | ||
} | ||
} | ||
|
||
pub fn process(&mut self, feedback: f32, input: &impl Buffer, output: &mut impl BufferMut) { | ||
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 = core::array::from_fn(|i| if i == 0 { *input } else { 0.0 }); | ||
*output = self | ||
.feedback_loop | ||
.process(self.diffuser.process(&mc_input), feedback)[0]; | ||
} | ||
} else if input.num_channels() == 2 { | ||
let input_l = input.channel(0); | ||
let input_r = input.channel(1); | ||
for (i, (input_l, input_r)) in input_l.iter().zip(input_r.iter()).enumerate() { | ||
{ | ||
let mc_input = core::array::from_fn(|i| { | ||
if i == 0 { | ||
*input_l | ||
} else if i == 1 { | ||
*input_r | ||
} else { | ||
0.0 | ||
} | ||
}); | ||
let x = self | ||
.feedback_loop | ||
.process(self.diffuser.process(&mc_input), feedback); | ||
output.channel_mut(0)[i] = x[0]; | ||
output.channel_mut(1)[i] = x[1]; | ||
} | ||
} | ||
} else { | ||
panic!("Reverb only supports mono and stereo input"); | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; |
3 changes: 3 additions & 0 deletions
3
rust/reverb/component/src/reverb/snapshots/impulse_response.snap.wav
Git LFS file not shown
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use super::*; | ||
use conformal_component::{ | ||
audio::{BufferData, ChannelLayout}, | ||
ProcessingMode, | ||
}; | ||
use snapshots::assert_snapshot; | ||
|
||
#[test] | ||
fn impulse_response() { | ||
const SNAPSHOT_LENGTH: usize = 48_000 * 2; | ||
const SAMPLE_RATE: f32 = 48000.0; | ||
let mut reverb = Reverb::new(&ProcessingEnvironment { | ||
sampling_rate: SAMPLE_RATE, | ||
max_samples_per_process_call: SNAPSHOT_LENGTH, | ||
channel_layout: ChannelLayout::Mono, | ||
processing_mode: ProcessingMode::Realtime, | ||
}); | ||
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(0.85, &BufferData::new_mono(impulse_vec), &mut output); | ||
assert_snapshot!("impulse_response", 48000, output.channel(0).iter().copied()); | ||
} |
Oops, something went wrong.