diff --git a/src-tauri/src/superdough.rs b/src-tauri/src/superdough.rs index 4259cde34..b57e5e2b3 100644 --- a/src-tauri/src/superdough.rs +++ b/src-tauri/src/superdough.rs @@ -78,38 +78,21 @@ pub fn superdough(message: &WebAudioMessage, context: &mut AudioContext) { } if message.delay.wet > 0.0 { - let output = context.create_gain(); - output.connect(&context.destination()); - - let delay = delays.get(message.orbit).unwrap(); - delay.delay_time().set_value(message.delay.delay_time); - delay.connect(&output); - - let feedback = context.create_gain(); - feedback.gain().set_value(message.delay.feedback); - feedback.connect(delay); - delay.connect(&feedback); - - let pre_gain = context.create_gain(); - pre_gain.gain().set_value(message.delay.wet); - pre_gain.connect(&feedback); - - let input = context.create_gain(); - input.connect(&pre_gain); - input.connect(&context.destination()); // direct sound - env.connect(&input); + create_delay(message, &context, &env, &mut delays, now); } + let (sustain, velocity, release) = (message.adsr.sustain, message.velocity, message.adsr.release); // Connect nodes and play synth connect_nodes(chain, context); - let osc= play_synth(&message, now, osc, filters, &env); + let osc = play_synth(&message, now, osc, filters, &env); osc.set_onended(move |_| { for d in &delays { + // env.gain().set_value_at_time(velocity * sustain, now); + // env.gain().exponential_ramp_to_value_at_time(0.0001, now + release); d.disconnect(); } }); - } _ => { match File::open(format!("samples/{}/{}.wav", message.bank, message.waveform)) { @@ -128,10 +111,23 @@ pub fn superdough(message: &WebAudioMessage, context: &mut AudioContext) { let env = context.create_gain(); chain.push(&env); + let mut delays = Vec::new(); + for _ in 0..=message.orbit { + delays.push(context.create_delay(1.)); + } + + if message.delay.wet > 0.0 { + create_delay(message, &context, &env, &mut delays, now); + } // Connect nodes and play sample connect_nodes(chain, context); src.playback_rate().set_value(message.speed); - play_sample(message, now, audio_buffer_duration, src, filters, &env); + let src = play_sample(message, now, audio_buffer_duration, src, filters, &env); + src.set_onended(move |_| { + for d in &delays { + d.disconnect(); + } + }); } Err(e) => eprintln!("Failed to open file: {:?}", e), } @@ -139,6 +135,30 @@ pub fn superdough(message: &WebAudioMessage, context: &mut AudioContext) { } } +fn create_delay(message: &WebAudioMessage, context: &&mut AudioContext, env: &GainNode, delays: &mut Vec, now: f64) { + + let output = context.create_gain(); + output.connect(&context.destination()); + + let delay = delays.get(message.orbit).unwrap(); + delay.delay_time().set_value(message.delay.delay_time); + delay.connect(&output); + + let feedback = context.create_gain(); + feedback.gain().set_value(message.delay.feedback); + feedback.connect(delay); + delay.connect(&feedback); + + let pre_gain = context.create_gain(); + // pre_gain.gain().set_value_at_time(0.0, now); + pre_gain.gain().set_value_at_time(message.delay.wet, now + message.delay.delay_time as f64); + pre_gain.connect(&feedback); + + let input = context.create_gain(); + input.connect(&pre_gain); + env.connect(&input); +} + fn play_synth(message: &&WebAudioMessage, now: f64, osc: OscillatorNode, filters: Vec, env: &GainNode) -> OscillatorNode { osc.set_type(create_osc_type(&message)); osc.frequency().set_value(message.note); @@ -165,7 +185,7 @@ fn create_osc_type(message: &WebAudioMessage) -> OscillatorType { } } -fn play_sample(message: &WebAudioMessage, now: f64, audio_buffer_duration: f64, src: AudioBufferSourceNode, filters: Vec, env: &GainNode) { +fn play_sample(message: &WebAudioMessage, now: f64, audio_buffer_duration: f64, src: AudioBufferSourceNode, filters: Vec, env: &GainNode) -> AudioBufferSourceNode { let (start_at, stop_at) = if message.speed < 0.0 { (audio_buffer_duration, now + message.duration + 0.2) } else { @@ -201,6 +221,7 @@ fn play_sample(message: &WebAudioMessage, now: f64, audio_buffer_duration: f64, }; src.stop_at(stop_at); } + src } fn create_filters(context: &mut AudioContext, message: &WebAudioMessage) -> Vec { @@ -288,8 +309,8 @@ fn apply_adsr( sustain * message.velocity, now + attack + decay, ) - .set_value_at_time(sustain * message.velocity, now + attack + decay) - .linear_ramp_to_value_at_time(0.0, now + attack + decay + release); + .set_value_at_time(sustain * message.velocity, message.duration) + .linear_ramp_to_value_at_time(0.001, message.duration + release); } fn apply_synth_adsr(envelope: &GainNode, message: &WebAudioMessage, now: f64) { @@ -321,7 +342,7 @@ fn apply_drum_adsr(envelope: &GainNode, message: &WebAudioMessage, now: f64) { } fn apply_default_drum_adsr(envelope: &GainNode, message: &WebAudioMessage, now: f64) { - apply_adsr(envelope, message, now, 0.001, message.velocity, 0.05, 0.001); + apply_adsr(envelope, message, now, 0.001, message.velocity, 0.5, 0.5); } fn connect_nodes<'a>(nodes: Vec<&'a (dyn AudioNode + 'a)>, context: &'a mut AudioContext) -> Vec<&'a (dyn AudioNode)> { diff --git a/src-tauri/src/webaudiobridge.rs b/src-tauri/src/webaudiobridge.rs index 02d0d0183..38857c6f4 100644 --- a/src-tauri/src/webaudiobridge.rs +++ b/src-tauri/src/webaudiobridge.rs @@ -3,6 +3,7 @@ use std::{ time::Duration, thread::sleep }; + use tokio::{ sync::{mpsc, Mutex}, time::Instant @@ -12,10 +13,9 @@ use web_audio_api::{ context::{AudioContext, AudioContextLatencyCategory, AudioContextOptions}, node::AudioNode }; -use web_audio_api::context::BaseAudioContext; use crate::superdough::{ADSR, BPF, Delay, FilterADSR, HPF, Loop, LPF, superdough}; - +const BLOCK_SIZE: usize = 128; #[derive(Debug)] pub struct WebAudioMessage { pub note: f32, @@ -56,6 +56,8 @@ pub fn init( ............................................................*/ let message_queue_clone = Arc::clone(&message_queue); tauri::async_runtime::spawn(async move { + + loop { if let Some(package) = async_output_receiver.recv().await { let mut message_queue = message_queue_clone.lock().await; @@ -72,6 +74,7 @@ pub fn init( /* ........................................................... Prepare audio context ............................................................*/ + let latency_hint = match std::env::var("WEB_AUDIO_LATENCY").as_deref() { Ok("playback") => AudioContextLatencyCategory::Playback, _ => AudioContextLatencyCategory::default(), @@ -98,7 +101,7 @@ pub fn init( return false; }); - sleep(Duration::from_millis(1)); + tokio::time::sleep(Duration::from_millis(1)).await; } }); }