Change `SupportedFormatsError::Unknown` variant to `BackendSpecific`

This allows for also passing through a description of the
unknown/platform-specific error.
This commit is contained in:
mitchmindtree 2019-06-21 00:22:30 +02:00
parent 42fc702f53
commit 78a7cb9e79
4 changed files with 106 additions and 43 deletions

View File

@ -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<VecIntoIter<SupportedFormat>, 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(),
}

View File

@ -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<u8> = 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::<AudioValueRange>();
let mut ranges: Vec<u8> = 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 })
}
}
}

View File

@ -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<BackendSpecificError> for DevicesError {
}
}
impl From<BackendSpecificError> 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.
//

View File

@ -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 _);