From 476f6c4c2ca9c7664ede69083aea395f5f0a81d3 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 18:28:38 +0100 Subject: [PATCH 1/5] Only allow for private construction of `SupportedStreamConfig`. This should give the user a higher confidence that, if they have a `SupportedStreamConfig` format type that it is actually supported. Also updates the `raw` stream builder methods to take a `StreamConfig` and `SampleFormat` as separate arguments for flexibility. **Backends Updated** - [x] null - [x] alsa - [ ] emscripten - [ ] coreaudio - [ ] wasapi - [ ] asio --- examples/beep.rs | 2 +- examples/enumerate.rs | 8 ++++---- examples/record_wav.rs | 10 +++++----- src/host/alsa/mod.rs | 39 ++++++++++++++++++++++---------------- src/host/null/mod.rs | 10 ++++++---- src/lib.rs | 43 ++++++++++++++++++++++++------------------ src/platform/mod.rs | 22 +++++++++++++++++---- src/traits.rs | 16 ++++++++++------ 8 files changed, 92 insertions(+), 58 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 20dc5ea..60ec8aa 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -10,7 +10,7 @@ fn main() -> Result<(), anyhow::Error> { .expect("failed to find a default output device"); let config = device.default_output_config()?; - match config.sample_format { + match config.sample_format() { cpal::SampleFormat::F32 => run::(&device, &config.into())?, cpal::SampleFormat::I16 => run::(&device, &config.into())?, cpal::SampleFormat::U16 => run::(&device, &config.into())?, diff --git a/examples/enumerate.rs b/examples/enumerate.rs index 08606ae..d599790 100644 --- a/examples/enumerate.rs +++ b/examples/enumerate.rs @@ -22,8 +22,8 @@ fn main() -> Result<(), anyhow::Error> { println!(" {}. \"{}\"", device_index + 1, device.name()?); // Input configs - if let Ok(fmt) = device.default_input_config() { - println!(" Default input stream config:\n {:?}", fmt); + if let Ok(conf) = device.default_input_config() { + println!(" Default input stream config:\n {:?}", conf); } let mut input_configs = match device.supported_input_configs() { Ok(f) => f.peekable(), @@ -45,8 +45,8 @@ fn main() -> Result<(), anyhow::Error> { } // Output configs - if let Ok(fmt) = device.default_output_config() { - println!(" Default output stream config:\n {:?}", fmt); + if let Ok(conf) = device.default_output_config() { + println!(" Default output stream config:\n {:?}", conf); } let mut output_configs = match device.supported_output_configs() { Ok(f) => f.peekable(), diff --git a/examples/record_wav.rs b/examples/record_wav.rs index 793b781..223a153 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -40,7 +40,7 @@ fn main() -> Result<(), anyhow::Error> { eprintln!("an error occurred on stream: {}", err); }; - let stream = match config.sample_format { + let stream = match config.sample_format() { cpal::SampleFormat::F32 => device.build_input_stream( &config.into(), move |data| write_input_data::(data, &writer_2), @@ -78,10 +78,10 @@ fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { fn wav_spec_from_config(config: &cpal::SupportedStreamConfig) -> hound::WavSpec { hound::WavSpec { - channels: config.channels as _, - sample_rate: config.sample_rate.0 as _, - bits_per_sample: (config.sample_format.sample_size() * 8) as _, - sample_format: sample_format(config.sample_format), + channels: config.channels() as _, + sample_rate: config.sample_rate().0 as _, + bits_per_sample: (config.sample_format().sample_size() * 8) as _, + sample_format: sample_format(config.sample_format()), } } diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 6050d74..42f5660 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -4,7 +4,8 @@ extern crate libc; use crate::{ BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, - StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, }; use std::sync::Arc; use std::thread::{self, JoinHandle}; @@ -82,7 +83,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - conf: &SupportedStreamConfig, + conf: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -90,14 +92,16 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_stream_inner(conf, alsa::SND_PCM_STREAM_CAPTURE)?; + let stream_inner = + self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_CAPTURE)?; let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback); Ok(stream) } fn build_output_stream_raw( &self, - conf: &SupportedStreamConfig, + conf: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -105,7 +109,8 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_stream_inner(conf, alsa::SND_PCM_STREAM_PLAYBACK)?; + let stream_inner = + self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_PLAYBACK)?; let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback); Ok(stream) } @@ -161,7 +166,8 @@ pub struct Device(String); impl Device { fn build_stream_inner( &self, - conf: &SupportedStreamConfig, + conf: &StreamConfig, + sample_format: SampleFormat, stream_type: alsa::snd_pcm_stream_t, ) -> Result { let name = ffi::CString::new(self.0.clone()).expect("unable to clone device"); @@ -185,7 +191,7 @@ impl Device { }; let can_pause = unsafe { let hw_params = HwParams::alloc(); - set_hw_params_from_format(handle, &hw_params, conf) + set_hw_params_from_format(handle, &hw_params, conf, sample_format) .map_err(|description| BackendSpecificError { description })?; alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1 @@ -213,7 +219,7 @@ impl Device { let stream_inner = StreamInner { channel: handle, - sample_format: conf.sample_format, + sample_format, num_descriptors, num_channels: conf.channels as u16, buffer_len, @@ -904,7 +910,8 @@ fn get_available_samples(stream: &StreamInner) -> Result Result<(), String> { if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) { return Err(format!("errors on pcm handle: {}", e)); @@ -918,13 +925,13 @@ unsafe fn set_hw_params_from_format( } let sample_format = if cfg!(target_endian = "big") { - match format.sample_format { + match sample_format { SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE, SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE, } } else { - match format.sample_format { + match sample_format { SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE, SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE, @@ -941,7 +948,7 @@ unsafe fn set_hw_params_from_format( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate( pcm_handle, hw_params.0, - format.sample_rate.0 as libc::c_uint, + config.sample_rate.0 as libc::c_uint, 0, )) { return Err(format!("sample rate could not be set: {}", e)); @@ -949,7 +956,7 @@ unsafe fn set_hw_params_from_format( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels( pcm_handle, hw_params.0, - format.channels as libc::c_uint, + config.channels as libc::c_uint, )) { return Err(format!("channel count could not be set: {}", e)); } @@ -973,7 +980,7 @@ unsafe fn set_hw_params_from_format( unsafe fn set_sw_params_from_format( pcm_handle: *mut alsa::snd_pcm_t, - format: &SupportedStreamConfig, + config: &StreamConfig, ) -> Result<(usize, usize), String> { let mut sw_params = ptr::null_mut(); // TODO: RAII if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) { @@ -1009,8 +1016,8 @@ unsafe fn set_sw_params_from_format( )) { return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e)); } - let buffer = buffer as usize * format.channels as usize; - let period = period as usize * format.channels as usize; + let buffer = buffer as usize * config.channels as usize; + let period = period as usize * config.channels as usize; (buffer, period) }; diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 2aab365..1424ca8 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -1,7 +1,7 @@ use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, StreamError, SupportedStreamConfig, - SupportedStreamConfigRange, SupportedStreamConfigsError, + PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, + SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -68,7 +68,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - _format: &SupportedStreamConfig, + _config: &StreamConfig, + _sample_format: SampleFormat, _data_callback: D, _error_callback: E, ) -> Result @@ -82,7 +83,8 @@ impl DeviceTrait for Device { /// Create an output stream. fn build_output_stream_raw( &self, - _format: &SupportedStreamConfig, + _config: &StreamConfig, + _sample_format: SampleFormat, _data_callback: D, _error_callback: E, ) -> Result diff --git a/src/lib.rs b/src/lib.rs index cf98a67..e97b2ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,7 @@ //! # let device = host.default_output_device().unwrap(); //! # let supported_config = device.default_output_config().unwrap(); //! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err); -//! let sample_format = supported_config.sample_format; +//! let sample_format = supported_config.sample_format(); //! let config = supported_config.into(); //! let stream = match sample_format { //! SampleFormat::F32 => device.build_output_stream(&config, write_silence::, err_fn), @@ -117,10 +117,11 @@ //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let supported_config = device.default_output_config().unwrap(); +//! # let sample_format = supported_config.sample_format(); //! # let config = supported_config.into(); //! # let data_fn = move |_data: &mut cpal::Data| {}; //! # let err_fn = move |_err| {}; -//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap(); +//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap(); //! stream.play().unwrap(); //! ``` //! @@ -132,10 +133,11 @@ //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let supported_config = device.default_output_config().unwrap(); +//! # let sample_format = supported_config.sample_format(); //! # let config = supported_config.into(); //! # let data_fn = move |_data: &mut cpal::Data| {}; //! # let err_fn = move |_err| {}; -//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap(); +//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap(); //! stream.pause().unwrap(); //! ``` @@ -202,9 +204,9 @@ pub struct SupportedStreamConfigRange { /// `SupportedStreamConfigRange` instance or one of the `Device::default_input/output_config` methods. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SupportedStreamConfig { - pub channels: ChannelCount, - pub sample_rate: SampleRate, - pub sample_format: SampleFormat, + channels: ChannelCount, + sample_rate: SampleRate, + sample_format: SampleFormat, } /// A buffer of dynamically typed audio data, passed to raw stream callbacks. @@ -219,12 +221,22 @@ pub struct Data { } impl SupportedStreamConfig { - /// Construct a `SupportedStreamConfig` from an existing `StreamConfig`. - pub fn from_config(conf: &StreamConfig, fmt: SampleFormat) -> Self { - Self { - channels: conf.channels, - sample_rate: conf.sample_rate, - sample_format: fmt, + pub fn channels(&self) -> ChannelCount { + self.channels + } + + pub fn sample_rate(&self) -> SampleRate { + self.sample_rate + } + + pub fn sample_format(&self) -> SampleFormat { + self.sample_format + } + + pub fn config(&self) -> StreamConfig { + StreamConfig { + channels: self.channels, + sample_rate: self.sample_rate, } } } @@ -406,12 +418,7 @@ impl SupportedStreamConfigRange { impl From for StreamConfig { fn from(conf: SupportedStreamConfig) -> Self { - let channels = conf.channels; - let sample_rate = conf.sample_rate; - StreamConfig { - channels, - sample_rate, - } + conf.config() } } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 08fa5ed..cd5f529 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -257,7 +257,8 @@ macro_rules! impl_platform_host { fn build_input_stream_raw( &self, - format: &crate::SupportedStreamConfig, + config: &crate::StreamConfig, + sample_format: crate::SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -267,7 +268,13 @@ macro_rules! impl_platform_host { { match self.0 { $( - DeviceInner::$HostVariant(ref d) => d.build_input_stream_raw(format, data_callback, error_callback) + DeviceInner::$HostVariant(ref d) => d + .build_input_stream_raw( + config, + sample_format, + data_callback, + error_callback, + ) .map(StreamInner::$HostVariant) .map(Stream::from), )* @@ -276,7 +283,8 @@ macro_rules! impl_platform_host { fn build_output_stream_raw( &self, - format: &crate::SupportedStreamConfig, + config: &crate::StreamConfig, + sample_format: crate::SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -286,7 +294,13 @@ macro_rules! impl_platform_host { { match self.0 { $( - DeviceInner::$HostVariant(ref d) => d.build_output_stream_raw(format, data_callback, error_callback) + DeviceInner::$HostVariant(ref d) => d + .build_output_stream_raw( + config, + sample_format, + data_callback, + error_callback, + ) .map(StreamInner::$HostVariant) .map(Stream::from), )* diff --git a/src/traits.rs b/src/traits.rs index 16fa240..ef94aa7 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,8 +2,8 @@ use { BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices, - OutputDevices, PauseStreamError, PlayStreamError, Sample, StreamConfig, StreamError, - SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + OutputDevices, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig, + StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; /// A **Host** provides access to the available audio devices on the system. @@ -126,7 +126,8 @@ pub trait DeviceTrait { E: FnMut(StreamError) + Send + 'static, { self.build_input_stream_raw( - &SupportedStreamConfig::from_config(config, T::FORMAT), + config, + T::FORMAT, move |data| { data_callback( data.as_slice() @@ -150,7 +151,8 @@ pub trait DeviceTrait { E: FnMut(StreamError) + Send + 'static, { self.build_output_stream_raw( - &SupportedStreamConfig::from_config(config, T::FORMAT), + config, + T::FORMAT, move |data| { data_callback( data.as_slice_mut() @@ -164,7 +166,8 @@ pub trait DeviceTrait { /// Create a dynamically typed input stream. fn build_input_stream_raw( &self, - format: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -175,7 +178,8 @@ pub trait DeviceTrait { /// Create a dynamically typed output stream. fn build_output_stream_raw( &self, - format: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result From f05ba582076360630a72bd7aa9457948e085bab1 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 18:50:12 +0100 Subject: [PATCH 2/5] Update emscripten backend for SupportedStreamConfig private fields --- src/host/emscripten/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/host/emscripten/mod.rs b/src/host/emscripten/mod.rs index ee97b98..999746c 100644 --- a/src/host/emscripten/mod.rs +++ b/src/host/emscripten/mod.rs @@ -9,8 +9,8 @@ use stdweb::Reference; use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedStreamConfig, - SupportedStreamConfigRange, SupportedStreamConfigsError, + PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, + SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -154,7 +154,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - _config: &SupportedStreamConfig, + _config: &StreamConfig, + _sample_format: SampleFormat, _data_callback: D, _error_callback: E, ) -> Result @@ -167,7 +168,8 @@ impl DeviceTrait for Device { fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + _config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -176,7 +178,7 @@ impl DeviceTrait for Device { E: FnMut(StreamError) + Send + 'static, { assert_eq!( - config.sample_format, + sample_format, SampleFormat::F32, "emscripten backend currently only supports `f32` data", ); From 009b796b7cfa340b80882e73c9670271dfb20b3a Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 19:06:16 +0100 Subject: [PATCH 3/5] Update wasapi backend for SupportedStreamConfig private fields --- src/host/wasapi/device.rs | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 8d7ed34..f1e1e1b 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,6 +1,6 @@ use crate::{ BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - SampleFormat, SampleRate, SupportedStreamConfig, SupportedStreamConfigRange, + SampleFormat, SampleRate, StreamConfig, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, COMMON_SAMPLE_RATES, }; use std; @@ -98,7 +98,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -106,7 +107,7 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_input_stream_raw_inner(config)?; + let stream_inner = self.build_input_stream_raw_inner(config, sample_format)?; Ok(Stream::new_input( stream_inner, data_callback, @@ -116,7 +117,8 @@ impl DeviceTrait for Device { fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -124,7 +126,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_output_stream_raw_inner(config)?; + let stream_inner = self.build_output_stream_raw_inner(config, sample_format)?; Ok(Stream::new_output( stream_inner, data_callback, @@ -616,7 +618,8 @@ impl Device { pub(crate) fn build_input_stream_raw_inner( &self, - format: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, ) -> Result { unsafe { // Making sure that COM is initialized. @@ -638,7 +641,7 @@ impl Device { // Computing the format and initializing the device. let waveformatex = { - let format_attempt = format_to_waveformatextensible(format) + let format_attempt = config_to_waveformatextensible(config, sample_format) .ok_or(BuildStreamError::StreamConfigNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; @@ -753,14 +756,15 @@ impl Device { playing: false, max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, - sample_format: format.sample_format, + sample_format, }) } } pub(crate) fn build_output_stream_raw_inner( &self, - format: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, ) -> Result { unsafe { // Making sure that COM is initialized. @@ -782,7 +786,7 @@ impl Device { // Computing the format and initializing the device. let waveformatex = { - let format_attempt = format_to_waveformatextensible(format) + let format_attempt = config_to_waveformatextensible(config, sample_format) .ok_or(BuildStreamError::StreamConfigNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; @@ -897,7 +901,7 @@ impl Device { playing: false, max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, - sample_format: format.sample_format, + sample_format, }) } } @@ -1145,21 +1149,22 @@ pub fn default_output_device() -> Option { // Turns a `Format` into a `WAVEFORMATEXTENSIBLE`. // // Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format. -fn format_to_waveformatextensible( - config: &SupportedStreamConfig, +fn config_to_waveformatextensible( + config: &StreamConfig, + sample_format: SampleFormat, ) -> Option { - let format_tag = match config.sample_format { + let format_tag = match sample_format { SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM, SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE, SampleFormat::U16 => return None, }; let channels = config.channels as WORD; let sample_rate = config.sample_rate.0 as DWORD; - let sample_bytes = config.sample_format.sample_size() as WORD; + let sample_bytes = sample_format.sample_size() as WORD; let avg_bytes_per_sec = u32::from(channels) * sample_rate * u32::from(sample_bytes); let block_align = channels * sample_bytes; let bits_per_sample = 8 * sample_bytes; - let cb_size = match config.sample_format { + let cb_size = match sample_format { SampleFormat::I16 => 0, SampleFormat::F32 => { let extensible_size = mem::size_of::(); @@ -1183,7 +1188,7 @@ fn format_to_waveformatextensible( const KSAUDIO_SPEAKER_DIRECTOUT: DWORD = 0; let channel_mask = KSAUDIO_SPEAKER_DIRECTOUT; - let sub_format = match config.sample_format { + let sub_format = match sample_format { SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM, SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, SampleFormat::U16 => return None, From 9bf5664f7d657939fe14b0961d90bf6852331701 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 19:22:27 +0100 Subject: [PATCH 4/5] Update wasapi and emscripten backends for SupportedStreamConfig private fields --- src/host/asio/mod.rs | 14 ++++++++------ src/host/asio/stream.rs | 35 ++++++++++++++++++++--------------- src/host/coreaudio/mod.rs | 33 +++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index 917b958..a885db6 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -3,8 +3,8 @@ extern crate parking_lot; use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, StreamError, SupportedStreamConfig, - SupportedStreamConfigsError, + PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, + SupportedStreamConfig, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -84,7 +84,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -92,12 +93,13 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_input_stream_raw(self, config, data_callback, error_callback) + Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback) } fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -105,7 +107,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_output_stream_raw(self, config, data_callback, error_callback) + Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback) } } diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 42ff8ac..33f7974 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -14,6 +14,7 @@ use PauseStreamError; use PlayStreamError; use Sample; use SampleFormat; +use StreamConfig; use StreamError; use SupportedStreamConfig; @@ -59,7 +60,8 @@ impl Stream { impl Device { pub fn build_input_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, mut data_callback: D, _error_callback: E, ) -> Result @@ -70,14 +72,14 @@ impl Device { let stream_type = self.driver.input_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. - let sample_format = super::device::convert_data_type(&stream_type) + let expected_sample_format = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::StreamConfigNotSupported)?; - if config.sample_format != sample_format { + if sample_format != expected_sample_format { return Err(BuildStreamError::StreamConfigNotSupported); } let num_channels = config.channels.clone(); - let buffer_size = self.get_or_create_input_stream(config)?; + let buffer_size = self.get_or_create_input_stream(config, sample_format)?; let cpal_num_samples = buffer_size * num_channels as usize; // Create the buffer depending on the size of the data type. @@ -225,7 +227,8 @@ impl Device { pub fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, mut data_callback: D, _error_callback: E, ) -> Result @@ -236,14 +239,14 @@ impl Device { let stream_type = self.driver.output_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. - let sample_format = super::device::convert_data_type(&stream_type) + let expected_sample_format = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::StreamConfigNotSupported)?; - if config.sample_format != sample_format { + if sample_format != expected_sample_format { return Err(BuildStreamError::StreamConfigNotSupported); } let num_channels = config.channels.clone(); - let buffer_size = self.get_or_create_output_stream(config)?; + let buffer_size = self.get_or_create_output_stream(config, sample_format)?; let cpal_num_samples = buffer_size * num_channels as usize; // Create buffers depending on data type. @@ -438,12 +441,13 @@ impl Device { /// On success, the buffer size of the stream is returned. fn get_or_create_input_stream( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, ) -> Result { match self.default_input_config() { Ok(f) => { let num_asio_channels = f.channels; - check_config(&self.driver, config, num_asio_channels) + check_config(&self.driver, config, sample_format, num_asio_channels) } Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; @@ -478,12 +482,13 @@ impl Device { /// If there is no existing ASIO Output Stream it will be created. fn get_or_create_output_stream( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, ) -> Result { match self.default_output_config() { Ok(f) => { let num_asio_channels = f.channels; - check_config(&self.driver, config, num_asio_channels) + check_config(&self.driver, config, sample_format, num_asio_channels) } Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; @@ -581,13 +586,13 @@ impl AsioSample for f64 { /// Checks sample rate, data type and then finally the number of channels. fn check_config( driver: &sys::Driver, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, num_asio_channels: u16, ) -> Result<(), BuildStreamError> { - let SupportedStreamConfig { + let StreamConfig { channels, sample_rate, - sample_format, } = config; // Try and set the sample rate to what the user selected. let sample_rate = sample_rate.0.into(); diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index 61800e4..e3a699f 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -22,7 +22,8 @@ use crate::traits::{DeviceTrait, HostTrait, StreamTrait}; use crate::{ BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, - StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, }; use std::cell::RefCell; use std::ffi::CStr; @@ -104,7 +105,8 @@ impl DeviceTrait for Device { fn build_input_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -112,12 +114,13 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_input_stream_raw(self, config, data_callback, error_callback) + Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback) } fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, data_callback: D, error_callback: E, ) -> Result @@ -125,7 +128,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_output_stream_raw(self, config, data_callback, error_callback) + Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback) } } @@ -402,15 +405,17 @@ impl From for BuildStreamError { } // Create a coreaudio AudioStreamBasicDescription from a CPAL Format. -fn asbd_from_config(config: &SupportedStreamConfig) -> AudioStreamBasicDescription { +fn asbd_from_config( + config: &StreamConfig, + sample_format: SampleFormat, +) -> AudioStreamBasicDescription { let n_channels = config.channels as usize; let sample_rate = config.sample_rate.0; - let bytes_per_channel = config.sample_format.sample_size(); + let bytes_per_channel = sample_format.sample_size(); let bits_per_channel = bytes_per_channel * 8; let bytes_per_frame = n_channels * bytes_per_channel; let frames_per_packet = 1; let bytes_per_packet = frames_per_packet * bytes_per_frame; - let sample_format = config.sample_format; let format_flags = match sample_format { SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32, _ => kAudioFormatFlagIsPacked as u32, @@ -475,7 +480,8 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, mut data_callback: D, _error_callback: E, ) -> Result @@ -625,12 +631,11 @@ impl Device { let mut audio_unit = audio_unit_from_device(self, true)?; // Set the stream in interleaved mode. - let asbd = asbd_from_config(config); + let asbd = asbd_from_config(config, sample_format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. - let sample_format = config.sample_format; let bytes_per_channel = sample_format.sample_size(); type Args = render_callback::Args; audio_unit.set_input_callback(move |args: Args| unsafe { @@ -663,7 +668,8 @@ impl Device { fn build_output_stream_raw( &self, - config: &SupportedStreamConfig, + config: &StreamConfig, + sample_format: SampleFormat, mut data_callback: D, _error_callback: E, ) -> Result @@ -678,12 +684,11 @@ impl Device { let element = Element::Output; // Set the stream in interleaved mode. - let asbd = asbd_from_config(config); + let asbd = asbd_from_config(config, sample_format); audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. - let sample_format = config.sample_format; let bytes_per_channel = sample_format.sample_size(); type Args = render_callback::Args; audio_unit.set_render_callback(move |args: Args| unsafe { From 7a6cb0bd6a30328611e44b903d4124765e4d0b69 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 20:08:46 +0100 Subject: [PATCH 5/5] Make SupportedStreamConfigRange fields private --- src/lib.rs | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e97b2ac..abea8ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,13 +191,13 @@ pub struct StreamConfig { /// `Device::supported_input/output_configs` method. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SupportedStreamConfigRange { - pub channels: ChannelCount, + pub(crate) channels: ChannelCount, /// Minimum value for the samples rate of the supported formats. - pub min_sample_rate: SampleRate, + pub(crate) min_sample_rate: SampleRate, /// Maximum value for the samples rate of the supported formats. - pub max_sample_rate: SampleRate, + pub(crate) max_sample_rate: SampleRate, /// Type of data expected by the device. - pub sample_format: SampleFormat, + pub(crate) sample_format: SampleFormat, } /// Describes a single supported stream configuration, retrieved via either a @@ -335,6 +335,35 @@ impl Data { } impl SupportedStreamConfigRange { + pub fn channels(&self) -> ChannelCount { + self.channels + } + + pub fn min_sample_rate(&self) -> SampleRate { + self.min_sample_rate + } + + pub fn max_sample_rate(&self) -> SampleRate { + self.max_sample_rate + } + + pub fn sample_format(&self) -> SampleFormat { + self.sample_format + } + + /// Retrieve a `SupportedStreamConfig` with the given sample rate. + /// + /// **panic!**s if the given `sample_rate` is outside the range specified within this + /// `SupportedStreamConfigRange` instance. + pub fn with_sample_rate(self, sample_rate: SampleRate) -> SupportedStreamConfig { + assert!(sample_rate <= self.min_sample_rate && sample_rate <= self.max_sample_rate); + SupportedStreamConfig { + channels: self.channels, + sample_format: self.sample_format, + sample_rate, + } + } + /// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate. #[inline] pub fn with_max_sample_rate(self) -> SupportedStreamConfig {