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}; pub use self::enumerate::{Devices, default_input_device, default_output_device};
use ChannelCount; use ChannelCount;
use BackendSpecificError;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use Format; use Format;
@ -83,7 +84,14 @@ impl Device {
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> ) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError>
{ {
let mut handle = mem::uninitialized(); 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( match alsa::snd_pcm_open(
&mut handle, &mut handle,
@ -94,14 +102,18 @@ impl Device {
-2 | -2 |
-16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable), -16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable),
-22 => return Err(SupportedFormatsError::InvalidArgument), -22 => return Err(SupportedFormatsError::InvalidArgument),
e => if check_errors(e).is_err() { e => if let Err(description) = check_errors(e) {
return Err(SupportedFormatsError::Unknown) let err = BackendSpecificError { description };
return Err(err.into())
} }
} }
let hw_params = HwParams::alloc(); let hw_params = HwParams::alloc();
match check_errors(alsa::snd_pcm_hw_params_any(handle, hw_params.0)) { 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(_) => (), Ok(_) => (),
}; };
@ -158,15 +170,26 @@ impl Device {
} }
let mut min_rate = mem::uninitialized(); let mut min_rate = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_get_rate_min(hw_params.0, if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_min(
&mut min_rate, hw_params.0,
ptr::null_mut())) &mut min_rate,
.expect("unable to get minimum supported rete"); 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(); let mut max_rate = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_get_rate_max(hw_params.0, if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_rate_max(
&mut max_rate, hw_params.0,
ptr::null_mut())) &mut max_rate,
.expect("unable to get maximum supported 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 { let sample_rates = if min_rate == max_rate {
vec![(min_rate, max_rate)] vec![(min_rate, max_rate)]
@ -212,11 +235,19 @@ impl Device {
}; };
let mut min_channels = mem::uninitialized(); let mut min_channels = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_min(hw_params.0, &mut min_channels)) {
.expect("unable to get minimum supported channel count"); 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(); let mut max_channels = mem::uninitialized();
check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) if let Err(desc) = check_errors(alsa::snd_pcm_hw_params_get_channels_max(hw_params.0, &mut max_channels)) {
.expect("unable to get maximum supported channel count"); 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 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) let supported_channels = (min_channels .. max_channels + 1)
.filter_map(|num| if alsa::snd_pcm_hw_params_test_channels( .filter_map(|num| if alsa::snd_pcm_hw_params_test_channels(
@ -280,8 +311,9 @@ impl Device {
// the device supports only one // the device supports only one
return Err(DefaultFormatError::StreamTypeNotSupported); return Err(DefaultFormatError::StreamTypeNotSupported);
} }
Err(SupportedFormatsError::Unknown) => { Err(SupportedFormatsError::BackendSpecific { err }) => {
return Err(DefaultFormatError::DeviceNotAvailable); unimplemented!();
//return Err(err.into());
} }
Ok(fmts) => fmts.collect(), Ok(fmts) => fmts.collect(),
} }

View File

@ -2,6 +2,7 @@ extern crate coreaudio;
extern crate core_foundation_sys; extern crate core_foundation_sys;
use ChannelCount; use ChannelCount;
use BackendSpecificError;
use BuildStreamError; use BuildStreamError;
use DefaultFormatError; use DefaultFormatError;
use Format; use Format;
@ -126,9 +127,8 @@ impl Device {
null(), null(),
&data_size as *const _ as *mut _, &data_size as *const _ as *mut _,
); );
if status != kAudioHardwareNoError as i32 { check_os_status(status)?;
unimplemented!();
}
let mut audio_buffer_list: Vec<u8> = vec![]; let mut audio_buffer_list: Vec<u8> = vec![];
audio_buffer_list.reserve_exact(data_size as usize); audio_buffer_list.reserve_exact(data_size as usize);
let status = AudioObjectGetPropertyData( let status = AudioObjectGetPropertyData(
@ -139,9 +139,8 @@ impl Device {
&data_size as *const _ as *mut _, &data_size as *const _ as *mut _,
audio_buffer_list.as_mut_ptr() as *mut _, audio_buffer_list.as_mut_ptr() as *mut _,
); );
if status != kAudioHardwareNoError as i32 { check_os_status(status)?;
unimplemented!();
}
let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList; let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList;
// If there's no buffers, skip. // If there's no buffers, skip.
@ -176,9 +175,8 @@ impl Device {
null(), null(),
&data_size as *const _ as *mut _, &data_size as *const _ as *mut _,
); );
if status != kAudioHardwareNoError as i32 { check_os_status(status)?;
unimplemented!();
}
let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>(); let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>();
let mut ranges: Vec<u8> = vec![]; let mut ranges: Vec<u8> = vec![];
ranges.reserve_exact(data_size as usize); ranges.reserve_exact(data_size as usize);
@ -190,9 +188,8 @@ impl Device {
&data_size as *const _ as *mut _, &data_size as *const _ as *mut _,
ranges.as_mut_ptr() as *mut _, ranges.as_mut_ptr() as *mut _,
); );
if status != kAudioHardwareNoError as i32 { check_os_status(status)?;
unimplemented!();
}
let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _;
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); 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. /// An error that might occur while attempting to enumerate the available devices on a system.
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
pub enum DevicesError { pub enum DevicesError {
/// Some error that is specific to the backend from which it was produced. /// See the `BackendSpecificError` docs for more information about this error variant.
///
/// Note: This error is often used when
#[fail(display = "{}", err)] #[fail(display = "{}", err)]
BackendSpecific { BackendSpecific {
#[fail(cause)] #[fail(cause)]
@ -320,9 +318,12 @@ pub enum SupportedFormatsError {
/// We called something the C-Layer did not understand /// 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.")] #[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, InvalidArgument,
/// The C-Layer returned an error we don't know about /// See the `BackendSpecificError` docs for more information about this error variant.
#[fail(display = "An unknown error in the backend occured.")] #[fail(display = "{}", err)]
Unknown BackendSpecific {
#[fail(cause)]
err: BackendSpecificError,
}
} }
/// May occur when attempting to request the default input or output stream format from a `Device`. /// 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 // 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. // 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`. // Retrieve the `IAudioClient`.
let lock = match self.ensure_future_audio_client() { let lock = match self.ensure_future_audio_client() {
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => Ok(lock) => lock,
return Err(SupportedFormatsError::DeviceNotAvailable), Err(e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
e => e.unwrap(), return Err(SupportedFormatsError::DeviceNotAvailable)
}
Err(e) => {
let description = format!("{}", e);
let err = BackendSpecificError { description };
return Err(err.into());
},
}; };
let client = lock.unwrap().0; let client = lock.unwrap().0;
@ -405,11 +411,15 @@ impl Device {
// Retrieve the pointer to the default WAVEFORMATEX. // Retrieve the pointer to the default WAVEFORMATEX.
let mut default_waveformatex_ptr = WaveFormatExPtr(mem::uninitialized()); let mut default_waveformatex_ptr = WaveFormatExPtr(mem::uninitialized());
match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) { match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) {
Ok(()) => (),
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(SupportedFormatsError::DeviceNotAvailable); return Err(SupportedFormatsError::DeviceNotAvailable);
}, },
Err(e) => panic!("{:?}", e), Err(e) => {
Ok(()) => (), 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. // 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? // TODO: Test the different sample formats?
// Create the supported formats. // Create the supported formats.
let mut format = format_from_waveformatex_ptr(default_waveformatex_ptr.0) let mut format = match format_from_waveformatex_ptr(default_waveformatex_ptr.0) {
.expect("could not create a cpal::Format from a WAVEFORMATEX"); 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()); let mut supported_formats = Vec::with_capacity(supported_sample_rates.len());
for rate in supported_sample_rates { for rate in supported_sample_rates {
format.sample_rate = SampleRate(rate as _); format.sample_rate = SampleRate(rate as _);