diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index 8c7efd777..dc9c0d9bf 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -257,6 +257,16 @@ impl DeviceTrait for Device { let mut temporary_buffer = vec![0f32; buffer_size_samples]; let mut temporary_channel_buffer = vec![0f32; buffer_size_frames]; + #[cfg(target_feature = "atomics")] + let temporary_channel_array_view: js_sys::Float32Array; + #[cfg(target_feature = "atomics")] + { + let temporary_channel_array = js_sys::ArrayBuffer::new( + (std::mem::size_of::() * buffer_size_frames) as u32, + ); + temporary_channel_array_view = js_sys::Float32Array::new(&temporary_channel_array); + } + // Create a webaudio buffer which will be reused to avoid allocations. let ctx_buffer = ctx .create_buffer( @@ -316,9 +326,31 @@ impl DeviceTrait for Device { temporary_channel_buffer[i] = temporary_buffer[n_channels * i + channel]; } - ctx_buffer - .copy_to_channel(&mut temporary_channel_buffer, channel as i32) - .expect("Unable to write sample data into the audio context buffer"); + + #[cfg(not(target_feature = "atomics"))] + { + ctx_buffer + .copy_to_channel(&mut temporary_channel_buffer, channel as i32) + .expect( + "Unable to write sample data into the audio context buffer", + ); + } + + // copyToChannel cannot be directly copied into from a SharedArrayBuffer, + // which WASM memory is backed by if the 'atomics' flag is enabled. + // This workaround copies the data into an intermediary buffer first. + // There's a chance browsers may eventually relax that requirement. + // See this issue: https://github.com/WebAudio/web-audio-api/issues/2565 + #[cfg(target_feature = "atomics")] + { + temporary_channel_array_view.copy_from(&mut temporary_channel_buffer); + ctx_buffer + .unchecked_ref::() + .copy_to_channel(&temporary_channel_array_view, channel as i32) + .expect( + "Unable to write sample data into the audio context buffer", + ); + } } // Create an AudioBufferSourceNode, schedule it to playback the reused buffer @@ -480,3 +512,17 @@ fn valid_config(conf: &StreamConfig, sample_format: SampleFormat) -> bool { fn buffer_time_step_secs(buffer_size_frames: usize, sample_rate: SampleRate) -> f64 { buffer_size_frames as f64 / sample_rate.0 as f64 } + +#[cfg(target_feature = "atomics")] +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_name = AudioBuffer)] + type ExternalArrayAudioBuffer; + + # [wasm_bindgen(catch, method, structural, js_class = "AudioBuffer", js_name = copyToChannel)] + pub fn copy_to_channel( + this: &ExternalArrayAudioBuffer, + source: &js_sys::Float32Array, + channel_number: i32, + ) -> Result<(), JsValue>; +}