diff --git a/Cargo.toml b/Cargo.toml index 843ac8c57..c4ced566c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ keywords = ["audio", "sound"] [dependencies] lazy_static = "0.2" +failure = "0.1" +failure_derive = "0.1" [dev-dependencies] hound = "3.0" diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index 47d17bfe6..099eed0b0 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -4,10 +4,9 @@ extern crate libc; pub use self::enumerate::{Devices, default_input_device, default_output_device}; use ChannelCount; -use CreationError; -use DefaultFormatError; +use ErrorKind; +use Result; use Format; -use FormatsEnumerationError; use SampleFormat; use SampleRate; use StreamData; @@ -19,6 +18,8 @@ use std::{cmp, ffi, iter, mem, ptr}; use std::sync::Mutex; use std::sync::atomic::{AtomicUsize, Ordering}; use std::vec::IntoIter as VecIntoIter; +use std::result::Result as StdResult; +use failure::{ResultExt, err_msg}; pub type SupportedInputFormats = VecIntoIter; pub type SupportedOutputFormats = VecIntoIter; @@ -76,24 +77,18 @@ impl Device { self.0.clone() } - unsafe fn supported_formats( - &self, - stream_t: alsa::snd_pcm_stream_t, - ) -> Result, FormatsEnumerationError> + unsafe fn supported_formats(&self, stream_t: alsa::snd_pcm_stream_t) + -> Result> { + let device_name = ffi::CString::new(&self.0[..]).context(ErrorKind::NullInString)?; let mut handle = mem::uninitialized(); - let device_name = ffi::CString::new(&self.0[..]).expect("Unable to get device name"); - match alsa::snd_pcm_open( + check_errors(alsa::snd_pcm_open( &mut handle, device_name.as_ptr() as *const _, stream_t, alsa::SND_PCM_NONBLOCK, - ) { - -2 | - -16 /* determined empirically */ => return Err(FormatsEnumerationError::DeviceNotAvailable), - e => check_errors(e).expect("device not available") - } + )).map_err(err_msg).context(ErrorKind::DeviceNotAvailable)?; let hw_params = HwParams::alloc(); match check_errors(alsa::snd_pcm_hw_params_any(handle, hw_params.0)) { @@ -110,37 +105,37 @@ impl Device { //SND_PCM_FORMAT_S16_BE, (SampleFormat::U16, alsa::SND_PCM_FORMAT_U16_LE), //SND_PCM_FORMAT_U16_BE, - /*SND_PCM_FORMAT_S24_LE, - SND_PCM_FORMAT_S24_BE, - SND_PCM_FORMAT_U24_LE, - SND_PCM_FORMAT_U24_BE, - SND_PCM_FORMAT_S32_LE, - SND_PCM_FORMAT_S32_BE, - SND_PCM_FORMAT_U32_LE, - SND_PCM_FORMAT_U32_BE,*/ + /*SND_PCM_FORMAT_S24_LE, + SND_PCM_FORMAT_S24_BE, + SND_PCM_FORMAT_U24_LE, + SND_PCM_FORMAT_U24_BE, + SND_PCM_FORMAT_S32_LE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_U32_LE, + SND_PCM_FORMAT_U32_BE,*/ (SampleFormat::F32, alsa::SND_PCM_FORMAT_FLOAT_LE) /*SND_PCM_FORMAT_FLOAT_BE, - SND_PCM_FORMAT_FLOAT64_LE, - SND_PCM_FORMAT_FLOAT64_BE, - SND_PCM_FORMAT_IEC958_SUBFRAME_LE, - SND_PCM_FORMAT_IEC958_SUBFRAME_BE, - SND_PCM_FORMAT_MU_LAW, - SND_PCM_FORMAT_A_LAW, - SND_PCM_FORMAT_IMA_ADPCM, - SND_PCM_FORMAT_MPEG, - SND_PCM_FORMAT_GSM, - SND_PCM_FORMAT_SPECIAL, - SND_PCM_FORMAT_S24_3LE, - SND_PCM_FORMAT_S24_3BE, - SND_PCM_FORMAT_U24_3LE, - SND_PCM_FORMAT_U24_3BE, - SND_PCM_FORMAT_S20_3LE, - SND_PCM_FORMAT_S20_3BE, - SND_PCM_FORMAT_U20_3LE, - SND_PCM_FORMAT_U20_3BE, - SND_PCM_FORMAT_S18_3LE, - SND_PCM_FORMAT_S18_3BE, - SND_PCM_FORMAT_U18_3LE, - SND_PCM_FORMAT_U18_3BE,*/, + SND_PCM_FORMAT_FLOAT64_LE, + SND_PCM_FORMAT_FLOAT64_BE, + SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + SND_PCM_FORMAT_IEC958_SUBFRAME_BE, + SND_PCM_FORMAT_MU_LAW, + SND_PCM_FORMAT_A_LAW, + SND_PCM_FORMAT_IMA_ADPCM, + SND_PCM_FORMAT_MPEG, + SND_PCM_FORMAT_GSM, + SND_PCM_FORMAT_SPECIAL, + SND_PCM_FORMAT_S24_3LE, + SND_PCM_FORMAT_S24_3BE, + SND_PCM_FORMAT_U24_3LE, + SND_PCM_FORMAT_U24_3BE, + SND_PCM_FORMAT_S20_3LE, + SND_PCM_FORMAT_S20_3BE, + SND_PCM_FORMAT_U20_3LE, + SND_PCM_FORMAT_U20_3BE, + SND_PCM_FORMAT_S18_3LE, + SND_PCM_FORMAT_S18_3BE, + SND_PCM_FORMAT_U18_3LE, + SND_PCM_FORMAT_U18_3BE,*/, ]; let mut supported_formats = Vec::new(); @@ -157,12 +152,14 @@ impl Device { 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"); + .map_err(err_msg) + .context(ErrorKind::CannotGetMinimumSupportedRate)?; 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"); + .map_err(err_msg) + .context(ErrorKind::CannotGetMaximumSupportedRate)?; let sample_rates = if min_rate == max_rate { vec![(min_rate, max_rate)] @@ -247,13 +244,13 @@ impl Device { Ok(output.into_iter()) } - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) } } - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) } @@ -261,19 +258,11 @@ impl Device { // ALSA does not offer default stream formats, so instead we compare all supported formats by // the `SupportedFormat::cmp_default_heuristics` order and select the greatest. - fn default_format( - &self, - stream_t: alsa::snd_pcm_stream_t, - ) -> Result - { + fn default_format(&self, stream_t: alsa::snd_pcm_stream_t) -> Result { let mut formats: Vec<_> = unsafe { - match self.supported_formats(stream_t) { - Err(FormatsEnumerationError::DeviceNotAvailable) => { - return Err(DefaultFormatError::DeviceNotAvailable); - }, - Ok(fmts) => fmts.collect(), - } - }; + self.supported_formats(stream_t) + .map(|fmts| fmts.collect()) + }?; formats.sort_by(|a, b| a.cmp_default_heuristics(b)); @@ -288,15 +277,15 @@ impl Device { } Ok(format) }, - None => Err(DefaultFormatError::StreamTypeNotSupported) + None => Err(ErrorKind::StreamTypeNotSupported.into()) } } - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { self.default_format(alsa::SND_PCM_STREAM_CAPTURE) } - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { self.default_format(alsa::SND_PCM_STREAM_PLAYBACK) } } @@ -616,25 +605,18 @@ impl EventLoop { } } - pub fn build_input_stream( - &self, - device: &Device, - format: &Format, - ) -> Result + pub fn build_input_stream(&self, device: &Device, format: &Format) -> Result { unsafe { let name = ffi::CString::new(device.0.clone()).expect("unable to clone device"); let mut capture_handle = mem::uninitialized(); - match alsa::snd_pcm_open( + check_errors(alsa::snd_pcm_open( &mut capture_handle, name.as_ptr(), alsa::SND_PCM_STREAM_CAPTURE, alsa::SND_PCM_NONBLOCK, - ) { - -16 /* determined empirically */ => return Err(CreationError::DeviceNotAvailable), - e => check_errors(e).expect("Device unavailable") - } + )).map_err(err_msg).context(ErrorKind::DeviceNotAvailable)?; let hw_params = HwParams::alloc(); set_hw_params_from_format(capture_handle, &hw_params, format); @@ -676,25 +658,17 @@ impl EventLoop { } } - pub fn build_output_stream( - &self, - device: &Device, - format: &Format, - ) -> Result - { + pub fn build_output_stream(&self, device: &Device, format: &Format) -> Result { unsafe { - let name = ffi::CString::new(device.0.clone()).expect("unable to clone device"); + let name = ffi::CString::new(device.0.clone()).context(ErrorKind::NullInString)?; let mut playback_handle = mem::uninitialized(); - match alsa::snd_pcm_open( + check_errors(alsa::snd_pcm_open( &mut playback_handle, name.as_ptr(), alsa::SND_PCM_STREAM_PLAYBACK, alsa::SND_PCM_NONBLOCK, - ) { - -16 /* determined empirically */ => return Err(CreationError::DeviceNotAvailable), - e => check_errors(e).expect("Device unavailable") - } + )).map_err(err_msg).context(ErrorKind::DeviceNotAvailable)?; let hw_params = HwParams::alloc(); set_hw_params_from_format(playback_handle, &hw_params, format); @@ -926,19 +900,20 @@ impl<'a, T> OutputBuffer<'a, T> { } } +/// Convert an error code into a `Result` with the value of `snd_strerror(code)` #[inline] -fn check_errors(err: libc::c_int) -> Result<(), String> { +fn check_errors(err: libc::c_int) -> StdResult<(), String> { use std::ffi; if err < 0 { unsafe { let s = ffi::CStr::from_ptr(alsa::snd_strerror(err)) - .to_bytes() - .to_vec(); - let s = String::from_utf8(s).expect("Streaming error occured"); + .to_bytes(); + let s = String::from_utf8_lossy(s).into_owned(); return Err(s); } } Ok(()) } + diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index ed6c32d3f..8fa119841 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -2,10 +2,10 @@ extern crate coreaudio; extern crate core_foundation_sys; use ChannelCount; -use CreationError; -use DefaultFormatError; +use ErrorKind; +use Error; +use Result; use Format; -use FormatsEnumerationError; use Sample; use SampleFormat; use SampleRate; @@ -22,6 +22,7 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use std::slice; +use std::result::Result as StdResult; use self::coreaudio::audio_unit::{AudioUnit, Scope, Element}; use self::coreaudio::audio_unit::render_callback::{self, data}; @@ -104,10 +105,7 @@ impl Device { } // Logic re-used between `supported_input_formats` and `supported_output_formats`. - fn supported_formats( - &self, - scope: AudioObjectPropertyScope, - ) -> Result + fn supported_formats(&self, scope: AudioObjectPropertyScope) -> Result { let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyStreamConfiguration, @@ -211,20 +209,17 @@ impl Device { } } - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeInput) } - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { self.supported_formats(kAudioObjectPropertyScopeOutput) } - fn default_format( - &self, - scope: AudioObjectPropertyScope, - ) -> Result + fn default_format(&self, scope: AudioObjectPropertyScope) -> Result { - fn default_format_error_from_os_status(status: OSStatus) -> Option { + fn default_format_error_from_os_status(status: OSStatus) -> Option { let err = match coreaudio::Error::from_os_status(status) { Err(err) => err, Ok(_) => return None, @@ -234,8 +229,8 @@ impl Device { coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | - coreaudio::Error::AudioFormat(_) => Some(DefaultFormatError::StreamTypeNotSupported), - _ => Some(DefaultFormatError::DeviceNotAvailable), + coreaudio::Error::AudioFormat(_) => Some(ErrorKind::StreamTypeNotSupported), + _ => Some(ErrorKind::DeviceNotAvailable), } } @@ -260,7 +255,7 @@ impl Device { if status != kAudioHardwareNoError as i32 { let err = default_format_error_from_os_status(status) .expect("no known error for OSStatus"); - return Err(err); + return Err(err.into()); } let sample_format = { @@ -270,7 +265,7 @@ impl Device { ); let flags = match audio_format { Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags, - _ => return Err(DefaultFormatError::StreamTypeNotSupported), + _ => return Err(ErrorKind::StreamTypeNotSupported.into()), }; let maybe_sample_format = coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame( @@ -280,7 +275,7 @@ impl Device { match maybe_sample_format { Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32, Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16, - _ => return Err(DefaultFormatError::StreamTypeNotSupported), + _ => return Err(ErrorKind::StreamTypeNotSupported.into()), } }; @@ -293,11 +288,11 @@ impl Device { } } - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeInput) } - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { self.default_format(kAudioObjectPropertyScopeOutput) } } @@ -328,15 +323,15 @@ struct StreamInner { } // TODO need stronger error identification -impl From for CreationError { - fn from(err: coreaudio::Error) -> CreationError { +impl From for Error { + fn from(err: coreaudio::Error) -> Error { match err { coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat | coreaudio::Error::NoKnownSubtype | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) | coreaudio::Error::AudioCodec(_) | - coreaudio::Error::AudioFormat(_) => CreationError::FormatNotSupported, - _ => CreationError::DeviceNotAvailable, + coreaudio::Error::AudioFormat(_) => ErrorKind::FormatNotSupported.into(), + _ => ErrorKind::DeviceNotAvailable.into(), } } } @@ -369,7 +364,7 @@ fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription { asbd } -fn audio_unit_from_device(device: &Device, input: bool) -> Result { +fn audio_unit_from_device(device: &Device, input: bool) -> StdResult { let mut audio_unit = { let au_type = if cfg!(target_os = "ios") { // The HalOutput unit isn't available in iOS unfortunately. @@ -469,11 +464,7 @@ impl EventLoop { } #[inline] - pub fn build_input_stream( - &self, - device: &Device, - format: &Format, - ) -> Result + pub fn build_input_stream(&self, device: &Device, format: &Format) -> Result { // The scope and element for working with a device's input stream. let scope = Scope::Output; @@ -484,8 +475,8 @@ impl EventLoop { // Get the current sample rate. let mut property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyNominalSampleRate, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMaster, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMaster, }; let sample_rate: f64 = 0.0; let data_size = mem::size_of::() as u32; @@ -499,16 +490,17 @@ impl EventLoop { ); coreaudio::Error::from_os_status(status)?; - // If the requested sample rate is different to the device sample rate, update the device. + // If the requested sample rate is different to the device sample rate, + // update the device. if sample_rate as u32 != format.sample_rate.0 { - // In order to avoid breaking existing input streams we `panic!` if there is already an - // active input stream for this device with the actual sample rate. + // In order to avoid breaking existing input streams we `panic!` if there is + // already an active input stream for this device with the actual sample rate. for stream in &*self.streams.lock().unwrap() { if let Some(stream) = stream.as_ref() { if stream.device_id == device.audio_device_id { - panic!("cannot change device sample rate for stream as an existing stream \ - is already running at the current sample rate."); + panic!("cannot change device sample rate for stream as an existing \ + stream is already running at the current sample rate."); } } } @@ -545,7 +537,7 @@ impl EventLoop { .iter() .position(|r| r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate); let range_index = match maybe_index { - None => return Err(CreationError::FormatNotSupported), + None => return Err(ErrorKind::FormatNotSupported.into()), Some(i) => i, }; @@ -687,11 +679,7 @@ impl EventLoop { } #[inline] - pub fn build_output_stream( - &self, - device: &Device, - format: &Format, - ) -> Result + pub fn build_output_stream(&self, device: &Device, format: &Format) -> Result { let mut audio_unit = audio_unit_from_device(device, false)?; diff --git a/src/emscripten/mod.rs b/src/emscripten/mod.rs index 9b4449698..2834cd50b 100644 --- a/src/emscripten/mod.rs +++ b/src/emscripten/mod.rs @@ -8,10 +8,8 @@ use stdweb::unstable::TryInto; use stdweb::web::TypedArray; use stdweb::web::set_timeout; -use CreationError; -use DefaultFormatError; +use Result; use Format; -use FormatsEnumerationError; use Sample; use StreamData; use SupportedFormat; @@ -87,12 +85,12 @@ impl EventLoop { } #[inline] - pub fn build_input_stream(&self, _: &Device, _format: &Format) -> Result { + pub fn build_input_stream(&self, _: &Device, _format: &Format) -> Result { unimplemented!(); } #[inline] - pub fn build_output_stream(&self, _: &Device, _format: &Format) -> Result { + pub fn build_output_stream(&self, _: &Device, _format: &Format) -> Result { let stream = js!(return new AudioContext()).into_reference().unwrap(); let mut streams = self.streams.lock().unwrap(); @@ -195,12 +193,12 @@ impl Device { } #[inline] - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { unimplemented!(); } #[inline] - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { // TODO: right now cpal's API doesn't allow flexibility here // "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if // this ever becomes more flexible, don't forget to change that @@ -216,11 +214,11 @@ impl Device { ) } - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { unimplemented!(); } - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { unimplemented!(); } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..4f58031d3 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,70 @@ +use failure::{Context, Fail, Backtrace}; +use std::fmt::{self, Display}; +use std::result::Result as StdResult; + +/// The type of all errors in this library. +#[derive(Debug)] +pub struct Error { + inner: Context +} + +/// The possible kinds of errors that functions in this library can return +#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] +pub enum ErrorKind { + /// The device no longer exists. This can happen if the device is disconnected while the + /// program is running. + #[fail(display="device not available")] + DeviceNotAvailable, + /// Returned if e.g. the default input format was requested on an output-only audio device. + #[fail(display="stream type not supported")] + StreamTypeNotSupported, + /// The required format is not supported. + #[fail(display="format not supported")] + FormatNotSupported, + /// There was an error getting the minimum supported sample rate. + #[fail(display="error getting minimum supported rate")] + CannotGetMinimumSupportedRate, + /// There was an error getting the maximum supported sample rate. + #[fail(display="error getting maximum supported rate")] + CannotGetMaximumSupportedRate, + /// Tried to create a C string from a rust String with a null byte (`\0`). + #[fail(display="tried to create a C style string from a string with a null byte")] + NullInString, +} + +impl Error { + /// Get the kind of this error + pub fn kind(&self) -> &ErrorKind { + self.inner.get_context() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Self { + Error { inner: Context::new(kind) } + } +} + +impl From> for Error { + fn from(inner: Context) -> Self { + Error { inner } + } +} + +impl Fail for Error { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +pub type Result = StdResult; diff --git a/src/lib.rs b/src/lib.rs index e801fee4f..772b60039 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,10 @@ #![recursion_limit = "512"] +#[macro_use] +extern crate failure_derive; +extern crate failure; + #[cfg(target_os = "windows")] #[macro_use] extern crate lazy_static; @@ -129,11 +133,11 @@ pub use samples_formats::{Sample, SampleFormat}; target_os = "macos", target_os = "ios", target_os = "emscripten")))] use null as cpal_impl; -use std::error::Error; -use std::fmt; use std::iter; use std::ops::{Deref, DerefMut}; +pub use error::*; +mod error; mod null; mod samples_formats; @@ -283,34 +287,6 @@ pub struct SupportedInputFormats(cpal_impl::SupportedInputFormats); /// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats). pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats); -/// 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, -} - -/// May occur when attempting to request the default input or output stream format from a `Device`. -#[derive(Debug)] -pub enum DefaultFormatError { - /// The device no longer exists. This can happen if the device is disconnected while the - /// program is running. - DeviceNotAvailable, - /// Returned if e.g. the default input format was requested on an output-only audio device. - StreamTypeNotSupported, -} - -/// 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, -} - /// An iterator yielding all `Device`s currently available to the system. /// /// Can be empty if the system does not support audio in general. @@ -370,7 +346,7 @@ impl Device { /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). #[inline] - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { Ok(SupportedInputFormats(self.0.supported_input_formats()?)) } @@ -378,19 +354,19 @@ impl Device { /// /// Can return an error if the device is no longer valid (eg. it has been disconnected). #[inline] - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { Ok(SupportedOutputFormats(self.0.supported_output_formats()?)) } /// The default input stream format for the device. #[inline] - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { self.0.default_input_format() } /// The default output stream format for the device. #[inline] - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { self.0.default_output_format() } } @@ -409,11 +385,7 @@ impl EventLoop { /// Can return an error if the device is no longer valid, or if the input stream format is not /// supported by the device. #[inline] - pub fn build_input_stream( - &self, - device: &Device, - format: &Format, - ) -> Result + pub fn build_input_stream(&self, device: &Device, format: &Format) -> Result { self.0.build_input_stream(&device.0, format).map(StreamId) } @@ -425,11 +397,7 @@ impl EventLoop { /// Can return an error if the device is no longer valid, or if the output stream format is not /// supported by the device. #[inline] - pub fn build_output_stream( - &self, - device: &Device, - format: &Format, - ) -> Result + pub fn build_output_stream(&self, device: &Device, format: &Format) -> Result { self.0.build_output_stream(&device.0, format).map(StreamId) } @@ -700,46 +668,6 @@ impl Iterator for SupportedOutputFormats { } } -impl fmt::Display for FormatsEnumerationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "{}", self.description()) - } -} - -impl Error for FormatsEnumerationError { - #[inline] - fn description(&self) -> &str { - match self { - &FormatsEnumerationError::DeviceNotAvailable => { - "The requested device is no longer available (for example, it has been unplugged)." - }, - } - } -} - -impl fmt::Display for CreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "{}", self.description()) - } -} - -impl Error for CreationError { - #[inline] - 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." - }, - } - } -} - // 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. // diff --git a/src/null/mod.rs b/src/null/mod.rs index d74506c16..d864d9752 100644 --- a/src/null/mod.rs +++ b/src/null/mod.rs @@ -2,10 +2,9 @@ use std::marker::PhantomData; -use CreationError; -use DefaultFormatError; +use Result; +use ErrorKind; use Format; -use FormatsEnumerationError; use StreamData; use SupportedFormat; @@ -21,17 +20,21 @@ impl EventLoop { pub fn run(&self, _callback: F) -> ! where F: FnMut(StreamId, StreamData) { - loop { /* TODO: don't spin */ } + use std::sync::mpsc::channel; + let (_, rx) = channel::<()>(); + rx.recv().unwrap(); + // convince the compiler that we never return + loop {} } #[inline] - pub fn build_input_stream(&self, _: &Device, _: &Format) -> Result { - Err(CreationError::DeviceNotAvailable) + pub fn build_input_stream(&self, _: &Device, _: &Format) -> Result { + Err(ErrorKind::DeviceNotAvailable.into()) } #[inline] - pub fn build_output_stream(&self, _: &Device, _: &Format) -> Result { - Err(CreationError::DeviceNotAvailable) + pub fn build_output_stream(&self, _: &Device, _: &Format) -> Result { + Err(ErrorKind::DeviceNotAvailable.into()) } #[inline] @@ -80,22 +83,22 @@ pub struct Device; impl Device { #[inline] - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { unimplemented!() } #[inline] - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { unimplemented!() } #[inline] - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { unimplemented!() } #[inline] - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { unimplemented!() } diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index 0ef90eb82..5e663149c 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -7,10 +7,12 @@ use std::os::windows::ffi::OsStringExt; use std::ptr; use std::slice; use std::sync::{Arc, Mutex, MutexGuard}; +use std::result::Result as StdResult; +use failure::{Fail, ResultExt}; -use DefaultFormatError; +use ErrorKind; +use Result; use Format; -use FormatsEnumerationError; use SampleFormat; use SampleRate; use SupportedFormat; @@ -159,8 +161,8 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow // Given the audio client and format, returns whether or not the format is supported. pub unsafe fn is_format_supported( client: *const IAudioClient, - waveformatex_ptr: *const mmreg::WAVEFORMATEX, -) -> Result + waveformatex_ptr: *const mmreg::WAVEFORMATEX +) -> Result { @@ -208,7 +210,7 @@ pub unsafe fn is_format_supported( // has been found, but not an exact match) so we also treat this as unsupported. match (result, check_result(result)) { (_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(FormatsEnumerationError::DeviceNotAvailable); + return Err(ErrorKind::DeviceNotAvailable.into()); }, (_, Err(_)) => { Ok(false) @@ -337,7 +339,7 @@ impl Device { /// Ensures that `future_audio_client` contains a `Some` and returns a locked mutex to it. fn ensure_future_audio_client(&self) - -> Result>, IoError> { + -> StdResult>, IoError> { let mut lock = self.future_audio_client.lock().unwrap(); if lock.is_some() { return Ok(lock); @@ -363,7 +365,7 @@ impl Device { /// Returns an uninitialized `IAudioClient`. #[inline] - pub(crate) fn build_audioclient(&self) -> Result<*mut IAudioClient, IoError> { + pub(crate) fn build_audioclient(&self) -> StdResult<*mut IAudioClient, IoError> { let mut lock = self.ensure_future_audio_client()?; let client = lock.unwrap().0; *lock = None; @@ -381,31 +383,25 @@ impl Device { // number of channels seems to be supported. Any more or less returns an invalid // parameter error. Thus we just assume that the default number of channels is the only // number supported. - fn supported_formats(&self) -> Result { + fn supported_formats(&self) -> Result { // initializing COM because we call `CoTaskMemFree` to release the format. com::com_initialized(); // 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(FormatsEnumerationError::DeviceNotAvailable), - e => e.unwrap(), + Err(e) => return Err(e.context(ErrorKind::DeviceNotAvailable).into()), + Ok(lock) => lock, }; let client = lock.unwrap().0; unsafe { // 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)) { - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(FormatsEnumerationError::DeviceNotAvailable); - }, - Err(e) => panic!("{:?}", e), - Ok(()) => (), - }; + check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) + .map_err(|e| e.context(ErrorKind::DeviceNotAvailable))?; // If the default format can't succeed we have no hope of finding other formats. - assert_eq!(try!(is_format_supported(client, default_waveformatex_ptr.0)), true); + assert_eq!(is_format_supported(client, default_waveformatex_ptr.0)?, true); // Copy the format to use as a test format (as to avoid mutating the original format). let mut test_format = { @@ -457,7 +453,7 @@ impl Device { } } - pub fn supported_input_formats(&self) -> Result { + pub fn supported_input_formats(&self) -> Result { if self.data_flow() == eCapture { self.supported_formats() // If it's an output device, assume no input formats. @@ -466,7 +462,7 @@ impl Device { } } - pub fn supported_output_formats(&self) -> Result { + pub fn supported_output_formats(&self) -> Result { if self.data_flow() == eRender { self.supported_formats() // If it's an input device, assume no output formats. @@ -479,13 +475,13 @@ impl Device { // processor to mix them together. // // One format is guaranteed to be supported, the one returned by `GetMixFormat`. - fn default_format(&self) -> Result { + fn default_format(&self) -> Result { // initializing COM because we call `CoTaskMemFree` com::com_initialized(); let lock = match self.ensure_future_audio_client() { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => - return Err(DefaultFormatError::DeviceNotAvailable), + return Err(ErrorKind::DeviceNotAvailable.into()), e => e.unwrap(), }; let client = lock.unwrap().0; @@ -493,15 +489,14 @@ impl Device { unsafe { let mut format_ptr = WaveFormatExPtr(mem::uninitialized()); match check_result((*client).GetMixFormat(&mut format_ptr.0)) { - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - return Err(DefaultFormatError::DeviceNotAvailable); + Err(e) => { + Err(e).context(ErrorKind::DeviceNotAvailable)? }, - Err(e) => panic!("{:?}", e), Ok(()) => (), }; format_from_waveformatex_ptr(format_ptr.0) - .ok_or(DefaultFormatError::StreamTypeNotSupported) + .ok_or(ErrorKind::StreamTypeNotSupported.into()) } } @@ -510,20 +505,20 @@ impl Device { endpoint.data_flow() } - pub fn default_input_format(&self) -> Result { + pub fn default_input_format(&self) -> Result { if self.data_flow() == eCapture { self.default_format() } else { - Err(DefaultFormatError::StreamTypeNotSupported) + Err(ErrorKind::StreamTypeNotSupported.into()) } } - pub fn default_output_format(&self) -> Result { + pub fn default_output_format(&self) -> Result { let data_flow = self.data_flow(); if data_flow == eRender { self.default_format() } else { - Err(DefaultFormatError::StreamTypeNotSupported) + Err(ErrorKind::StreamTypeNotSupported.into()) } } } diff --git a/src/wasapi/stream.rs b/src/wasapi/stream.rs index ff8ef7400..eb9e74103 100644 --- a/src/wasapi/stream.rs +++ b/src/wasapi/stream.rs @@ -19,8 +19,10 @@ use std::slice; use std::sync::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; +use failure::{Fail, ResultExt}; -use CreationError; +use Result; +use ErrorKind; use Format; use SampleFormat; use StreamData; @@ -110,7 +112,7 @@ impl EventLoop { &self, device: &Device, format: &Format, - ) -> Result + ) -> Result { unsafe { // Making sure that COM is initialized. @@ -118,22 +120,19 @@ impl EventLoop { com::com_initialized(); // Obtaining a `IAudioClient`. - let audio_client = match device.build_audioclient() { - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => - return Err(CreationError::DeviceNotAvailable), - e => e.unwrap(), - }; + let audio_client = device.build_audioclient() + .context(ErrorKind::DeviceNotAvailable)?; // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) - .ok_or(CreationError::FormatNotSupported)?; + .ok_or(ErrorKind::FormatNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { - Ok(false) => return Err(CreationError::FormatNotSupported), - Err(_) => return Err(CreationError::DeviceNotAvailable), + Ok(false) => return Err(ErrorKind::FormatNotSupported.into()), + Err(_) => return Err(ErrorKind::DeviceNotAvailable.into()), _ => (), } @@ -146,18 +145,7 @@ impl EventLoop { &format_attempt.Format, ptr::null(), ); - match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, - Err(e) => { - (*audio_client).Release(); - panic!("{:?}", e); - }, - Ok(()) => (), - }; + check_result(hresult).map_err(|e| e.context(ErrorKind::DeviceNotAvailable))?; format_attempt.Format }; @@ -168,14 +156,9 @@ impl EventLoop { let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer); match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, Err(e) => { (*audio_client).Release(); - panic!("{:?}", e); + return Err(e.context(ErrorKind::DeviceNotAvailable).into()); }, Ok(()) => (), }; @@ -192,9 +175,9 @@ impl EventLoop { } match check_result((*audio_client).SetEventHandle(event)) { - Err(_) => { + Err(_e) => { (*audio_client).Release(); - panic!("Failed to call SetEventHandle") + panic!("Failed to call SetEventHandle"); }, Ok(_) => (), }; @@ -211,14 +194,9 @@ impl EventLoop { ); match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, Err(e) => { (*audio_client).Release(); - panic!("{:?}", e); + return Err(e.context(ErrorKind::DeviceNotAvailable).into()); }, Ok(()) => (), }; @@ -260,7 +238,7 @@ impl EventLoop { &self, device: &Device, format: &Format, - ) -> Result + ) -> Result { unsafe { // Making sure that COM is initialized. @@ -269,21 +247,20 @@ impl EventLoop { // Obtaining a `IAudioClient`. let audio_client = match device.build_audioclient() { - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => - return Err(CreationError::DeviceNotAvailable), - e => e.unwrap(), + Ok(client) => client, + Err(e) => return Err(e.context(ErrorKind::DeviceNotAvailable).into()), }; // Computing the format and initializing the device. let waveformatex = { let format_attempt = format_to_waveformatextensible(format) - .ok_or(CreationError::FormatNotSupported)?; + .ok_or(ErrorKind::FormatNotSupported)?; let share_mode = AUDCLNT_SHAREMODE_SHARED; // Ensure the format is supported. match super::device::is_format_supported(audio_client, &format_attempt.Format) { - Ok(false) => return Err(CreationError::FormatNotSupported), - Err(_) => return Err(CreationError::DeviceNotAvailable), + Ok(false) => return Err(ErrorKind::FormatNotSupported.into()), + Err(e) => return Err(e.context(ErrorKind::DeviceNotAvailable).into()), _ => (), } @@ -295,14 +272,9 @@ impl EventLoop { &format_attempt.Format, ptr::null()); match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, Err(e) => { (*audio_client).Release(); - panic!("{:?}", e); + return Err(e.context(ErrorKind::DeviceNotAvailable).into()); }, Ok(()) => (), }; @@ -335,14 +307,9 @@ impl EventLoop { let hresult = (*audio_client).GetBufferSize(&mut max_frames_in_buffer); match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, Err(e) => { (*audio_client).Release(); - panic!("{:?}", e); + return Err(e.context(ErrorKind::DeviceNotAvailable).into()); }, Ok(()) => (), }; @@ -359,14 +326,9 @@ impl EventLoop { *mut _); match check_result(hresult) { - Err(ref e) - if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - (*audio_client).Release(); - return Err(CreationError::DeviceNotAvailable); - }, Err(e) => { (*audio_client).Release(); - panic!("{:?}", e); + return Err(e.context(ErrorKind::DeviceNotAvailable).into()); }, Ok(()) => (), }; @@ -521,7 +483,7 @@ impl EventLoop { ); check_result(hresult).unwrap(); debug_assert!(!buffer.is_null()); - let buffer_len = frames_available as usize + let buffer_len = frames_available as usize * stream.bytes_per_frame as usize / sample_size; // Simplify the capture callback sample format branches. @@ -538,11 +500,14 @@ impl EventLoop { // Release the buffer. let hresult = (*capture_client).ReleaseBuffer(frames_available); - match check_result(hresult) { + if let Err(e) = check_result(hresult) { // Ignoring unavailable device error. - Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { - }, - e => e.unwrap(), + if ! (e.raw_os_error() + == Some(AUDCLNT_E_DEVICE_INVALIDATED)) + { + panic!("{:?}", + e.context(ErrorKind::DeviceNotAvailable)); + } }; }}; } @@ -561,9 +526,9 @@ impl EventLoop { &mut buffer as *mut *mut _, ); // FIXME: can return `AUDCLNT_E_DEVICE_INVALIDATED` - check_result(hresult).unwrap(); + check_result(hresult).unwrap(); debug_assert!(!buffer.is_null()); - let buffer_len = frames_available as usize + let buffer_len = frames_available as usize * stream.bytes_per_frame as usize / sample_size; // Simplify the render callback sample format branches.