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:
parent
42fc702f53
commit
78a7cb9e79
|
@ -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,
|
||||
if let Err(desc) = 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");
|
||||
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,
|
||||
if let Err(desc) = 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");
|
||||
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(),
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
19
src/lib.rs
19
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<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.
|
||||
//
|
||||
|
|
|
@ -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 _);
|
||||
|
|
Loading…
Reference in New Issue