From 640a1d39edcf53091cf1e87f45c11539dee9431b Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 21 May 2020 16:05:13 +0200 Subject: [PATCH] Rebase/Update webaudio PR for recent breaking changes This rebases #372, addressing the recent changes introduced by #397, #395, and #371 in the process. TODO: - [ ] Complete implementation of `callback` and `playback` timestamps in the output stream callback. --- src/host/webaudio/mod.rs | 98 +++++++++++++++++++++++----------------- src/platform/mod.rs | 4 +- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index 12a78ca..8b2f293 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -7,8 +7,10 @@ use self::wasm_bindgen::prelude::*; use self::wasm_bindgen::JsCast; use self::web_sys::{AudioContext, AudioContextOptions}; use crate::{ - BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleRate, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, }; use std::ops::DerefMut; use std::sync::{Arc, Mutex, RwLock}; @@ -28,8 +30,8 @@ pub struct Stream { on_ended_closures: Vec>>>>, } -pub type SupportedInputFormats = ::std::vec::IntoIter; -pub type SupportedOutputFormats = ::std::vec::IntoIter; +pub type SupportedInputConfigs = ::std::vec::IntoIter; +pub type SupportedOutputConfigs = ::std::vec::IntoIter; impl Host { pub fn new() -> Result { @@ -72,12 +74,16 @@ impl Device { } #[inline] - fn supported_input_formats(&self) -> Result { + fn supported_input_configs( + &self, + ) -> Result { unimplemented!(); } #[inline] - fn supported_output_formats(&self) -> Result { + fn supported_output_configs( + &self, + ) -> Result { // TODO: right now cpal's API doesn't allow flexibility here // "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if // this ever becomes more flexible, don't forget to change that @@ -86,34 +92,34 @@ impl Device { // // UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and // filter out those that lay outside the range specified above. - Ok(vec![SupportedFormat { + Ok(vec![SupportedStreamConfigRange { channels: 2, - min_sample_rate: ::SampleRate(44100), - max_sample_rate: ::SampleRate(44100), - data_type: ::SampleFormat::F32, + min_sample_rate: SampleRate(44100), + max_sample_rate: SampleRate(44100), + sample_format: ::SampleFormat::F32, }] .into_iter()) } #[inline] - fn default_input_format(&self) -> Result { + fn default_input_config(&self) -> Result { unimplemented!(); } #[inline] - fn default_output_format(&self) -> Result { + fn default_output_config(&self) -> Result { // TODO: because it is hard coded, see supported_output_formats. - Ok(Format { + Ok(SupportedStreamConfig { channels: 2, sample_rate: ::SampleRate(44100), - data_type: ::SampleFormat::F32, + sample_format: ::SampleFormat::F32, }) } } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; #[inline] @@ -122,37 +128,38 @@ impl DeviceTrait for Device { } #[inline] - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } #[inline] - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } #[inline] - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } #[inline] - fn default_output_format(&self) -> Result { - Device::default_output_format(self) + fn default_output_config(&self) -> Result { + Device::default_output_config(self) } fn build_input_stream_raw( &self, - _format: &Format, + _config: &StreamConfig, + _sample_format: SampleFormat, _data_callback: D, _error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() @@ -161,27 +168,28 @@ impl DeviceTrait for Device { /// Create an output stream. fn build_output_stream_raw( &self, - format: &Format, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, _error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { assert_eq!( - format.data_type, + sample_format, SampleFormat::F32, "WebAudio backend currently only supports `f32` data", ); // Use a buffer period of 1/3s for this early proof of concept. - let buffer_length = (format.sample_rate.0 as f64 / 3.0).round() as usize; + let buffer_length = (config.sample_rate.0 as f64 / 3.0).round() as usize; let data_callback = Arc::new(Mutex::new(Box::new(data_callback))); // Create the WebAudio stream. let mut stream_opts = AudioContextOptions::new(); - stream_opts.sample_rate(format.sample_rate.0 as f32); + stream_opts.sample_rate(config.sample_rate.0 as f32); let ctx = Arc::new( AudioContext::new_with_context_options(&stream_opts).map_err( |err| -> BuildStreamError { @@ -201,21 +209,20 @@ impl DeviceTrait for Device { // Create a set of closures / callbacks which will continuously fetch and schedule sample playback. // Starting with two workers, eg a front and back buffer so that audio frames can be fetched in the background. for _i in 0..2 { - let format = format.clone(); let data_callback_handle = data_callback.clone(); let ctx_handle = ctx.clone(); let time_handle = time.clone(); // A set of temporary buffers to be used for intermediate sample transformation steps. - let mut temporary_buffer = vec![0f32; buffer_length * format.channels as usize]; + let mut temporary_buffer = vec![0f32; buffer_length * config.channels as usize]; let mut temporary_channel_buffer = vec![0f32; buffer_length]; // Create a webaudio buffer which will be reused to avoid allocations. let ctx_buffer = ctx .create_buffer( - format.channels as u32, + config.channels as u32, buffer_length as u32, - format.sample_rate.0 as f32, + config.sample_rate.0 as f32, ) .map_err(|err| -> BuildStreamError { let description = format!("{:?}", err); @@ -228,6 +235,9 @@ impl DeviceTrait for Device { Arc::new(RwLock::new(None)); let on_ended_closure_handle = on_ended_closure.clone(); + let n_channels = config.channels as usize; + let sample_rate = config.sample_rate.0 as f64; + on_ended_closure .write() .unwrap() @@ -252,16 +262,20 @@ impl DeviceTrait for Device { let sample_format = SampleFormat::F32; let mut data = unsafe { Data::from_parts(data, len, sample_format) }; let mut data_callback = data_callback_handle.lock().unwrap(); - (data_callback.deref_mut())(&mut data); + let callback = unimplemented!(); + let playback = unimplemented!(); + let timestamp = crate::OutputStreamTimestamp { callback, playback }; + let info = OutputCallbackInfo { timestamp }; + (data_callback.deref_mut())(&mut data, &info); } // Deinterleave the sample data and copy into the audio context buffer. // We do not reference the audio context buffer directly eg getChannelData. // As wasm-bindgen only gives us a copy, not a direct reference. - for channel in 0..(format.channels as usize) { + for channel in 0..n_channels { for i in 0..buffer_length { temporary_channel_buffer[i] = - temporary_buffer[(format.channels as usize) * i + channel]; + temporary_buffer[n_channels * i + channel]; } ctx_buffer .copy_to_channel(&mut temporary_channel_buffer, channel as i32) @@ -293,8 +307,8 @@ impl DeviceTrait for Device { .expect("Unable to start the webaudio buffer source"); // Keep track of when the next buffer worth of samples should be played. - *time_handle.write().unwrap() = time_at_start_of_buffer - + (buffer_length as f64 / format.sample_rate.0 as f64); + *time_handle.write().unwrap() = + time_at_start_of_buffer + (buffer_length as f64 / sample_rate); }) as Box)); on_ended_closures.push(on_ended_closure); diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f3be04c..033c91e 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -504,8 +504,8 @@ mod platform_impl { mod platform_impl { pub use crate::host::webaudio::{ Device as WebAudioDevice, Devices as WebAudioDevices, Host as WebAudioHost, - Stream as WebAudioStream, SupportedInputFormats as WebAudioSupportedInputFormats, - SupportedOutputFormats as WebAudioSupportedOutputFormats, + Stream as WebAudioStream, SupportedInputConfigs as WebAudioSupportedInputConfigs, + SupportedOutputConfigs as WebAudioSupportedOutputConfigs, }; impl_platform_host!(WebAudio webaudio "WebAudio");