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};
|
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(
|
||||||
|
hw_params.0,
|
||||||
&mut min_rate,
|
&mut min_rate,
|
||||||
ptr::null_mut()))
|
ptr::null_mut(),
|
||||||
.expect("unable to get minimum supported rete");
|
)) {
|
||||||
|
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(
|
||||||
|
hw_params.0,
|
||||||
&mut max_rate,
|
&mut max_rate,
|
||||||
ptr::null_mut()))
|
ptr::null_mut(),
|
||||||
.expect("unable to get maximum supported rate");
|
)) {
|
||||||
|
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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
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.
|
/// 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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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 _);
|
||||||
|
|
Loading…
Reference in New Issue