From 9c781bd381615bec53b1bd27eaa37c6b19dffc0f Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 27 Jan 2020 21:28:07 +0100 Subject: [PATCH 01/11] Rename stream `Format` types to `Config` This implements the changes described at #370. This commit implements only the `null` and `alsa` backends - the rest will be implemented in follow-up commits. Closes #370. --- examples/beep.rs | 18 ++--- examples/enumerate.rs | 36 ++++----- examples/feedback.rs | 18 ++--- examples/record_wav.rs | 32 ++++---- src/error.rs | 4 +- src/host/alsa/mod.rs | 118 +++++++++++++++--------------- src/host/null/mod.rs | 41 ++++++----- src/lib.rs | 161 +++++++++++++++++++++-------------------- src/platform/mod.rs | 92 +++++++++++------------ src/traits.rs | 39 +++++----- 10 files changed, 287 insertions(+), 272 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 9df4f2b..20dc5ea 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -8,23 +8,23 @@ fn main() -> Result<(), anyhow::Error> { let device = host .default_output_device() .expect("failed to find a default output device"); - let format = device.default_output_format()?; + let config = device.default_output_config()?; - match format.data_type { - cpal::SampleFormat::F32 => run::(&device, &format.shape())?, - cpal::SampleFormat::I16 => run::(&device, &format.shape())?, - cpal::SampleFormat::U16 => run::(&device, &format.shape())?, + 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())?, } Ok(()) } -fn run(device: &cpal::Device, shape: &cpal::Shape) -> Result<(), anyhow::Error> +fn run(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> where T: cpal::Sample, { - let sample_rate = shape.sample_rate.0 as f32; - let channels = shape.channels as usize; + let sample_rate = config.sample_rate.0 as f32; + let channels = config.channels as usize; // Produce a sinusoid of maximum amplitude. let mut sample_clock = 0f32; @@ -36,7 +36,7 @@ where let err_fn = |err| eprintln!("an error occurred on stream: {}", err); let stream = device.build_output_stream( - shape, + config, move |data: &mut [T]| write_data(data, channels, &mut next_value), err_fn, )?; diff --git a/examples/enumerate.rs b/examples/enumerate.rs index 56542eb..08606ae 100644 --- a/examples/enumerate.rs +++ b/examples/enumerate.rs @@ -21,48 +21,48 @@ fn main() -> Result<(), anyhow::Error> { for (device_index, device) in devices.enumerate() { println!(" {}. \"{}\"", device_index + 1, device.name()?); - // Input formats - if let Ok(fmt) = device.default_input_format() { - println!(" Default input stream format:\n {:?}", fmt); + // Input configs + if let Ok(fmt) = device.default_input_config() { + println!(" Default input stream config:\n {:?}", fmt); } - let mut input_formats = match device.supported_input_formats() { + let mut input_configs = match device.supported_input_configs() { Ok(f) => f.peekable(), Err(e) => { println!("Error: {:?}", e); continue; } }; - if input_formats.peek().is_some() { - println!(" All supported input stream formats:"); - for (format_index, format) in input_formats.enumerate() { + if input_configs.peek().is_some() { + println!(" All supported input stream configs:"); + for (config_index, config) in input_configs.enumerate() { println!( " {}.{}. {:?}", device_index + 1, - format_index + 1, - format + config_index + 1, + config ); } } - // Output formats - if let Ok(fmt) = device.default_output_format() { - println!(" Default output stream format:\n {:?}", fmt); + // Output configs + if let Ok(fmt) = device.default_output_config() { + println!(" Default output stream config:\n {:?}", fmt); } - let mut output_formats = match device.supported_output_formats() { + let mut output_configs = match device.supported_output_configs() { Ok(f) => f.peekable(), Err(e) => { println!("Error: {:?}", e); continue; } }; - if output_formats.peek().is_some() { - println!(" All supported output stream formats:"); - for (format_index, format) in output_formats.enumerate() { + if output_configs.peek().is_some() { + println!(" All supported output stream configs:"); + for (config_index, config) in output_configs.enumerate() { println!( " {}.{}. {:?}", device_index + 1, - format_index + 1, - format + config_index + 1, + config ); } } diff --git a/examples/feedback.rs b/examples/feedback.rs index 06d3970..2601136 100644 --- a/examples/feedback.rs +++ b/examples/feedback.rs @@ -1,7 +1,7 @@ //! Feeds back the input stream directly into the output stream. //! -//! Assumes that the input and output devices can use the same stream format and that they support -//! the f32 sample format. +//! Assumes that the input and output devices can use the same stream configuration and that they +//! support the f32 sample format. //! //! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not //! precisely synchronised. @@ -28,12 +28,12 @@ fn main() -> Result<(), anyhow::Error> { println!("Using default input device: \"{}\"", input_device.name()?); println!("Using default output device: \"{}\"", output_device.name()?); - // We'll try and use the same format between streams to keep it simple - let shape = input_device.default_input_format()?.shape(); + // We'll try and use the same configuration between streams to keep it simple. + let config: cpal::StreamConfig = input_device.default_input_config()?.into(); // Create a delay in case the input and output devices aren't synced. - let latency_frames = (LATENCY_MS / 1_000.0) * shape.sample_rate.0 as f32; - let latency_samples = latency_frames as usize * shape.channels as usize; + let latency_frames = (LATENCY_MS / 1_000.0) * config.sample_rate.0 as f32; + let latency_samples = latency_frames as usize * config.channels as usize; // The buffer to share samples let ring = RingBuffer::new(latency_samples * 2); @@ -80,10 +80,10 @@ fn main() -> Result<(), anyhow::Error> { // Build streams. println!( "Attempting to build both streams with f32 samples and `{:?}`.", - shape + config ); - let input_stream = input_device.build_input_stream(&shape, input_data_fn, err_fn)?; - let output_stream = output_device.build_output_stream(&shape, output_data_fn, err_fn)?; + let input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn)?; + let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn)?; println!("Successfully built streams."); // Play the streams. diff --git a/examples/record_wav.rs b/examples/record_wav.rs index 96a8b93..793b781 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -1,4 +1,4 @@ -//! Records a WAV file (roughly 3 seconds long) using the default input device and format. +//! Records a WAV file (roughly 3 seconds long) using the default input device and config. //! //! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav". @@ -15,18 +15,18 @@ fn main() -> Result<(), anyhow::Error> { // Use the default host for working with audio devices. let host = cpal::default_host(); - // Setup the default input device and stream with the default input format. + // Setup the default input device and stream with the default input config. let device = host .default_input_device() .expect("Failed to get default input device"); println!("Default input device: {}", device.name()?); - let format = device - .default_input_format() - .expect("Failed to get default input format"); - println!("Default input format: {:?}", format); + let config = device + .default_input_config() + .expect("Failed to get default input config"); + println!("Default input config: {:?}", config); // The WAV file we're recording to. const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); - let spec = wav_spec_from_format(&format); + let spec = wav_spec_from_config(&config); let writer = hound::WavWriter::create(PATH, spec)?; let writer = Arc::new(Mutex::new(Some(writer))); @@ -40,19 +40,19 @@ fn main() -> Result<(), anyhow::Error> { eprintln!("an error occurred on stream: {}", err); }; - let stream = match format.data_type { + let stream = match config.sample_format { cpal::SampleFormat::F32 => device.build_input_stream( - &format.shape(), + &config.into(), move |data| write_input_data::(data, &writer_2), err_fn, )?, cpal::SampleFormat::I16 => device.build_input_stream( - &format.shape(), + &config.into(), move |data| write_input_data::(data, &writer_2), err_fn, )?, cpal::SampleFormat::U16 => device.build_input_stream( - &format.shape(), + &config.into(), move |data| write_input_data::(data, &writer_2), err_fn, )?, @@ -76,12 +76,12 @@ fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { } } -fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec { +fn wav_spec_from_config(config: &cpal::SupportedStreamConfig) -> hound::WavSpec { hound::WavSpec { - channels: format.channels as _, - sample_rate: format.sample_rate.0 as _, - bits_per_sample: (format.data_type.sample_size() * 8) as _, - sample_format: sample_format(format.data_type), + 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/error.rs b/src/error.rs index 0a87bbe..90b6dbb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -47,7 +47,7 @@ pub enum DeviceNameError { /// Error that can happen when enumerating the list of supported formats. #[derive(Debug, Error)] -pub enum SupportedFormatsError { +pub enum SupportedStreamConfigsError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[error("The requested device is no longer available. For example, it has been unplugged.")] @@ -67,7 +67,7 @@ pub enum SupportedFormatsError { /// May occur when attempting to request the default input or output stream format from a `Device`. #[derive(Debug, Error)] -pub enum DefaultFormatError { +pub enum DefaultStreamConfigError { /// The device no longer exists. This can happen if the device is disconnected while the /// program is running. #[error("The requested device is no longer available. For example, it has been unplugged.")] diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index d556359..6050d74 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -2,9 +2,9 @@ extern crate alsa_sys as alsa; extern crate libc; use crate::{ - BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError, - DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat, - SampleRate, StreamError, SupportedFormat, SupportedFormatsError, + BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, + DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, + StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; use std::sync::Arc; use std::thread::{self, JoinHandle}; @@ -14,8 +14,8 @@ use traits::{DeviceTrait, HostTrait, StreamTrait}; pub use self::enumerate::{default_input_device, default_output_device, Devices}; -pub type SupportedInputFormats = VecIntoIter; -pub type SupportedOutputFormats = VecIntoIter; +pub type SupportedInputConfigs = VecIntoIter; +pub type SupportedOutputConfigs = VecIntoIter; mod enumerate; @@ -52,37 +52,37 @@ impl HostTrait for Host { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { Device::name(self) } - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } - 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, + conf: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -90,14 +90,14 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?; + let stream_inner = self.build_stream_inner(conf, 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, - format: &Format, + conf: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -105,7 +105,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?; + let stream_inner = self.build_stream_inner(conf, alsa::SND_PCM_STREAM_PLAYBACK)?; let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback); Ok(stream) } @@ -161,7 +161,7 @@ pub struct Device(String); impl Device { fn build_stream_inner( &self, - format: &Format, + conf: &SupportedStreamConfig, stream_type: alsa::snd_pcm_stream_t, ) -> Result { let name = ffi::CString::new(self.0.clone()).expect("unable to clone device"); @@ -185,13 +185,13 @@ impl Device { }; let can_pause = unsafe { let hw_params = HwParams::alloc(); - set_hw_params_from_format(handle, &hw_params, format) + set_hw_params_from_format(handle, &hw_params, conf) .map_err(|description| BackendSpecificError { description })?; alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1 }; let (buffer_len, period_len) = unsafe { - set_sw_params_from_format(handle, format) + set_sw_params_from_format(handle, conf) .map_err(|description| BackendSpecificError { description })? }; @@ -213,9 +213,9 @@ impl Device { let stream_inner = StreamInner { channel: handle, - sample_format: format.data_type, + sample_format: conf.sample_format, num_descriptors, - num_channels: format.channels as u16, + num_channels: conf.channels as u16, buffer_len, period_len, can_pause, @@ -235,10 +235,10 @@ impl Device { Ok(self.0.clone()) } - unsafe fn supported_formats( + unsafe fn supported_configs( &self, stream_t: alsa::snd_pcm_stream_t, - ) -> Result, SupportedFormatsError> { + ) -> Result, SupportedStreamConfigsError> { let mut handle = ptr::null_mut(); let device_name = match ffi::CString::new(&self.0[..]) { Ok(name) => name, @@ -256,8 +256,8 @@ impl Device { alsa::SND_PCM_NONBLOCK, ) { -2 | - -16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable), - -22 => return Err(SupportedFormatsError::InvalidArgument), + -16 /* determined empirically */ => return Err(SupportedStreamConfigsError::DeviceNotAvailable), + -22 => return Err(SupportedStreamConfigsError::InvalidArgument), e => if let Err(description) = check_errors(e) { let err = BackendSpecificError { description }; return Err(err.into()) @@ -402,14 +402,14 @@ impl Device { let mut output = Vec::with_capacity( supported_formats.len() * supported_channels.len() * sample_rates.len(), ); - for &data_type in supported_formats.iter() { + for &sample_format in supported_formats.iter() { for channels in supported_channels.iter() { for &(min_rate, max_rate) in sample_rates.iter() { - output.push(SupportedFormat { + output.push(SupportedStreamConfigRange { channels: channels.clone(), min_sample_rate: SampleRate(min_rate as u32), max_sample_rate: SampleRate(max_rate as u32), - data_type: data_type, + sample_format: sample_format, }); } } @@ -420,31 +420,35 @@ impl Device { Ok(output.into_iter()) } - fn supported_input_formats(&self) -> Result { - unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) } + fn supported_input_configs( + &self, + ) -> Result { + unsafe { self.supported_configs(alsa::SND_PCM_STREAM_CAPTURE) } } - fn supported_output_formats(&self) -> Result { - unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) } + fn supported_output_configs( + &self, + ) -> Result { + unsafe { self.supported_configs(alsa::SND_PCM_STREAM_PLAYBACK) } } // ALSA does not offer default stream formats, so instead we compare all supported formats by - // the `SupportedFormat::cmp_default_heuristics` order and select the greatest. - fn default_format( + // the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest. + fn default_config( &self, stream_t: alsa::snd_pcm_stream_t, - ) -> Result { + ) -> Result { let mut formats: Vec<_> = unsafe { - match self.supported_formats(stream_t) { - Err(SupportedFormatsError::DeviceNotAvailable) => { - return Err(DefaultFormatError::DeviceNotAvailable); + match self.supported_configs(stream_t) { + Err(SupportedStreamConfigsError::DeviceNotAvailable) => { + return Err(DefaultStreamConfigError::DeviceNotAvailable); } - Err(SupportedFormatsError::InvalidArgument) => { + Err(SupportedStreamConfigsError::InvalidArgument) => { // this happens sometimes when querying for input and output capabilities but // the device supports only one - return Err(DefaultFormatError::StreamTypeNotSupported); + return Err(DefaultStreamConfigError::StreamTypeNotSupported); } - Err(SupportedFormatsError::BackendSpecific { err }) => { + Err(SupportedStreamConfigsError::BackendSpecific { err }) => { return Err(err.into()); } Ok(fmts) => fmts.collect(), @@ -464,16 +468,16 @@ impl Device { } Ok(format) } - None => Err(DefaultFormatError::StreamTypeNotSupported), + None => Err(DefaultStreamConfigError::StreamTypeNotSupported), } } - fn default_input_format(&self) -> Result { - self.default_format(alsa::SND_PCM_STREAM_CAPTURE) + fn default_input_config(&self) -> Result { + self.default_config(alsa::SND_PCM_STREAM_CAPTURE) } - fn default_output_format(&self) -> Result { - self.default_format(alsa::SND_PCM_STREAM_PLAYBACK) + fn default_output_config(&self) -> Result { + self.default_config(alsa::SND_PCM_STREAM_PLAYBACK) } } @@ -900,7 +904,7 @@ 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)); @@ -913,14 +917,14 @@ unsafe fn set_hw_params_from_format( return Err(format!("handle not acessible: {}", e)); } - let data_type = if cfg!(target_endian = "big") { - match format.data_type { + let sample_format = if cfg!(target_endian = "big") { + match format.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.data_type { + match format.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, @@ -930,7 +934,7 @@ unsafe fn set_hw_params_from_format( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format( pcm_handle, hw_params.0, - data_type, + sample_format, )) { return Err(format!("format could not be set: {}", e)); } @@ -969,7 +973,7 @@ unsafe fn set_hw_params_from_format( unsafe fn set_sw_params_from_format( pcm_handle: *mut alsa::snd_pcm_t, - format: &Format, + format: &SupportedStreamConfig, ) -> 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)) { diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 2cdbff2..2aab365 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -1,6 +1,7 @@ use crate::{ - BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + PauseStreamError, PlayStreamError, StreamError, SupportedStreamConfig, + SupportedStreamConfigRange, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -15,8 +16,8 @@ pub struct Host; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Stream; -pub struct SupportedInputFormats; -pub struct SupportedOutputFormats; +pub struct SupportedInputConfigs; +pub struct SupportedOutputConfigs; impl Host { #[allow(dead_code)] @@ -32,8 +33,8 @@ impl Devices { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; #[inline] @@ -42,28 +43,32 @@ impl DeviceTrait for 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 { unimplemented!() } #[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 { unimplemented!() } fn build_input_stream_raw( &self, - _format: &Format, + _format: &SupportedStreamConfig, _data_callback: D, _error_callback: E, ) -> Result @@ -77,7 +82,7 @@ impl DeviceTrait for Device { /// Create an output stream. fn build_output_stream_raw( &self, - _format: &Format, + _format: &SupportedStreamConfig, _data_callback: D, _error_callback: E, ) -> Result @@ -129,20 +134,20 @@ impl Iterator for Devices { } } -impl Iterator for SupportedInputFormats { - type Item = SupportedFormat; +impl Iterator for SupportedInputConfigs { + type Item = SupportedStreamConfigRange; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { None } } -impl Iterator for SupportedOutputFormats { - type Item = SupportedFormat; +impl Iterator for SupportedOutputConfigs { + type Item = SupportedStreamConfigRange; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { None } } diff --git a/src/lib.rs b/src/lib.rs index f05cb7e..cf98a67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,24 +31,25 @@ //! let device = host.default_output_device().expect("no output device available"); //! ``` //! -//! Before we can create a stream, we must decide what the format of the audio samples is going to -//! be. You can query all the supported formats with the `supported_input_formats()` and -//! `supported_output_formats()` methods. These produce a list of `SupportedFormat` structs which -//! can later be turned into actual `Format` structs. If you don't want to query the list of -//! formats, you can also build your own `Format` manually, but doing so could lead to an error -//! when building the stream if the format is not supported by the device. +//! Before we can create a stream, we must decide what the configuration of the audio stream is +//! going to be. You can query all the supported configurations with the +//! `supported_input_configs()` and `supported_output_configs()` methods. These produce a list of +//! `SupportedStreamConfigRange` structs which can later be turned into actual +//! `SupportedStreamConfig` structs. If you don't want to query the list of configs, you can also +//! build your own `StreamConfig` manually, but doing so could lead to an error when building the +//! stream if the config is not supported by the device. //! -//! > **Note**: the `supported_formats()` method could return an error for example if the device -//! > has been disconnected. +//! > **Note**: the `supported_input/output_configs()` methods could return an error for example if +//! > the device has been disconnected. //! //! ```no_run //! use cpal::traits::{DeviceTrait, HostTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); -//! let mut supported_formats_range = device.supported_output_formats() -//! .expect("error while querying formats"); -//! let format = supported_formats_range.next() -//! .expect("no supported format?!") +//! let mut supported_configs_range = device.supported_output_configs() +//! .expect("error while querying configs"); +//! let supported_config = supported_configs_range.next() +//! .expect("no supported config?!") //! .with_max_sample_rate(); //! ``` //! @@ -59,9 +60,9 @@ //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); -//! # let shape = device.default_output_format().unwrap().shape(); +//! # let config = device.default_output_config().unwrap().into(); //! let stream = device.build_output_stream( -//! &shape, +//! &config, //! move |data: &mut [f32]| { //! // react to stream events and read or write stream data here. //! }, @@ -91,13 +92,14 @@ //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); -//! # let format = device.default_output_format().unwrap(); +//! # let supported_config = device.default_output_config().unwrap(); //! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err); -//! let shape = format.shape(); -//! let stream = match format.data_type { -//! SampleFormat::F32 => device.build_output_stream(&shape, write_silence::, err_fn), -//! SampleFormat::I16 => device.build_output_stream(&shape, write_silence::, err_fn), -//! SampleFormat::U16 => device.build_output_stream(&shape, write_silence::, err_fn), +//! 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), +//! SampleFormat::I16 => device.build_output_stream(&config, write_silence::, err_fn), +//! SampleFormat::U16 => device.build_output_stream(&config, write_silence::, err_fn), //! }.unwrap(); //! //! fn write_silence(data: &mut [T]) { @@ -114,10 +116,11 @@ //! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); -//! # let format = device.default_output_format().unwrap(); +//! # let supported_config = device.default_output_config().unwrap(); +//! # 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(&format, data_fn, err_fn).unwrap(); +//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap(); //! stream.play().unwrap(); //! ``` //! @@ -128,10 +131,11 @@ //! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); -//! # let format = device.default_output_format().unwrap(); +//! # let supported_config = device.default_output_config().unwrap(); +//! # 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(&format, data_fn, err_fn).unwrap(); +//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap(); //! stream.pause().unwrap(); //! ``` @@ -149,7 +153,7 @@ extern crate thiserror; pub use error::*; pub use platform::{ available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream, - SupportedInputFormats, SupportedOutputFormats, ALL_HOSTS, + SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS, }; pub use samples_formats::{Sample, SampleFormat}; @@ -172,56 +176,35 @@ pub type ChannelCount = u16; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct SampleRate(pub u32); -/// The format of an input or output audio stream. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Format { - pub channels: ChannelCount, - pub sample_rate: SampleRate, - pub data_type: SampleFormat, -} - -impl Format { - /// Construct a format having a particular `shape` and `data_type`. - pub fn with_shape(shape: &Shape, data_type: SampleFormat) -> Self { - Self { - channels: shape.channels, - sample_rate: shape.sample_rate, - data_type, - } - } - - /// Extract aspects of the format independent of the data type. - pub fn shape(&self) -> Shape { - self.clone().into() - } -} - -/// The properties of an input or output audio stream excluding its data type. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Shape { +/// The set of parameters used to describe how to open a stream. +/// +/// The sample format is omitted in favour of using a sample type. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct StreamConfig { pub channels: ChannelCount, pub sample_rate: SampleRate, } -impl From for Shape { - fn from(x: Format) -> Self { - Self { - channels: x.channels, - sample_rate: x.sample_rate, - } - } -} - -/// Describes a range of supported stream formats. +/// Describes a range of supported stream configurations, retrieved via the +/// `Device::supported_input/output_configs` method. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct SupportedFormat { +pub struct SupportedStreamConfigRange { pub channels: ChannelCount, /// Minimum value for the samples rate of the supported formats. pub min_sample_rate: SampleRate, /// Maximum value for the samples rate of the supported formats. pub max_sample_rate: SampleRate, /// Type of data expected by the device. - pub data_type: SampleFormat, + pub sample_format: SampleFormat, +} + +/// Describes a single supported stream configuration, retrieved via either a +/// `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, } /// A buffer of dynamically typed audio data, passed to raw stream callbacks. @@ -235,6 +218,17 @@ pub struct Data { sample_format: SampleFormat, } +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, + } + } +} + impl Data { // Internal constructor for host implementations to use. // @@ -328,25 +322,25 @@ impl Data { } } -impl SupportedFormat { - /// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. +impl SupportedStreamConfigRange { + /// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate. #[inline] - pub fn with_max_sample_rate(self) -> Format { - Format { + pub fn with_max_sample_rate(self) -> SupportedStreamConfig { + SupportedStreamConfig { channels: self.channels, sample_rate: self.max_sample_rate, - data_type: self.data_type, + sample_format: self.sample_format, } } - /// A comparison function which compares two `SupportedFormat`s in terms of their priority of + /// A comparison function which compares two `SupportedStreamConfigRange`s in terms of their priority of /// use as a default stream format. /// /// Some backends do not provide a default stream format for their audio devices. In these /// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we /// use the "greatest" of all supported stream formats when compared with this method. /// - /// Formats are prioritised by the following heuristics: + /// SupportedStreamConfigs are prioritised by the following heuristics: /// /// **Channels**: /// @@ -382,17 +376,17 @@ impl SupportedFormat { return cmp_channels; } - let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32)); + let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32)); if cmp_f32 != Equal { return cmp_f32; } - let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16)); + let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16)); if cmp_i16 != Equal { return cmp_i16; } - let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16)); + let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16)); if cmp_u16 != Equal { return cmp_u16; } @@ -410,14 +404,25 @@ impl SupportedFormat { } } -impl From for SupportedFormat { +impl From for StreamConfig { + fn from(conf: SupportedStreamConfig) -> Self { + let channels = conf.channels; + let sample_rate = conf.sample_rate; + StreamConfig { + channels, + sample_rate, + } + } +} + +impl From for SupportedStreamConfigRange { #[inline] - fn from(format: Format) -> SupportedFormat { - SupportedFormat { + fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange { + SupportedStreamConfigRange { channels: format.channels, min_sample_rate: format.sample_rate, max_sample_rate: format.sample_rate, - data_type: format.data_type, + sample_format: format.sample_format, } } } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 6296ce5..08fa5ed 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -27,8 +27,8 @@ pub use self::platform_impl::*; // } // ``` // -// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats, -// SupportedOutputFormats and all their necessary trait implementations. +// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputConfigs, +// SupportedOutputConfigs and all their necessary trait implementations. // ``` macro_rules! impl_platform_host { ($($HostVariant:ident $host_mod:ident $host_name:literal),*) => { @@ -67,13 +67,13 @@ macro_rules! impl_platform_host { // TODO: Confirm this and add more specific detail and references. pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms); - /// The **SupportedInputFormats** iterator associated with the platform's dynamically + /// The **SupportedInputConfigs** iterator associated with the platform's dynamically /// dispatched **Host** type. - pub struct SupportedInputFormats(SupportedInputFormatsInner); + pub struct SupportedInputConfigs(SupportedInputConfigsInner); - /// The **SupportedOutputFormats** iterator associated with the platform's dynamically + /// The **SupportedOutputConfigs** iterator associated with the platform's dynamically /// dispatched **Host** type. - pub struct SupportedOutputFormats(SupportedOutputFormatsInner); + pub struct SupportedOutputConfigs(SupportedOutputConfigsInner); /// Unique identifier for available hosts on the platform. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] @@ -107,15 +107,15 @@ macro_rules! impl_platform_host { )* } - enum SupportedInputFormatsInner { + enum SupportedInputConfigsInner { $( - $HostVariant(crate::host::$host_mod::SupportedInputFormats), + $HostVariant(crate::host::$host_mod::SupportedInputConfigs), )* } - enum SupportedOutputFormatsInner { + enum SupportedOutputConfigsInner { $( - $HostVariant(crate::host::$host_mod::SupportedOutputFormats), + $HostVariant(crate::host::$host_mod::SupportedOutputConfigs), )* } @@ -162,13 +162,13 @@ macro_rules! impl_platform_host { } } - impl Iterator for SupportedInputFormats { - type Item = crate::SupportedFormat; + impl Iterator for SupportedInputConfigs { + type Item = crate::SupportedStreamConfigRange; fn next(&mut self) -> Option { match self.0 { $( - SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(), + SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(), )* } } @@ -176,19 +176,19 @@ macro_rules! impl_platform_host { fn size_hint(&self) -> (usize, Option) { match self.0 { $( - SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(), + SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(), )* } } } - impl Iterator for SupportedOutputFormats { - type Item = crate::SupportedFormat; + impl Iterator for SupportedOutputConfigs { + type Item = crate::SupportedStreamConfigRange; fn next(&mut self) -> Option { match self.0 { $( - SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(), + SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(), )* } } @@ -196,15 +196,15 @@ macro_rules! impl_platform_host { fn size_hint(&self) -> (usize, Option) { match self.0 { $( - SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(), + SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(), )* } } } impl crate::traits::DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { @@ -215,49 +215,49 @@ macro_rules! impl_platform_host { } } - fn supported_input_formats(&self) -> Result { + fn supported_input_configs(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => { - d.supported_input_formats() - .map(SupportedInputFormatsInner::$HostVariant) - .map(SupportedInputFormats) + d.supported_input_configs() + .map(SupportedInputConfigsInner::$HostVariant) + .map(SupportedInputConfigs) } )* } } - fn supported_output_formats(&self) -> Result { + fn supported_output_configs(&self) -> Result { match self.0 { $( DeviceInner::$HostVariant(ref d) => { - d.supported_output_formats() - .map(SupportedOutputFormatsInner::$HostVariant) - .map(SupportedOutputFormats) + d.supported_output_configs() + .map(SupportedOutputConfigsInner::$HostVariant) + .map(SupportedOutputConfigs) } )* } } - fn default_input_format(&self) -> Result { + fn default_input_config(&self) -> Result { match self.0 { $( - DeviceInner::$HostVariant(ref d) => d.default_input_format(), + DeviceInner::$HostVariant(ref d) => d.default_input_config(), )* } } - fn default_output_format(&self) -> Result { + fn default_output_config(&self) -> Result { match self.0 { $( - DeviceInner::$HostVariant(ref d) => d.default_output_format(), + DeviceInner::$HostVariant(ref d) => d.default_output_config(), )* } } fn build_input_stream_raw( &self, - format: &crate::Format, + format: &crate::SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -276,7 +276,7 @@ macro_rules! impl_platform_host { fn build_output_stream_raw( &self, - format: &crate::Format, + format: &crate::SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -436,8 +436,8 @@ macro_rules! impl_platform_host { mod platform_impl { pub use crate::host::alsa::{ Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream, - SupportedInputFormats as AlsaSupportedInputFormats, - SupportedOutputFormats as AlsaSupportedOutputFormats, + SupportedInputConfigs as AlsaSupportedInputConfigs, + SupportedOutputConfigs as AlsaSupportedOutputConfigs, }; impl_platform_host!(Alsa alsa "ALSA"); @@ -454,8 +454,8 @@ mod platform_impl { mod platform_impl { pub use crate::host::coreaudio::{ Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost, - Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats, - SupportedOutputFormats as CoreAudioSupportedOutputFormats, + Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs, + SupportedOutputConfigs as CoreAudioSupportedOutputConfigs, }; impl_platform_host!(CoreAudio coreaudio "CoreAudio"); @@ -472,8 +472,8 @@ mod platform_impl { mod platform_impl { pub use crate::host::emscripten::{ Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost, - Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats, - SupportedOutputFormats as EmscriptenSupportedOutputFormats, + Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs, + SupportedOutputConfigs as EmscriptenSupportedOutputConfigs, }; impl_platform_host!(Emscripten emscripten "Emscripten"); @@ -491,13 +491,13 @@ mod platform_impl { #[cfg(feature = "asio")] pub use crate::host::asio::{ Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream, - SupportedInputFormats as AsioSupportedInputFormats, - SupportedOutputFormats as AsioSupportedOutputFormats, + SupportedInputConfigs as AsioSupportedInputConfigs, + SupportedOutputConfigs as AsioSupportedOutputConfigs, }; pub use crate::host::wasapi::{ Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost, - Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats, - SupportedOutputFormats as WasapiSupportedOutputFormats, + Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs, + SupportedOutputConfigs as WasapiSupportedOutputConfigs, }; #[cfg(feature = "asio")] @@ -526,8 +526,8 @@ mod platform_impl { mod platform_impl { pub use crate::host::null::{ Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost, - StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats, - SupportedOutputFormats as NullSupportedOutputFormats, + StreamId as NullStreamId, SupportedInputConfigs as NullSupportedInputConfigs, + SupportedOutputConfigs as NullSupportedOutputConfigs, }; impl_platform_host!(Null null "Null"); diff --git a/src/traits.rs b/src/traits.rs index 7ab61b7..16fa240 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,9 +1,9 @@ //! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs. use { - BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError, - SupportedFormat, SupportedFormatsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices, + OutputDevices, PauseStreamError, PlayStreamError, Sample, StreamConfig, StreamError, + SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; /// A **Host** provides access to the available audio devices on the system. @@ -56,7 +56,7 @@ pub trait HostTrait { fn input_devices(&self) -> Result, DevicesError> { fn supports_input(device: &D) -> bool { device - .supported_input_formats() + .supported_input_configs() .map(|mut iter| iter.next().is_some()) .unwrap_or(false) } @@ -70,7 +70,7 @@ pub trait HostTrait { fn output_devices(&self) -> Result, DevicesError> { fn supports_output(device: &D) -> bool { device - .supported_output_formats() + .supported_output_configs() .map(|mut iter| iter.next().is_some()) .unwrap_or(false) } @@ -84,9 +84,9 @@ pub trait HostTrait { /// methods that involve a device return a `Result` allowing the user to handle this case. pub trait DeviceTrait { /// The iterator type yielding supported input stream formats. - type SupportedInputFormats: Iterator; + type SupportedInputConfigs: Iterator; /// The iterator type yielding supported output stream formats. - type SupportedOutputFormats: Iterator; + type SupportedOutputConfigs: Iterator; /// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`. type Stream: StreamTrait; @@ -96,26 +96,27 @@ pub trait DeviceTrait { /// An iterator yielding formats that are supported by the backend. /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). - fn supported_input_formats(&self) - -> Result; + fn supported_input_configs( + &self, + ) -> Result; /// An iterator yielding output stream formats that are supported by the device. /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result; + ) -> Result; /// The default input stream format for the device. - fn default_input_format(&self) -> Result; + fn default_input_config(&self) -> Result; /// The default output stream format for the device. - fn default_output_format(&self) -> Result; + fn default_output_config(&self) -> Result; /// Create an input stream. fn build_input_stream( &self, - shape: &Shape, + config: &StreamConfig, mut data_callback: D, error_callback: E, ) -> Result @@ -125,7 +126,7 @@ pub trait DeviceTrait { E: FnMut(StreamError) + Send + 'static, { self.build_input_stream_raw( - &Format::with_shape(shape, T::FORMAT), + &SupportedStreamConfig::from_config(config, T::FORMAT), move |data| { data_callback( data.as_slice() @@ -139,7 +140,7 @@ pub trait DeviceTrait { /// Create an output stream. fn build_output_stream( &self, - shape: &Shape, + config: &StreamConfig, mut data_callback: D, error_callback: E, ) -> Result @@ -149,7 +150,7 @@ pub trait DeviceTrait { E: FnMut(StreamError) + Send + 'static, { self.build_output_stream_raw( - &Format::with_shape(shape, T::FORMAT), + &SupportedStreamConfig::from_config(config, T::FORMAT), move |data| { data_callback( data.as_slice_mut() @@ -163,7 +164,7 @@ pub trait DeviceTrait { /// Create a dynamically typed input stream. fn build_input_stream_raw( &self, - format: &Format, + format: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -174,7 +175,7 @@ pub trait DeviceTrait { /// Create a dynamically typed output stream. fn build_output_stream_raw( &self, - format: &Format, + format: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result From 669aada98adcc6e93853c952e83392ca06a6a1cc Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 27 Jan 2020 21:44:52 +0100 Subject: [PATCH 02/11] Update emscripten backend for stream format renaming --- src/host/emscripten/mod.rs | 62 ++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/host/emscripten/mod.rs b/src/host/emscripten/mod.rs index 1b2d797..ee97b98 100644 --- a/src/host/emscripten/mod.rs +++ b/src/host/emscripten/mod.rs @@ -8,9 +8,9 @@ use stdweb::web::TypedArray; use stdweb::Reference; use crate::{ - BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedFormat, - SupportedFormatsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedStreamConfig, + SupportedStreamConfigRange, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -37,8 +37,8 @@ pub struct Stream { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StreamId(usize); -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 { @@ -60,12 +60,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 @@ -74,25 +78,25 @@ 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, + sample_format: ::SampleFormat::F32, }] .into_iter()) } - fn default_input_format(&self) -> Result { + fn default_input_config(&self) -> Result { unimplemented!(); } - fn default_output_format(&self) -> Result { - // TODO: because it is hard coded, see supported_output_formats. - Ok(Format { + fn default_output_config(&self) -> Result { + // TODO: because it is hard coded, see supported_output_configs. + Ok(SupportedStreamConfig { channels: 2, sample_rate: ::SampleRate(44100), - data_type: ::SampleFormat::F32, + sample_format: ::SampleFormat::F32, }) } } @@ -120,37 +124,37 @@ impl HostTrait for Host { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { Device::name(self) } - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } - 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: &SupportedStreamConfig, _data_callback: D, _error_callback: E, ) -> Result @@ -163,7 +167,7 @@ impl DeviceTrait for Device { fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -172,7 +176,7 @@ impl DeviceTrait for Device { E: FnMut(StreamError) + Send + 'static, { assert_eq!( - format.data_type, + config.sample_format, SampleFormat::F32, "emscripten backend currently only supports `f32` data", ); From e0da586f77e8246898425d3c4f33bb37c92a1179 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 28 Jan 2020 00:26:15 +0100 Subject: [PATCH 03/11] Update wasapi backend for stream format renaming --- src/host/wasapi/device.rs | 112 ++++++++++++++++++++------------------ src/host/wasapi/mod.rs | 4 +- 2 files changed, 61 insertions(+), 55 deletions(-) diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 69c82f2..65d6263 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,6 +1,7 @@ use crate::{ - BackendSpecificError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - SampleFormat, SampleRate, SupportedFormat, SupportedFormatsError, COMMON_SAMPLE_RATES, + BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + SampleFormat, SampleRate, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, COMMON_SAMPLE_RATES, }; use std; use std::ffi::OsString; @@ -49,8 +50,8 @@ use super::{ }; use crate::{traits::DeviceTrait, BuildStreamError, StreamError}; -pub type SupportedInputFormats = std::vec::IntoIter; -pub type SupportedOutputFormats = std::vec::IntoIter; +pub type SupportedInputConfigs = std::vec::IntoIter; +pub type SupportedOutputConfigs = std::vec::IntoIter; /// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers. #[derive(Copy, Clone)] @@ -67,37 +68,37 @@ pub struct Device { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { Device::name(self) } - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } - 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: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -105,7 +106,7 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - let stream_inner = self.build_input_stream_raw_inner(format)?; + let stream_inner = self.build_input_stream_raw_inner(config)?; Ok(Stream::new_input( stream_inner, data_callback, @@ -115,7 +116,7 @@ impl DeviceTrait for Device { fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -123,7 +124,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(format)?; + let stream_inner = self.build_output_stream_raw_inner(config)?; Ok(Stream::new_output( stream_inner, data_callback, @@ -214,7 +215,7 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow pub unsafe fn is_format_supported( client: *const IAudioClient, waveformatex_ptr: *const mmreg::WAVEFORMATEX, -) -> Result { +) -> Result { /* // `IsFormatSupported` checks whether the format is supported and fills // a `WAVEFORMATEX` @@ -258,7 +259,7 @@ pub unsafe fn is_format_supported( // has been found, but not an exact match) so we also treat this as unsupported. match (result, check_result(result)) { (_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - Err(SupportedFormatsError::DeviceNotAvailable) + Err(SupportedStreamConfigsError::DeviceNotAvailable) } (_, Err(_)) => Ok(false), (winerror::S_FALSE, _) => Ok(false), @@ -291,11 +292,11 @@ pub unsafe fn is_format_supported( // Get a cpal Format from a WAVEFORMATEX. unsafe fn format_from_waveformatex_ptr( waveformatex_ptr: *const mmreg::WAVEFORMATEX, -) -> Option { +) -> Option { fn cmp_guid(a: &GUID, b: &GUID) -> bool { a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4 } - let data_type = match ( + let sample_format = match ( (*waveformatex_ptr).wBitsPerSample, (*waveformatex_ptr).wFormatTag, ) { @@ -315,10 +316,10 @@ unsafe fn format_from_waveformatex_ptr( // Unknown data format returned by GetMixFormat. _ => return None, }; - let format = Format { + let format = SupportedStreamConfig { channels: (*waveformatex_ptr).nChannels as _, sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec), - data_type, + sample_format, }; Some(format) } @@ -433,7 +434,7 @@ impl Device { // number of channels seems to be supported. Any more or less returns an invalid // parameter error. Thus we just assume that the default number of channels is the only // number supported. - fn supported_formats(&self) -> Result { + fn supported_formats(&self) -> Result { // initializing COM because we call `CoTaskMemFree` to release the format. com::com_initialized(); @@ -441,7 +442,7 @@ impl Device { let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(SupportedFormatsError::DeviceNotAvailable) + return Err(SupportedStreamConfigsError::DeviceNotAvailable) } Err(e) => { let description = format!("{}", e); @@ -457,7 +458,7 @@ impl Device { match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) { Ok(()) => (), Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(SupportedFormatsError::DeviceNotAvailable); + return Err(SupportedStreamConfigsError::DeviceNotAvailable); } Err(e) => { let description = format!("{}", e); @@ -514,7 +515,8 @@ impl Device { Some(fmt) => fmt, None => { let description = - "could not create a `cpal::Format` from a `WAVEFORMATEX`".to_string(); + "could not create a `cpal::SupportedStreamConfig` from a `WAVEFORMATEX`" + .to_string(); let err = BackendSpecificError { description }; return Err(err.into()); } @@ -522,13 +524,15 @@ impl Device { let mut supported_formats = Vec::with_capacity(supported_sample_rates.len()); for rate in supported_sample_rates { format.sample_rate = SampleRate(rate as _); - supported_formats.push(SupportedFormat::from(format.clone())); + supported_formats.push(SupportedStreamConfigRange::from(format.clone())); } Ok(supported_formats.into_iter()) } } - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_configs( + &self, + ) -> Result { if self.data_flow() == eCapture { self.supported_formats() // If it's an output device, assume no input formats. @@ -537,9 +541,9 @@ impl Device { } } - pub fn supported_output_formats( + pub fn supported_output_configs( &self, - ) -> Result { + ) -> Result { if self.data_flow() == eRender { self.supported_formats() // If it's an input device, assume no output formats. @@ -552,14 +556,14 @@ impl Device { // processor to mix them together. // // One format is guaranteed to be supported, the one returned by `GetMixFormat`. - fn default_format(&self) -> Result { + fn default_format(&self) -> Result { // initializing COM because we call `CoTaskMemFree` com::com_initialized(); let lock = match self.ensure_future_audio_client() { Ok(lock) => lock, Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(DefaultFormatError::DeviceNotAvailable) + return Err(DefaultStreamConfigError::DeviceNotAvailable) } Err(e) => { let description = format!("{}", e); @@ -573,7 +577,7 @@ impl Device { let mut format_ptr = WaveFormatExPtr(ptr::null_mut()); match check_result((*client).GetMixFormat(&mut format_ptr.0)) { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(DefaultFormatError::DeviceNotAvailable); + return Err(DefaultStreamConfigError::DeviceNotAvailable); } Err(e) => { let description = format!("{}", e); @@ -584,7 +588,7 @@ impl Device { }; format_from_waveformatex_ptr(format_ptr.0) - .ok_or(DefaultFormatError::StreamTypeNotSupported) + .ok_or(DefaultStreamConfigError::StreamTypeNotSupported) } } @@ -593,26 +597,26 @@ impl Device { endpoint.data_flow() } - pub fn default_input_format(&self) -> Result { + pub fn default_input_config(&self) -> Result { if self.data_flow() == eCapture { self.default_format() } else { - Err(DefaultFormatError::StreamTypeNotSupported) + Err(DefaultStreamConfigError::StreamTypeNotSupported) } } - pub fn default_output_format(&self) -> Result { + pub fn default_output_config(&self) -> Result { let data_flow = self.data_flow(); if data_flow == eRender { self.default_format() } else { - Err(DefaultFormatError::StreamTypeNotSupported) + Err(DefaultStreamConfigError::StreamTypeNotSupported) } } pub(crate) fn build_input_stream_raw_inner( &self, - format: &Format, + format: &SupportedStreamConfig, ) -> Result { unsafe { // Making sure that COM is initialized. @@ -749,14 +753,14 @@ impl Device { playing: false, max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, - sample_format: format.data_type, + sample_format: format.sample_format, }) } } pub(crate) fn build_output_stream_raw_inner( &self, - format: &Format, + format: &SupportedStreamConfig, ) -> Result { unsafe { // Making sure that COM is initialized. @@ -893,7 +897,7 @@ impl Device { playing: false, max_frames_in_buffer, bytes_per_frame: waveformatex.nBlockAlign, - sample_format: format.data_type, + sample_format: format.sample_format, }) } } @@ -1141,19 +1145,21 @@ 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(format: &Format) -> Option { - let format_tag = match format.data_type { +fn format_to_waveformatextensible( + config: &SupportedStreamConfig, +) -> Option { + let format_tag = match config.sample_format { SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM, SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE, SampleFormat::U16 => return None, }; - let channels = format.channels as WORD; - let sample_rate = format.sample_rate.0 as DWORD; - let sample_bytes = format.data_type.sample_size() as WORD; + 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 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 format.data_type { + let cb_size = match config.sample_format { SampleFormat::I16 => 0, SampleFormat::F32 => { let extensible_size = mem::size_of::(); @@ -1177,7 +1183,7 @@ fn format_to_waveformatextensible(format: &Format) -> Option ksmedia::KSDATAFORMAT_SUBTYPE_PCM, SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, SampleFormat::U16 => return None, diff --git a/src/host/wasapi/mod.rs b/src/host/wasapi/mod.rs index 7fb6038..abf00a2 100644 --- a/src/host/wasapi/mod.rs +++ b/src/host/wasapi/mod.rs @@ -1,8 +1,8 @@ extern crate winapi; pub use self::device::{ - default_input_device, default_output_device, Device, Devices, SupportedInputFormats, - SupportedOutputFormats, + default_input_device, default_output_device, Device, Devices, SupportedInputConfigs, + SupportedOutputConfigs, }; pub use self::stream::Stream; use self::winapi::um::winnt::HRESULT; From 3a692f8bca3860a0bbaa2b63dc7be5c19335552c Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 28 Jan 2020 15:03:27 +0100 Subject: [PATCH 04/11] Update coreaudio backend for stream format renaming --- src/host/coreaudio/enumerate.rs | 6 +- src/host/coreaudio/mod.rs | 122 +++++++++++++++++--------------- 2 files changed, 67 insertions(+), 61 deletions(-) diff --git a/src/host/coreaudio/enumerate.rs b/src/host/coreaudio/enumerate.rs index 6762665..bdfd5d0 100644 --- a/src/host/coreaudio/enumerate.rs +++ b/src/host/coreaudio/enumerate.rs @@ -9,7 +9,7 @@ use super::Device; use std::mem; use std::ptr::null; use std::vec::IntoIter as VecIntoIter; -use {BackendSpecificError, DevicesError, SupportedFormat}; +use {BackendSpecificError, DevicesError, SupportedStreamConfigRange}; unsafe fn audio_devices() -> Result, OSStatus> { let property_address = AudioObjectPropertyAddress { @@ -143,5 +143,5 @@ pub fn default_output_device() -> Option { Some(device) } -pub type SupportedInputFormats = VecIntoIter; -pub type SupportedOutputFormats = VecIntoIter; +pub type SupportedInputConfigs = VecIntoIter; +pub type SupportedOutputConfigs = VecIntoIter; diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index cefcf88..2d4dee2 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -20,9 +20,9 @@ use self::coreaudio::sys::{ }; use crate::traits::{DeviceTrait, HostTrait, StreamTrait}; use crate::{ - BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError, - DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat, - SampleRate, StreamError, SupportedFormat, SupportedFormatsError, + BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, + DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, + StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, }; use std::cell::RefCell; use std::ffi::CStr; @@ -37,8 +37,8 @@ use std::time::Duration; mod enumerate; pub use self::enumerate::{ - default_input_device, default_output_device, Devices, SupportedInputFormats, - SupportedOutputFormats, + default_input_device, default_output_device, Devices, SupportedInputConfigs, + SupportedOutputConfigs, }; /// Coreaudio host, the default host on macOS and iOS. @@ -74,37 +74,37 @@ impl HostTrait for Host { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { Device::name(self) } - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } - 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: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -112,12 +112,12 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_input_stream_raw(self, format, data_callback, error_callback) + Device::build_input_stream_raw(self, config, data_callback, error_callback) } fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -125,7 +125,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_output_stream_raw(self, format, data_callback, error_callback) + Device::build_output_stream_raw(self, config, data_callback, error_callback) } } @@ -165,11 +165,11 @@ impl Device { Ok(c_str.to_string_lossy().into_owned()) } - // Logic re-used between `supported_input_formats` and `supported_output_formats`. - fn supported_formats( + // Logic re-used between `supported_input_configs` and `supported_output_configs`. + fn supported_configs( &self, scope: AudioObjectPropertyScope, - ) -> Result { + ) -> Result { let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamConfiguration, mScope: scope, @@ -255,11 +255,11 @@ impl Device { // Collect the supported formats for the device. let mut fmts = vec![]; for range in ranges { - let fmt = SupportedFormat { + let fmt = SupportedStreamConfigRange { channels: n_channels as ChannelCount, min_sample_rate: SampleRate(range.mMinimum as _), max_sample_rate: SampleRate(range.mMaximum as _), - data_type: sample_format, + sample_format: sample_format, }; fmts.push(fmt); } @@ -268,19 +268,25 @@ impl Device { } } - fn supported_input_formats(&self) -> Result { - self.supported_formats(kAudioObjectPropertyScopeInput) + fn supported_input_configs( + &self, + ) -> Result { + self.supported_configs(kAudioObjectPropertyScopeInput) } - fn supported_output_formats(&self) -> Result { - self.supported_formats(kAudioObjectPropertyScopeOutput) + fn supported_output_configs( + &self, + ) -> Result { + self.supported_configs(kAudioObjectPropertyScopeOutput) } - fn default_format( + fn default_config( &self, scope: AudioObjectPropertyScope, - ) -> Result { - fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> { + ) -> Result { + fn default_config_error_from_os_status( + status: OSStatus, + ) -> Result<(), DefaultStreamConfigError> { let err = match coreaudio::Error::from_os_status(status) { Err(err) => err, Ok(_) => return Ok(()), @@ -291,10 +297,10 @@ impl Device { ) | coreaudio::Error::AudioCodec(_) | coreaudio::Error::AudioFormat(_) => { - Err(DefaultFormatError::StreamTypeNotSupported) + Err(DefaultStreamConfigError::StreamTypeNotSupported) } coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => { - Err(DefaultFormatError::DeviceNotAvailable) + Err(DefaultStreamConfigError::DeviceNotAvailable) } err => { let description = format!("{}", std::error::Error::description(&err)); @@ -321,7 +327,7 @@ impl Device { &data_size as *const _ as *mut _, &asbd as *const _ as *mut _, ); - default_format_error_from_os_status(status)?; + default_config_error_from_os_status(status)?; let sample_format = { let audio_format = coreaudio::audio_unit::AudioFormat::from_format_and_flag( @@ -330,7 +336,7 @@ impl Device { ); let flags = match audio_format { Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags, - _ => return Err(DefaultFormatError::StreamTypeNotSupported), + _ => return Err(DefaultStreamConfigError::StreamTypeNotSupported), }; let maybe_sample_format = coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame( @@ -340,25 +346,25 @@ impl Device { match maybe_sample_format { Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32, Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16, - _ => return Err(DefaultFormatError::StreamTypeNotSupported), + _ => return Err(DefaultStreamConfigError::StreamTypeNotSupported), } }; - let format = Format { + let config = SupportedStreamConfig { sample_rate: SampleRate(asbd.mSampleRate as _), channels: asbd.mChannelsPerFrame as _, - data_type: sample_format, + sample_format: sample_format, }; - Ok(format) + Ok(config) } } - fn default_input_format(&self) -> Result { - self.default_format(kAudioObjectPropertyScopeInput) + fn default_input_config(&self) -> Result { + self.default_config(kAudioObjectPropertyScopeInput) } - fn default_output_format(&self) -> Result { - self.default_format(kAudioObjectPropertyScopeOutput) + fn default_output_config(&self) -> Result { + self.default_config(kAudioObjectPropertyScopeOutput) } } @@ -396,15 +402,15 @@ impl From for BuildStreamError { } // Create a coreaudio AudioStreamBasicDescription from a CPAL Format. -fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription { - let n_channels = format.channels as usize; - let sample_rate = format.sample_rate.0; - let bytes_per_channel = format.data_type.sample_size(); +fn asbd_from_config(config: &SupportedStreamConfig) -> 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 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 = format.data_type; + let sample_format = config.sample_format; let format_flags = match sample_format { SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32, _ => kAudioFormatFlagIsPacked as u32, @@ -469,7 +475,7 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result( &self, - format: &Format, + config: &SupportedStreamConfig, mut data_callback: D, _error_callback: E, ) -> Result @@ -502,7 +508,7 @@ impl Device { coreaudio::Error::from_os_status(status)?; // If the requested sample rate is different to the device sample rate, update the device. - if sample_rate as u32 != format.sample_rate.0 { + if sample_rate as u32 != config.sample_rate.0 { // Get available sample rate ranges. property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; let data_size = 0u32; @@ -530,7 +536,7 @@ impl Device { let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); // Now that we have the available ranges, pick the one matching the desired rate. - let sample_rate = format.sample_rate.0; + let sample_rate = config.sample_rate.0; let maybe_index = ranges.iter().position(|r| { r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate }); @@ -619,12 +625,12 @@ impl Device { let mut audio_unit = audio_unit_from_device(self, true)?; // Set the stream in interleaved mode. - let asbd = asbd_from_format(format); + let asbd = asbd_from_config(config); 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 = format.data_type; + 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 { @@ -657,7 +663,7 @@ impl Device { fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, mut data_callback: D, _error_callback: E, ) -> Result @@ -672,12 +678,12 @@ impl Device { let element = Element::Output; // Set the stream in interleaved mode. - let asbd = asbd_from_format(format); + let asbd = asbd_from_config(config); 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 = format.data_type; + 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 f29239119070be27c4bdef574aecf4fdd2f89cfb Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 28 Jan 2020 16:17:14 +0100 Subject: [PATCH 05/11] Update asio backend for stream format renaming --- src/host/asio/device.rs | 140 ++++++++++++++++++++-------------------- src/host/asio/mod.rs | 39 +++++------ src/host/asio/stream.rs | 66 ++++++++++--------- 3 files changed, 127 insertions(+), 118 deletions(-) diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index b0b6532..dee4df3 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -1,20 +1,20 @@ use std; -pub type SupportedInputFormats = std::vec::IntoIter; -pub type SupportedOutputFormats = std::vec::IntoIter; +pub type SupportedInputConfigs = std::vec::IntoIter; +pub type SupportedOutputConfigs = std::vec::IntoIter; use super::parking_lot::Mutex; use super::sys; use std::hash::{Hash, Hasher}; use std::sync::Arc; use BackendSpecificError; -use DefaultFormatError; +use DefaultStreamConfigError; use DeviceNameError; use DevicesError; -use Format; use SampleFormat; use SampleRate; -use SupportedFormat; -use SupportedFormatsError; +use SupportedStreamConfig; +use SupportedStreamConfigRange; +use SupportedStreamConfigsError; /// A ASIO Device pub struct Device { @@ -52,52 +52,21 @@ impl Device { Ok(self.driver.name().to_string()) } - /// Gets the supported input formats. + /// Gets the supported input configs. /// TODO currently only supports the default. - /// Need to find all possible formats. - pub fn supported_input_formats(&self) -> Result { - // Retrieve the default format for the total supported channels and supported sample - // format. - let mut f = match self.default_input_format() { - Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable), - Ok(f) => f, - }; - - // Collect a format for every combination of supported sample rate and number of channels. - let mut supported_formats = vec![]; - for &rate in ::COMMON_SAMPLE_RATES { - if !self - .driver - .can_sample_rate(rate.0.into()) - .ok() - .unwrap_or(false) - { - continue; - } - for channels in 1..f.channels + 1 { - f.channels = channels; - f.sample_rate = rate; - supported_formats.push(SupportedFormat::from(f.clone())); - } - } - Ok(supported_formats.into_iter()) - } - - /// Gets the supported output formats. - /// TODO currently only supports the default. - /// Need to find all possible formats. - pub fn supported_output_formats( + /// Need to find all possible configs. + pub fn supported_input_configs( &self, - ) -> Result { - // Retrieve the default format for the total supported channels and supported sample + ) -> Result { + // Retrieve the default config for the total supported channels and supported sample // format. - let mut f = match self.default_output_format() { - Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable), + let mut f = match self.default_input_config() { + Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable), Ok(f) => f, }; - // Collect a format for every combination of supported sample rate and number of channels. - let mut supported_formats = vec![]; + // Collect a config for every combination of supported sample rate and number of channels. + let mut supported_configs = vec![]; for &rate in ::COMMON_SAMPLE_RATES { if !self .driver @@ -110,38 +79,71 @@ impl Device { for channels in 1..f.channels + 1 { f.channels = channels; f.sample_rate = rate; - supported_formats.push(SupportedFormat::from(f.clone())); + supported_configs.push(SupportedStreamConfigRange::from(f.clone())); } } - Ok(supported_formats.into_iter()) + Ok(supported_configs.into_iter()) } - /// Returns the default input format - pub fn default_input_format(&self) -> Result { - let channels = self.driver.channels().map_err(default_format_err)?.ins as u16; - let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); + /// Gets the supported output configs. + /// TODO currently only supports the default. + /// Need to find all possible configs. + pub fn supported_output_configs( + &self, + ) -> Result { + // Retrieve the default config for the total supported channels and supported sample + // format. + let mut f = match self.default_output_config() { + Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable), + Ok(f) => f, + }; + + // Collect a config for every combination of supported sample rate and number of channels. + let mut supported_configs = vec![]; + for &rate in ::COMMON_SAMPLE_RATES { + if !self + .driver + .can_sample_rate(rate.0.into()) + .ok() + .unwrap_or(false) + { + continue; + } + for channels in 1..f.channels + 1 { + f.channels = channels; + f.sample_rate = rate; + supported_configs.push(SupportedStreamConfigRange::from(f.clone())); + } + } + Ok(supported_configs.into_iter()) + } + + /// Returns the default input config + pub fn default_input_config(&self) -> Result { + let channels = self.driver.channels().map_err(default_config_err)?.ins as u16; + let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _); // Map th ASIO sample type to a CPAL sample type - let data_type = self.driver.input_data_type().map_err(default_format_err)?; - let data_type = - convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?; - Ok(Format { + let data_type = self.driver.input_data_type().map_err(default_config_err)?; + let sample_format = convert_data_type(&data_type) + .ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?; + Ok(SupportedStreamConfig { channels, sample_rate, - data_type, + sample_format, }) } - /// Returns the default output format - pub fn default_output_format(&self) -> Result { - let channels = self.driver.channels().map_err(default_format_err)?.outs as u16; - let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _); - let data_type = self.driver.output_data_type().map_err(default_format_err)?; - let data_type = - convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?; - Ok(Format { + /// Returns the default output config + pub fn default_output_config(&self) -> Result { + let channels = self.driver.channels().map_err(default_config_err)?.outs as u16; + let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _); + let data_type = self.driver.output_data_type().map_err(default_config_err)?; + let sample_format = convert_data_type(&data_type) + .ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?; + Ok(SupportedStreamConfig { channels, sample_rate, - data_type, + sample_format, }) } } @@ -202,12 +204,12 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option DefaultFormatError { +fn default_config_err(e: sys::AsioError) -> DefaultStreamConfigError { match e { sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => { - DefaultFormatError::DeviceNotAvailable + DefaultStreamConfigError::DeviceNotAvailable } - sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported, + sys::AsioError::NoRate => DefaultStreamConfigError::StreamTypeNotSupported, err => { let description = format!("{}", err); BackendSpecificError { description }.into() diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index 24713a0..917b958 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -2,12 +2,13 @@ extern crate asio_sys as sys; extern crate parking_lot; use crate::{ - BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - PauseStreamError, PlayStreamError, StreamError, SupportedFormatsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + PauseStreamError, PlayStreamError, StreamError, SupportedStreamConfig, + SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; -pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats}; +pub use self::device::{Device, Devices, SupportedInputConfigs, SupportedOutputConfigs}; pub use self::stream::Stream; use std::sync::Arc; @@ -53,37 +54,37 @@ impl HostTrait for Host { } impl DeviceTrait for Device { - type SupportedInputFormats = SupportedInputFormats; - type SupportedOutputFormats = SupportedOutputFormats; + type SupportedInputConfigs = SupportedInputConfigs; + type SupportedOutputConfigs = SupportedOutputConfigs; type Stream = Stream; fn name(&self) -> Result { Device::name(self) } - fn supported_input_formats( + fn supported_input_configs( &self, - ) -> Result { - Device::supported_input_formats(self) + ) -> Result { + Device::supported_input_configs(self) } - fn supported_output_formats( + fn supported_output_configs( &self, - ) -> Result { - Device::supported_output_formats(self) + ) -> Result { + Device::supported_output_configs(self) } - fn default_input_format(&self) -> Result { - Device::default_input_format(self) + fn default_input_config(&self) -> Result { + Device::default_input_config(self) } - 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: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -91,12 +92,12 @@ impl DeviceTrait for Device { D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_input_stream_raw(self, format, data_callback, error_callback) + Device::build_input_stream_raw(self, config, data_callback, error_callback) } fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, data_callback: D, error_callback: E, ) -> Result @@ -104,7 +105,7 @@ impl DeviceTrait for Device { D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - Device::build_output_stream_raw(self, format, data_callback, error_callback) + Device::build_output_stream_raw(self, config, data_callback, error_callback) } } diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index e938b5f..7d339c1 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -10,12 +10,12 @@ use std::sync::Arc; use BackendSpecificError; use BuildStreamError; use Data; -use Format; use PauseStreamError; use PlayStreamError; use Sample; use SampleFormat; use StreamError; +use SupportedStreamConfig; /// Sample types whose constant silent value is known. trait Silence { @@ -59,7 +59,7 @@ impl Stream { impl Device { pub fn build_input_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, mut data_callback: D, _error_callback: E, ) -> Result @@ -70,18 +70,18 @@ impl Device { let stream_type = self.driver.input_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. - let data_type = super::device::convert_data_type(&stream_type) + let sample_format = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::FormatNotSupported)?; - if format.data_type != data_type { + if config.sample_format != sample_format { return Err(BuildStreamError::FormatNotSupported); } - let num_channels = format.channels.clone(); - let buffer_size = self.get_or_create_input_stream(format)?; + let num_channels = config.channels.clone(); + let buffer_size = self.get_or_create_input_stream(config)?; let cpal_num_samples = buffer_size * num_channels as usize; // Create the buffer depending on the size of the data type. - let len_bytes = cpal_num_samples * data_type.sample_size(); + let len_bytes = cpal_num_samples * sample_format.sample_size(); let mut interleaved = vec![0u8; len_bytes]; let stream_playing = Arc::new(AtomicBool::new(false)); @@ -134,7 +134,7 @@ impl Device { callback(&data); } - match (&stream_type, data_type) { + match (&stream_type, sample_format) { (&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => { process_input_callback::( &mut data_callback, @@ -225,7 +225,7 @@ impl Device { pub fn build_output_stream_raw( &self, - format: &Format, + config: &SupportedStreamConfig, mut data_callback: D, _error_callback: E, ) -> Result @@ -236,18 +236,18 @@ impl Device { let stream_type = self.driver.output_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. - let data_type = super::device::convert_data_type(&stream_type) + let sample_format = super::device::convert_data_type(&stream_type) .ok_or(BuildStreamError::FormatNotSupported)?; - if format.data_type != data_type { + if config.sample_format != sample_format { return Err(BuildStreamError::FormatNotSupported); } - let num_channels = format.channels.clone(); - let buffer_size = self.get_or_create_output_stream(format)?; + let num_channels = config.channels.clone(); + let buffer_size = self.get_or_create_output_stream(config)?; let cpal_num_samples = buffer_size * num_channels as usize; // Create buffers depending on data type. - let len_bytes = cpal_num_samples * data_type.sample_size(); + let len_bytes = cpal_num_samples * sample_format.sample_size(); let mut interleaved = vec![0u8; len_bytes]; let mut silence_asio_buffer = SilenceAsioBuffer::default(); @@ -336,7 +336,7 @@ impl Device { } } - match (data_type, &stream_type) { + match (sample_format, &stream_type) { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => { process_output_callback::( &mut data_callback, @@ -436,15 +436,18 @@ impl Device { /// If there is no existing ASIO Input Stream it will be created. /// /// On success, the buffer size of the stream is returned. - fn get_or_create_input_stream(&self, format: &Format) -> Result { - match self.default_input_format() { + fn get_or_create_input_stream( + &self, + config: &SupportedStreamConfig, + ) -> Result { + match self.default_input_config() { Ok(f) => { let num_asio_channels = f.channels; - check_format(&self.driver, format, num_asio_channels) + check_config(&self.driver, config, num_asio_channels) } Err(_) => Err(BuildStreamError::FormatNotSupported), }?; - let num_channels = format.channels as usize; + let num_channels = config.channels as usize; let ref mut streams = *self.asio_streams.lock(); // Either create a stream if thers none or had back the // size of the current one. @@ -473,15 +476,18 @@ impl Device { /// Create a new CPAL Output Stream. /// /// If there is no existing ASIO Output Stream it will be created. - fn get_or_create_output_stream(&self, format: &Format) -> Result { - match self.default_output_format() { + fn get_or_create_output_stream( + &self, + config: &SupportedStreamConfig, + ) -> Result { + match self.default_output_config() { Ok(f) => { let num_asio_channels = f.channels; - check_format(&self.driver, format, num_asio_channels) + check_config(&self.driver, config, num_asio_channels) } Err(_) => Err(BuildStreamError::FormatNotSupported), }?; - let num_channels = format.channels as usize; + let num_channels = config.channels as usize; let ref mut streams = *self.asio_streams.lock(); // Either create a stream if thers none or had back the // size of the current one. @@ -570,19 +576,19 @@ impl AsioSample for f64 { } } -/// Check whether or not the desired format is supported by the stream. +/// Check whether or not the desired config is supported by the stream. /// /// Checks sample rate, data type and then finally the number of channels. -fn check_format( +fn check_config( driver: &sys::Driver, - format: &Format, + config: &SupportedStreamConfig, num_asio_channels: u16, ) -> Result<(), BuildStreamError> { - let Format { + let SupportedStreamConfig { channels, sample_rate, - data_type, - } = format; + sample_format, + } = config; // Try and set the sample rate to what the user selected. let sample_rate = sample_rate.0.into(); if sample_rate != driver.sample_rate().map_err(build_stream_err)? { @@ -598,7 +604,7 @@ fn check_format( } } // unsigned formats are not supported by asio - match data_type { + match sample_format { SampleFormat::I16 | SampleFormat::F32 => (), SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported), } From 07b66f52f3ca7a1fb370537350d92311c26f2f5d Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 28 Jan 2020 16:38:02 +0100 Subject: [PATCH 06/11] Rename FormatNotSupported to StreamConfigNotSupported --- src/error.rs | 6 +++--- src/host/asio/stream.rs | 18 +++++++++--------- src/host/coreaudio/mod.rs | 4 ++-- src/host/wasapi/device.rs | 10 +++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/error.rs b/src/error.rs index 90b6dbb..f404f29 100644 --- a/src/error.rs +++ b/src/error.rs @@ -90,9 +90,9 @@ pub enum BuildStreamError { /// program is running. #[error("The requested device is no longer available. For example, it has been unplugged.")] DeviceNotAvailable, - /// The required format is not supported. - #[error("The requested stream format is not supported by the device.")] - FormatNotSupported, + /// The specified stream configuration is not supported. + #[error("The requested stream configuration is not supported by the device.")] + StreamConfigNotSupported, /// We called something the C-Layer did not understand /// /// On ALSA device functions called with a feature they do not support will yield this. E.g. diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 7d339c1..42ff8ac 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -71,9 +71,9 @@ impl Device { // Ensure that the desired sample type is supported. let sample_format = super::device::convert_data_type(&stream_type) - .ok_or(BuildStreamError::FormatNotSupported)?; + .ok_or(BuildStreamError::StreamConfigNotSupported)?; if config.sample_format != sample_format { - return Err(BuildStreamError::FormatNotSupported); + return Err(BuildStreamError::StreamConfigNotSupported); } let num_channels = config.channels.clone(); @@ -237,9 +237,9 @@ impl Device { // Ensure that the desired sample type is supported. let sample_format = super::device::convert_data_type(&stream_type) - .ok_or(BuildStreamError::FormatNotSupported)?; + .ok_or(BuildStreamError::StreamConfigNotSupported)?; if config.sample_format != sample_format { - return Err(BuildStreamError::FormatNotSupported); + return Err(BuildStreamError::StreamConfigNotSupported); } let num_channels = config.channels.clone(); @@ -445,7 +445,7 @@ impl Device { let num_asio_channels = f.channels; check_config(&self.driver, config, num_asio_channels) } - Err(_) => Err(BuildStreamError::FormatNotSupported), + Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; let num_channels = config.channels as usize; let ref mut streams = *self.asio_streams.lock(); @@ -485,7 +485,7 @@ impl Device { let num_asio_channels = f.channels; check_config(&self.driver, config, num_asio_channels) } - Err(_) => Err(BuildStreamError::FormatNotSupported), + Err(_) => Err(BuildStreamError::StreamConfigNotSupported), }?; let num_channels = config.channels as usize; let ref mut streams = *self.asio_streams.lock(); @@ -600,16 +600,16 @@ fn check_config( .set_sample_rate(sample_rate) .map_err(build_stream_err)?; } else { - return Err(BuildStreamError::FormatNotSupported); + return Err(BuildStreamError::StreamConfigNotSupported); } } // unsigned formats are not supported by asio match sample_format { SampleFormat::I16 | SampleFormat::F32 => (), - SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported), + SampleFormat::U16 => return Err(BuildStreamError::StreamConfigNotSupported), } if *channels > num_asio_channels { - return Err(BuildStreamError::FormatNotSupported); + return Err(BuildStreamError::StreamConfigNotSupported); } Ok(()) } diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index 2d4dee2..61800e4 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -395,7 +395,7 @@ impl From for BuildStreamError { | coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) - | coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported, + | coreaudio::Error::AudioFormat(_) => BuildStreamError::StreamConfigNotSupported, _ => BuildStreamError::DeviceNotAvailable, } } @@ -541,7 +541,7 @@ impl Device { r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate }); let range_index = match maybe_index { - None => return Err(BuildStreamError::FormatNotSupported), + None => return Err(BuildStreamError::StreamConfigNotSupported), Some(i) => i, }; diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 65d6263..8d7ed34 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -242,7 +242,7 @@ pub unsafe fn is_format_supported( }, (winerror::S_FALSE, _) => { (*audio_client).Release(); - return Err(BuildStreamError::FormatNotSupported); + return Err(BuildStreamError::StreamConfigNotSupported); }, (_, Ok(())) => (), }; @@ -639,12 +639,12 @@ impl Device { // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) - .ok_or(BuildStreamError::FormatNotSupported)?; + .ok_or(BuildStreamError::StreamConfigNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { - Ok(false) => return Err(BuildStreamError::FormatNotSupported), + Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported), Err(_) => return Err(BuildStreamError::DeviceNotAvailable), _ => (), } @@ -783,12 +783,12 @@ impl Device { // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) - .ok_or(BuildStreamError::FormatNotSupported)?; + .ok_or(BuildStreamError::StreamConfigNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { - Ok(false) => return Err(BuildStreamError::FormatNotSupported), + Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported), Err(_) => return Err(BuildStreamError::DeviceNotAvailable), _ => (), } From 476f6c4c2ca9c7664ede69083aea395f5f0a81d3 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 2 Feb 2020 18:28:38 +0100 Subject: [PATCH 07/11] 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 08/11] 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 09/11] 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 10/11] 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 11/11] 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 {