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