diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index 37bdc4d..0b9f675 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -4,6 +4,7 @@ extern crate libc; pub use self::enumerate::{Devices, default_input_device, default_output_device}; use ChannelCount; +use BackendSpecificError; use BuildStreamError; use DefaultFormatError; use Format; @@ -83,7 +84,14 @@ impl Device { ) -> Result, SupportedFormatsError> { let mut handle = mem::uninitialized(); - let device_name = ffi::CString::new(&self.0[..]).expect("Unable to get device name"); + let device_name = match ffi::CString::new(&self.0[..]) { + Ok(name) => name, + Err(err) => { + let description = format!("failed to retrieve device name: {}", err); + let err = BackendSpecificError { description }; + return Err(err.into()); + } + }; match alsa::snd_pcm_open( &mut handle, @@ -94,14 +102,18 @@ impl Device { -2 | -16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable), -22 => return Err(SupportedFormatsError::InvalidArgument), - e => if check_errors(e).is_err() { - return Err(SupportedFormatsError::Unknown) + e => if let Err(description) = check_errors(e) { + let err = BackendSpecificError { description }; + return Err(err.into()) } } let hw_params = HwParams::alloc(); match check_errors(alsa::snd_pcm_hw_params_any(handle, hw_params.0)) { - Err(_) => return Ok(Vec::new().into_iter()), + Err(description) => { + let err = BackendSpecificError { description }; + return Err(err.into()); + } Ok(_) => (), }; @@ -158,15 +170,26 @@ impl Device { } let mut min_rate = mem::uninitialized(); - check_errors(alsa::snd_pcm_hw_params_get_rate_min(hw_params.0, - &mut min_rate, - ptr::null_mut())) - .expect("unable to get minimum supported rete"); + if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_min( + hw_params.0, + &mut min_rate, + ptr::null_mut(), + )) { + let description = format!("unable to get minimum supported rate: {}", desc); + let err = BackendSpecificError { description }; + return Err(err.into()); + } + let mut max_rate = mem::uninitialized(); - check_errors(alsa::snd_pcm_hw_params_get_rate_max(hw_params.0, - &mut max_rate, - ptr::null_mut())) - .expect("unable to get maximum supported rate"); + if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_max( + hw_params.0, + &mut max_rate, + ptr::null_mut(), + )) { + let description = format!("unable to get maximum supported rate: {}", desc); + let err = BackendSpecificError { description }; + return Err(err.into()); + } let sample_rates = if min_rate == max_rate { vec![(min_rate, max_rate)] @@ -212,11 +235,19 @@ impl Device { }; let mut min_channels = mem::uninitialized(); - check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) - .expect("unable to get minimum supported channel count"); + if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) { + let description = format!("unable to get minimum supported channel count: {}", desc); + let err = BackendSpecificError { description }; + return Err(err.into()); + } + let mut max_channels = mem::uninitialized(); - check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) - .expect("unable to get maximum supported channel count"); + if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) { + let description = format!("unable to get maximum supported channel count: {}", desc); + let err = BackendSpecificError { description }; + return Err(err.into()); + } + let max_channels = cmp::min(max_channels, 32); // TODO: limiting to 32 channels or too much stuff is returned let supported_channels = (min_channels .. max_channels + 1) .filter_map(|num| if alsa::snd_pcm_hw_params_test_channels( @@ -280,8 +311,9 @@ impl Device { // the device supports only one return Err(DefaultFormatError::StreamTypeNotSupported); } - Err(SupportedFormatsError::Unknown) => { - return Err(DefaultFormatError::DeviceNotAvailable); + Err(SupportedFormatsError::BackendSpecific { err }) => { + unimplemented!(); + //return Err(err.into()); } Ok(fmts) => fmts.collect(), } diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index 3e8b498..eedd0c1 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -2,6 +2,7 @@ extern crate coreaudio; extern crate core_foundation_sys; use ChannelCount; +use BackendSpecificError; use BuildStreamError; use DefaultFormatError; use Format; @@ -126,9 +127,8 @@ impl Device { null(), &data_size as *const _ as *mut _, ); - if status != kAudioHardwareNoError as i32 { - unimplemented!(); - } + check_os_status(status)?; + let mut audio_buffer_list: Vec = vec![]; audio_buffer_list.reserve_exact(data_size as usize); let status = AudioObjectGetPropertyData( @@ -139,9 +139,8 @@ impl Device { &data_size as *const _ as *mut _, audio_buffer_list.as_mut_ptr() as *mut _, ); - if status != kAudioHardwareNoError as i32 { - unimplemented!(); - } + check_os_status(status)?; + let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList; // If there's no buffers, skip. @@ -176,9 +175,8 @@ impl Device { null(), &data_size as *const _ as *mut _, ); - if status != kAudioHardwareNoError as i32 { - unimplemented!(); - } + check_os_status(status)?; + let n_ranges = data_size as usize / mem::size_of::(); let mut ranges: Vec = vec![]; ranges.reserve_exact(data_size as usize); @@ -190,9 +188,8 @@ impl Device { &data_size as *const _ as *mut _, ranges.as_mut_ptr() as *mut _, ); - if status != kAudioHardwareNoError as i32 { - unimplemented!(); - } + check_os_status(status)?; + let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); @@ -796,3 +793,13 @@ impl EventLoop { } } } + +fn check_os_status(os_status: OSStatus) -> Result<(), BackendSpecificError> { + match coreaudio::Error::from_os_status(os_status) { + Ok(()) => Ok(()), + Err(err) => { + let description = std::error::Error::description(&err).to_string(); + Err(BackendSpecificError { description }) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 926dcaf..f25f0a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,9 +300,7 @@ pub struct BackendSpecificError { /// An error that might occur while attempting to enumerate the available devices on a system. #[derive(Debug, Fail)] pub enum DevicesError { - /// Some error that is specific to the backend from which it was produced. - /// - /// Note: This error is often used when + /// See the `BackendSpecificError` docs for more information about this error variant. #[fail(display = "{}", err)] BackendSpecific { #[fail(cause)] @@ -320,9 +318,12 @@ pub enum SupportedFormatsError { /// We called something the C-Layer did not understand #[fail(display = "Invalid argument passed to the backend. For example, this happens when trying to read capture capabilities when the device does not support it.")] InvalidArgument, - /// The C-Layer returned an error we don't know about - #[fail(display = "An unknown error in the backend occured.")] - Unknown + /// See the `BackendSpecificError` docs for more information about this error variant. + #[fail(display = "{}", err)] + BackendSpecific { + #[fail(cause)] + err: BackendSpecificError, + } } /// May occur when attempting to request the default input or output stream format from a `Device`. @@ -741,6 +742,12 @@ impl From for DevicesError { } } +impl From for SupportedFormatsError { + fn from(err: BackendSpecificError) -> Self { + SupportedFormatsError::BackendSpecific { err } + } +} + // If a backend does not provide an API for retrieving supported formats, we query it with a bunch // of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa. // diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index a927d93..786d80d 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -395,9 +395,15 @@ impl Device { // Retrieve the `IAudioClient`. let lock = match self.ensure_future_audio_client() { - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => - return Err(SupportedFormatsError::DeviceNotAvailable), - e => e.unwrap(), + Ok(lock) => lock, + Err(e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { + return Err(SupportedFormatsError::DeviceNotAvailable) + } + Err(e) => { + let description = format!("{}", e); + let err = BackendSpecificError { description }; + return Err(err.into()); + }, }; let client = lock.unwrap().0; @@ -405,11 +411,15 @@ impl Device { // Retrieve the pointer to the default WAVEFORMATEX. let mut default_waveformatex_ptr = WaveFormatExPtr(mem::uninitialized()); 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); }, - Err(e) => panic!("{:?}", e), - Ok(()) => (), + Err(e) => { + let description = format!("{}", e); + let err = BackendSpecificError { description }; + return Err(err.into()); + }, }; // If the default format can't succeed we have no hope of finding other formats. @@ -453,8 +463,15 @@ impl Device { // TODO: Test the different sample formats? // Create the supported formats. - let mut format = format_from_waveformatex_ptr(default_waveformatex_ptr.0) - .expect("could not create a cpal::Format from a WAVEFORMATEX"); + let mut format = match format_from_waveformatex_ptr(default_waveformatex_ptr.0) { + Some(fmt) => fmt, + None => { + let description = + "could not create a `cpal::Format` from a `WAVEFORMATEX`".to_string(); + let err = BackendSpecificError { description }; + return Err(err.into()); + } + }; let mut supported_formats = Vec::with_capacity(supported_sample_rates.len()); for rate in supported_sample_rates { format.sample_rate = SampleRate(rate as _);