tests: reverb

This commit is contained in:
Vasilii Milovidov
2023-10-06 03:14:56 +04:00
parent 163f8b53fd
commit c0411c389f
2 changed files with 155 additions and 78 deletions
+63
View File
@@ -120,6 +120,69 @@ impl Reverb {
}
}
pub struct ReverbGen {
pub generated_ir: Option<GeneratedIR>,
pub sample_ir: Option<SampleIR>,
}
pub struct SampleIR {
pub buffer: AudioBuffer,
pub convolver: ConvolverNode,
pub room: Option<f32>,
pub roomsize: Option<f32>,
}
pub struct GeneratedIR {
pub buffer: AudioBuffer,
pub convolver: ConvolverNode,
pub room: Option<f32>,
pub roomsize: Option<f32>,
pub roomfade: Option<f32>,
pub roomlp: Option<f32>,
pub roomdim: Option<f32>,
}
impl GeneratedIR {
pub fn new(context: &AudioContext, room: Option<f32>, roomsize: Option<f32>, roomfade: Option<f32>, roomlp: Option<f32>, roomdim: Option<f32>, compressor: &DynamicsCompressorNode) -> Self {
let mut convolver = context.create_convolver();
let buffer = context.create_buffer(2, 44100, 44100.0);
convolver.set_buffer(buffer.clone());
convolver.connect(compressor);
Self { buffer, convolver, room, roomsize, roomfade, roomlp, roomdim }
}
}
pub trait IR {
fn set_ir(&mut self, context: &AudioContext, ir: &AudioBuffer);
fn set_params(&mut self, room: Option<f32>, roomsize: Option<f32>, roomfade: Option<f32>, roomlp: Option<f32>, roomdim: Option<f32>);
fn set_convolver(&mut self, context: &AudioContext, compressor: &DynamicsCompressorNode);
fn set_convolver_buffer(&mut self, context: &AudioContext, compressor: &DynamicsCompressorNode, ir: &AudioBuffer);
}
impl IR for SampleIR {
fn set_ir(&mut self, context: &AudioContext, ir: &AudioBuffer) {
self.buffer = ir.clone();
self.convolver.set_buffer(ir.clone());
}
fn set_params(&mut self, room: Option<f32>, roomsize: Option<f32>, roomfade: Option<f32>, roomlp: Option<f32>, roomdim: Option<f32>) {
self.room = room;
self.roomsize = roomsize;
}
fn set_convolver(&mut self, context: &AudioContext, compressor: &DynamicsCompressorNode) {
self.convolver.connect(compressor);
}
fn set_convolver_buffer(&mut self, context: &AudioContext, compressor: &DynamicsCompressorNode, ir: &AudioBuffer) {
self.convolver.set_buffer(ir.clone());
self.convolver.connect(compressor);
}
}
pub struct Synth {
pub oscillator: OscillatorNode,
pub envelope: GainNode,
+92 -78
View File
@@ -165,7 +165,6 @@ pub fn init(
}
}
_ => {
let t = audio_context.current_time();
// CREATE FILEPATHS
let (url, file_path, file_path_clone) = create_filepath(&message);
@@ -214,14 +213,7 @@ pub fn init(
// DELAY
if message.delay.wet.is_some() {
delays.entry(message.orbit).or_insert({
Delay::new(&audio_context, &compressor)
});
let delay = delays.get(&message.orbit).unwrap();
sampler.envelope.connect(&delay.input);
delay.delay.delay_time().set_value(message.delay.delay_time.unwrap_or(0.25));
delay.feedback.gain().set_value(message.delay.feedback.unwrap_or(0.5));
delay.pre_gain.gain().set_value_at_time(message.delay.wet.unwrap_or(0.25), t + message.delay.delay_time.unwrap_or(0.5) as f64);
apply_delay(&mut audio_context, &mut delays, &compressor, &message, t, &mut sampler);
};
if message.reverb.room.is_some() && message.reverb.room.unwrap() > 0.0 {
@@ -250,19 +242,26 @@ pub fn init(
});
}
fn apply_delay(audio_context: &mut AudioContext, delays: &mut HashMap<usize, Delay>, compressor: &DynamicsCompressorNode, message: &WebAudioMessage, t: f64, mut sampler: &mut Sampler) {
delays.entry(message.orbit).or_insert({
Delay::new(&audio_context, &compressor)
});
let delay = delays.get(&message.orbit).unwrap();
sampler.envelope.connect(&delay.input);
delay.delay.delay_time().set_value(message.delay.delay_time.unwrap_or(0.25));
delay.feedback.gain().set_value(message.delay.feedback.unwrap_or(0.5));
delay.pre_gain.gain().set_value_at_time(message.delay.wet.unwrap_or(0.25), t + message.delay.delay_time.unwrap_or(0.5) as f64);
}
fn apply_reverb(mut audio_context: &AudioContext, compressor: &DynamicsCompressorNode, cache: &Cache<String, AudioBuffer>, message: &WebAudioMessage, mut sampler: &mut Sampler, reverbs: &mut HashMap<usize, Reverb>) {
// IR GENERATION
if message.reverb.ir.is_none() {
// CACHE KEY
let key = format!("reverb: {}{}{}{}{}", message.reverb.room.unwrap_or(0.0), message.reverb.size.unwrap_or(1.0), message.reverb.roomlp.unwrap_or(0.0), message.reverb.roomfade.unwrap_or(0.0), message.reverb.roomdim.unwrap_or(0.0));
// IS BUFFER CACHED?
if let Some(ir) = cache.get(&key) {
// IF REVERB STRUCT IS IN HASHMAP - PLAY IT, ELSE CREATE IT
let reverb = reverbs.entry(message.orbit).or_insert({
let mut reverb = Reverb::new(&audio_context, message.reverb.room, message.reverb.size, message.reverb.roomlp, message.reverb.roomfade, message.reverb.roomdim, &compressor);
reverb.convolver.set_buffer(ir.clone());
@@ -270,7 +269,6 @@ fn apply_reverb(mut audio_context: &AudioContext, compressor: &DynamicsCompresso
reverb
});
// IF BUFFER IS CACHED BUT PARAMETERS HAS CHANGED - GENERATE NEW BUFFER
if reverb.room.unwrap_or(0.0) != message.reverb.room.unwrap_or(0.0)
|| reverb.roomlp.unwrap_or(0.0) != message.reverb.roomlp.unwrap_or(0.0)
|| reverb.roomfade.unwrap_or(0.0) != message.reverb.roomfade.unwrap_or(0.0)
@@ -319,32 +317,10 @@ fn apply_reverb(mut audio_context: &AudioContext, compressor: &DynamicsCompresso
});
}
} else if message.reverb.ir.is_some() {
// IR FROM SAMPLES
let (ir_url, ir_file_path, ir_file_path_clone) = create_ir_filepath(&message);
// DOWNLOAD IR IF IT DOESN'T EXIST
tokio::spawn(async move {
if tokio::fs::metadata(&ir_file_path.clone()).await.is_err() {
let response = reqwest::get(ir_url)
.await
.unwrap_or_else(|_| panic!("Failed to send GET request"));
let bytes = response.bytes().await.unwrap();
let path = Path::new(&ir_file_path);
let mut file = create_file_and_dirs(path).await;
file.write_all(&bytes)
.await
.unwrap_or_else(|_| panic!("Failed to write to file"));
}
});
let ir_cache_clone = cache.clone();
let requested_size = message.reverb.size.unwrap_or(2.0).clone();
// IF BUFFER IS CACHED - PLAY IT
if let Some(ir_buffer) = cache.get(&ir_file_path_clone) {
// IF REVERB STRUCT IS IN HASHMAP - PLAY IT, ELSE CREATE IT
let reverb = reverbs.entry(message.orbit).or_insert({
let mut reverb = Reverb::new(&audio_context, message.reverb.room, message.reverb.size, message.reverb.roomlp, message.reverb.roomfade, message.reverb.roomdim, &compressor);
reverb.convolver.set_buffer(ir_buffer.clone());
@@ -352,59 +328,97 @@ fn apply_reverb(mut audio_context: &AudioContext, compressor: &DynamicsCompresso
reverb
});
// IF BUFFER IS CACHED BUT PARAMETERS HAS CHANGED - GENERATE NEW BUFFER
if reverb.roomsize.unwrap_or(0.0) != message.reverb.size.unwrap_or(0.0) {
let buf = reverb.convolver.buffer().unwrap();
reverb.roomsize = message.reverb.size;
let new_length = buf.sample_rate() * requested_size;
let mut new_buffer = audio_context.create_buffer(buf.number_of_channels(), new_length as usize, buf.sample_rate());
for ch in 0..buf.number_of_channels() {
let old_data = buf.get_channel_data(ch);
let new_data = new_buffer.get_channel_data_mut(ch);
for i in 0..new_length as usize {
if i < old_data.len() {
new_data[i] = old_data[i];
} else {
new_data[i] = 0.0;
}
}
}
reverb.convolver.set_buffer(new_buffer.clone());
cache.insert(ir_file_path_clone.clone(), new_buffer);
adjust_length(&mut audio_context, cache, message, ir_file_path_clone, requested_size, reverb);
}
reverb.convolver.set_buffer(ir_buffer);
sampler.envelope.connect(&reverb.convolver);
// IF FILE IS DOWNLOADED BUT NOT CACHED - DECODE IT
} else if let Ok(file) = File::open(&ir_file_path_clone) {
let ir_cache_clone = cache.clone();
} else {
tokio::spawn(async move {
let context = OfflineAudioContext::new(2, 400, 44100.0);
let ir_buffer = context.decode_audio_data_sync(file).expect("IR OOOOPS");
let new_length = ir_buffer.sample_rate() * requested_size;
let mut new_buffer = context.create_buffer(ir_buffer.number_of_channels(), new_length as usize, ir_buffer.sample_rate());
for ch in 0..ir_buffer.number_of_channels() {
let old_data = ir_buffer.get_channel_data(ch);
let new_data = new_buffer.get_channel_data_mut(ch);
for i in 0..new_length as usize {
if i < old_data.len() {
new_data[i] = old_data[i];
} else {
new_data[i] = 0.0;
}
}
if let Ok(file1) = File::open(&ir_file_path) {
create_new_ir_buffer_from_file(&ir_file_path, ir_cache_clone, requested_size, file1);
} else if tokio::fs::metadata(&ir_file_path.clone()).await.is_err() {
download_file(ir_url, &ir_file_path).await;
}
ir_cache_clone.insert(ir_file_path_clone.clone(), new_buffer);
});
}
}
}
async fn download_file(ir_url: Url, ir_file_path: &String) {
let response = reqwest::get(ir_url)
.await
.unwrap_or_else(|_| panic!("Failed to send GET request"));
let bytes = response.bytes().await.unwrap();
let path = Path::new(&ir_file_path);
let mut file = create_file_and_dirs(path).await;
file.write_all(&bytes)
.await
.unwrap_or_else(|_| panic!("Failed to write to file"));
}
fn create_new_ir_buffer_from_file(ir_file_path: &String, ir_cache_clone: Cache<String, AudioBuffer>, requested_size: f32, file1: File) {
let context = OfflineAudioContext::new(2, 400, 44100.0);
let ir_buffer = context.decode_audio_data_sync(file1).expect("IR OOOOPS");
let new_length = ir_buffer.sample_rate() * requested_size;
let mut new_buffer = context.create_buffer(ir_buffer.number_of_channels(), new_length as usize, ir_buffer.sample_rate());
for ch in 0..ir_buffer.number_of_channels() {
let old_data = ir_buffer.get_channel_data(ch);
let new_data = new_buffer.get_channel_data_mut(ch);
for i in 0..new_length as usize {
if i < old_data.len() {
new_data[i] = old_data[i];
} else {
new_data[i] = 0.0;
}
}
}
ir_cache_clone.insert(ir_file_path.clone(), new_buffer);
}
fn adjust_length(audio_context: &mut &AudioContext, cache: &Cache<String, AudioBuffer>, message: &WebAudioMessage, ir_file_path_clone: String, requested_size: f32, reverb: &mut Reverb) {
let buf = reverb.convolver.buffer().unwrap();
reverb.roomsize = message.reverb.size;
let new_length = buf.sample_rate() * requested_size;
let mut new_buffer = audio_context.create_buffer(buf.number_of_channels(), new_length as usize, buf.sample_rate());
for ch in 0..buf.number_of_channels() {
let old_data = buf.get_channel_data(ch);
let new_data = new_buffer.get_channel_data_mut(ch);
for i in 0..new_length as usize {
if i < old_data.len() {
new_data[i] = old_data[i];
} else {
new_data[i] = 0.0;
}
}
}
reverb.convolver.set_buffer(new_buffer.clone());
cache.insert(ir_file_path_clone.clone(), new_buffer);
}
async fn generate_buffer_and_update_cache(audio_context: &AudioContext, size: f32, key: String, cache: Cache<String, AudioBuffer>) {
let len = (audio_context.sample_rate() * size) as usize;
let decay = 4.0;
let num_coeffs = 101;
let fade = 2.4;
let cutoff = 500.0 / audio_context.sample_rate();
let mut buffer = audio_context.create_buffer(2, len, 44100.0);
let fir = generate_coefficients_lp(num_coeffs, cutoff);
let ir = create_impulse_response(len, decay, &fir, 44100.0, fade);
write_to_buffer(&mut buffer, &ir);
cache.insert(key, buffer);
}
fn create_filepath(message: &WebAudioMessage) -> (Url, String, String) {
let url = Url::parse(&*message.sampleurl).expect("failed to parse url");
let filename = url.path_segments()