Add proper error handling

This commit is contained in:
Pierre Krieger 2015-09-01 14:17:57 +02:00
parent 1985c346ac
commit 98b931edff
4 changed files with 117 additions and 27 deletions

View File

@ -2,7 +2,7 @@ extern crate cpal;
fn main() {
let endpoint = cpal::get_default_endpoint().unwrap();
let format = endpoint.get_supported_formats_list().next().unwrap();
let format = endpoint.get_supported_formats_list().unwrap().next().unwrap();
let mut channel = cpal::Voice::new(&endpoint, &format).unwrap();
// Produce a sinusoid of maximum amplitude.

View File

@ -33,6 +33,7 @@ extern crate lazy_static;
pub use samples_formats::{SampleFormat, Sample};
use std::fmt;
use std::error::Error;
use std::ops::{Deref, DerefMut};
@ -87,8 +88,10 @@ pub struct Endpoint(cpal_impl::Endpoint);
impl Endpoint {
/// Returns an iterator that produces the list of formats that are supported by the backend.
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
SupportedFormatsIterator(self.0.get_supported_formats_list())
pub fn get_supported_formats_list(&self) -> Result<SupportedFormatsIterator,
FormatsEnumerationError>
{
Ok(SupportedFormatsIterator(try!(self.0.get_supported_formats_list())))
}
}
@ -146,6 +149,61 @@ pub enum UnknownTypeBuffer<'a> {
F32(Buffer<'a, f32>),
}
/// Error that can happen when enumerating the list of supported formats.
#[derive(Debug)]
pub enum FormatsEnumerationError {
/// The device no longer exists. This can happen if the device is disconnected while the
/// program is running.
DeviceNotAvailable,
}
impl fmt::Display for FormatsEnumerationError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", self.description())
}
}
impl Error for FormatsEnumerationError {
fn description(&self) -> &str {
match self {
&FormatsEnumerationError::DeviceNotAvailable => {
"The requested device is no longer available (for example, it has been unplugged)."
},
}
}
}
/// Error that can happen when creating a `Voice`.
#[derive(Debug)]
pub enum CreationError {
/// The device no longer exists. This can happen if the device is disconnected while the
/// program is running.
DeviceNotAvailable,
/// The required format is not supported.
FormatNotSupported,
}
impl fmt::Display for CreationError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", self.description())
}
}
impl Error for CreationError {
fn description(&self) -> &str {
match self {
&CreationError::DeviceNotAvailable => {
"The requested device is no longer available (for example, it has been unplugged)."
},
&CreationError::FormatNotSupported => {
"The requested samples format is not supported by the device."
},
}
}
}
/// Controls a sound output. A typical application has one `Voice` for each sound
/// it wants to output.
///
@ -163,7 +221,7 @@ pub struct Voice(cpal_impl::Voice);
impl Voice {
/// Builds a new channel.
#[inline]
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, Box<Error>> {
pub fn new(endpoint: &Endpoint, format: &Format) -> Result<Voice, CreationError> {
let channel = try!(cpal_impl::Voice::new(&endpoint.0, format));
Ok(Voice(channel))
}

View File

@ -8,6 +8,7 @@ use std::ptr;
use std::mem;
use Format;
use FormatsEnumerationError;
use SamplesRate;
use SampleFormat;
@ -58,27 +59,39 @@ impl Endpoint {
}
/// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it.
fn ensure_future_audio_client(&self) -> MutexGuard<Option<IAudioClientWrapper>> {
fn ensure_future_audio_client(&self) -> Result<MutexGuard<Option<IAudioClientWrapper>>, IoError> {
let mut lock = self.future_audio_client.lock().unwrap();
if lock.is_some() {
return lock;
return Ok(lock);
}
let audio_client: *mut winapi::IAudioClient = unsafe {
let mut audio_client = mem::uninitialized();
let hresult = (*self.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
ptr::null_mut(), &mut audio_client);
// can fail if the device has been disconnected since we enumerated it, or if
// the device doesn't support playback for some reason
check_result(hresult).unwrap(); // FIXME: don't unwrap
try!(check_result(hresult));
assert!(!audio_client.is_null());
audio_client as *mut _
};
*lock = Some(IAudioClientWrapper(audio_client));
lock
Ok(lock)
}
pub fn get_supported_formats_list(&self) -> SupportedFormatsIterator {
/// Returns an uninitialized `IAudioClient`.
fn build_audioclient(&self) -> Result<*mut winapi::IAudioClient, IoError> {
let mut lock = try!(self.ensure_future_audio_client());
let client = lock.unwrap().0;
*lock = None;
Ok(client)
}
pub fn get_supported_formats_list(&self)
-> Result<SupportedFormatsIterator, FormatsEnumerationError>
{
// We always create voices in shared mode, therefore all samples go through an audio
// processor to mix them together.
// However there is no way to query the list of all formats that are supported by the
@ -88,7 +101,11 @@ impl Endpoint {
// initializing COM because we call `CoTaskMemFree`
com::com_initialized();
let lock = self.ensure_future_audio_client();
let lock = match self.ensure_future_audio_client() {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(FormatsEnumerationError::DeviceNotAvailable),
e => e.unwrap(),
};
let client = lock.unwrap().0;
unsafe {
@ -108,7 +125,7 @@ impl Endpoint {
ole32::CoTaskMemFree(format_ptr as *mut _);
Some(format).into_iter()
Ok(Some(format).into_iter())
}
}
}

View File

@ -4,13 +4,13 @@ use super::winapi;
use super::Endpoint;
use super::check_result;
use std::io::Error as IoError;
use std::cmp;
use std::slice;
use std::mem;
use std::ptr;
use std::marker::PhantomData;
use CreationError;
use Format;
pub struct Voice {
@ -28,22 +28,18 @@ unsafe impl Send for Voice {}
unsafe impl Sync for Voice {}
impl Voice {
pub fn new(end_point: &Endpoint, format: &Format) -> Result<Voice, IoError> {
pub fn new(end_point: &Endpoint, format: &Format) -> Result<Voice, CreationError> {
// FIXME: release everything
unsafe {
// making sure that COM is initialized
// it's not actually sure that this is required, but when in doubt do it
com::com_initialized();
// activating the end point in order to get a `IAudioClient`
let audio_client: *mut winapi::IAudioClient = {
let mut audio_client = mem::uninitialized();
let hresult = (*end_point.device).Activate(&winapi::IID_IAudioClient, winapi::CLSCTX_ALL,
ptr::null_mut(), &mut audio_client);
// can fail if the device has been disconnected since we enumerated it, or if
// the device doesn't support playback for some reason
try!(check_result(hresult));
audio_client as *mut _
// obtaining a `IAudioClient`
let audio_client = match end_point.build_audioclient() {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(CreationError::DeviceNotAvailable),
e => e.unwrap(),
};
// computing the format and initializing the device
@ -61,7 +57,12 @@ impl Voice {
let mut format_ptr: *mut winapi::WAVEFORMATEX = mem::uninitialized();
let hresult = (*audio_client).IsFormatSupported(winapi::AUDCLNT_SHAREMODE::AUDCLNT_SHAREMODE_SHARED,
&format_attempt, &mut format_ptr);
try!(check_result(hresult));
match check_result(hresult) {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(CreationError::DeviceNotAvailable),
e => e.unwrap(),
};
let format = if format_ptr.is_null() {
&format_attempt
@ -78,7 +79,11 @@ impl Voice {
ole32::CoTaskMemFree(format_ptr as *mut _);
}
try!(check_result(hresult));
match check_result(hresult) {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(CreationError::DeviceNotAvailable),
e => e.unwrap(),
};
format_copy
};
@ -87,7 +92,11 @@ impl Voice {
let max_frames_in_buffer = {
let mut max_frames_in_buffer = mem::uninitialized();
let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer);
try!(check_result(hresult));
match check_result(hresult) {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(CreationError::DeviceNotAvailable),
e => e.unwrap(),
};
max_frames_in_buffer
};
@ -95,8 +104,14 @@ impl Voice {
let render_client = {
let mut render_client: *mut winapi::IAudioRenderClient = mem::uninitialized();
let hresult = (*audio_client).GetService(&winapi::IID_IAudioRenderClient,
mem::transmute(&mut render_client));
try!(check_result(hresult));
&mut render_client as *mut *mut winapi::IAudioRenderClient
as *mut _);
match check_result(hresult) {
Err(ref e) if e.raw_os_error() == Some(winapi::AUDCLNT_E_DEVICE_INVALIDATED) =>
return Err(CreationError::DeviceNotAvailable),
e => e.unwrap(),
};
&mut *render_client
};