From 105086a108a6914aaa26b48befdfb2a971194e48 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Fri, 21 Jun 2019 00:53:11 +0200 Subject: [PATCH] Add new `DeviceNameError` type The coreaudio and wasapi backends may both potentially fail to produce the name associated with a device. This changes the API to allow for returning the errors in these cases. --- src/alsa/mod.rs | 5 +++-- src/coreaudio/mod.rs | 14 ++++++++------ src/emscripten/mod.rs | 5 +++-- src/lib.rs | 19 ++++++++++++++++++- src/null/mod.rs | 5 +++-- src/wasapi/device.rs | 25 +++++++++++++++++++------ 6 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index 0b9f675..969dc14 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -7,6 +7,7 @@ use ChannelCount; use BackendSpecificError; use BuildStreamError; use DefaultFormatError; +use DeviceNameError; use Format; use SupportedFormatsError; use SampleFormat; @@ -74,8 +75,8 @@ pub struct Device(String); impl Device { #[inline] - pub fn name(&self) -> String { - self.0.clone() + pub fn name(&self) -> Result { + Ok(self.0.clone()) } unsafe fn supported_formats( diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index eedd0c1..828db72 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -5,6 +5,7 @@ use ChannelCount; use BackendSpecificError; use BuildStreamError; use DefaultFormatError; +use DeviceNameError; use Format; use SupportedFormatsError; use Sample; @@ -76,7 +77,7 @@ pub struct Device { } impl Device { - pub fn name(&self) -> String { + pub fn name(&self) -> Result { let property_address = AudioObjectPropertyAddress { mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioDevicePropertyScopeOutput, @@ -93,16 +94,17 @@ impl Device { &data_size as *const _ as *mut _, &device_name as *const _ as *mut _, ); - if status != kAudioHardwareNoError as i32 { - return format!("", status); - } + check_os_status(status)?; + let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8); if c_string == null() { - return "".into(); + let description = "core foundation unexpectedly returned null string".to_string(); + let err = BackendSpecificError { description }; + return Err(err.into()); } CStr::from_ptr(c_string as *mut _) }; - c_str.to_string_lossy().into_owned() + Ok(c_str.to_string_lossy().into_owned()) } // Logic re-used between `supported_input_formats` and `supported_output_formats`. diff --git a/src/emscripten/mod.rs b/src/emscripten/mod.rs index 3f5067b..1138a39 100644 --- a/src/emscripten/mod.rs +++ b/src/emscripten/mod.rs @@ -10,6 +10,7 @@ use stdweb::web::set_timeout; use BuildStreamError; use DefaultFormatError; +use DeviceNameError; use DevicesError; use Format; use SupportedFormatsError; @@ -229,8 +230,8 @@ pub struct Device; impl Device { #[inline] - pub fn name(&self) -> String { - "Default Device".to_owned() + pub fn name(&self) -> Result { + Ok("Default Device".to_owned()) } #[inline] diff --git a/src/lib.rs b/src/lib.rs index f25f0a6..4f717ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,6 +308,17 @@ pub enum DevicesError { } } +/// An error that may occur while attempting to retrieve a device name. +#[derive(Debug, Fail)] +pub enum DeviceNameError { + /// See the `BackendSpecificError` docs for more information about this error variant. + #[fail(display = "{}", err)] + BackendSpecific { + #[fail(cause)] + err: BackendSpecificError, + } +} + /// Error that can happen when enumerating the list of supported formats. #[derive(Debug, Fail)] pub enum SupportedFormatsError { @@ -410,7 +421,7 @@ pub fn default_output_device() -> Option { impl Device { /// The human-readable name of the device. #[inline] - pub fn name(&self) -> String { + pub fn name(&self) -> Result { self.0.name() } @@ -742,6 +753,12 @@ impl From for DevicesError { } } +impl From for DeviceNameError { + fn from(err: BackendSpecificError) -> Self { + DeviceNameError::BackendSpecific { err } + } +} + impl From for SupportedFormatsError { fn from(err: BackendSpecificError) -> Self { SupportedFormatsError::BackendSpecific { err } diff --git a/src/null/mod.rs b/src/null/mod.rs index e7a5b3c..dc41f77 100644 --- a/src/null/mod.rs +++ b/src/null/mod.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use BuildStreamError; use DefaultFormatError; use DevicesError; +use DeviceNameError; use Format; use SupportedFormatsError; use StreamData; @@ -107,8 +108,8 @@ impl Device { } #[inline] - pub fn name(&self) -> String { - "null".to_owned() + pub fn name(&self) -> Result { + Ok("null".to_owned()) } } diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index 786d80d..ded0722 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -11,6 +11,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use BackendSpecificError; use DefaultFormatError; +use DeviceNameError; use DevicesError; use Format; use SupportedFormatsError; @@ -297,7 +298,7 @@ unsafe impl Sync for Device { } impl Device { - pub fn name(&self) -> String { + pub fn name(&self) -> Result { unsafe { // Open the device's property store. let mut property_store = ptr::null_mut(); @@ -305,15 +306,24 @@ impl Device { // Get the endpoint's friendly-name property. let mut property_value = mem::zeroed(); - check_result( + if let Err(err) = check_result( (*property_store).GetValue( &devpkey::DEVPKEY_Device_FriendlyName as *const _ as *const _, &mut property_value ) - ).expect("failed to get friendly-name from property store"); + ) { + let description = format!("failed to retrieve name from property store: {}", err); + let err = BackendSpecificError { description }; + return Err(err.into()); + } // Read the friendly-name from the union data field, expecting a *const u16. - assert_eq!(property_value.vt, wtypes::VT_LPWSTR as _); + if property_value.vt != wtypes::VT_LPWSTR as _ { + let description = + format!("property store produced invalid data: {:?}", property_value.vt); + let err = BackendSpecificError { description }; + return Err(err.into()); + } let ptr_usize: usize = *(&property_value.data as *const _ as *const usize); let ptr_utf16 = ptr_usize as *const u16; @@ -326,12 +336,15 @@ impl Device { // Create the utf16 slice and covert it into a string. let name_slice = slice::from_raw_parts(ptr_utf16, len as usize); let name_os_string: OsString = OsStringExt::from_wide(name_slice); - let name_string = name_os_string.into_string().unwrap(); + let name_string = match name_os_string.into_string() { + Ok(string) => string, + Err(os_string) => os_string.to_string_lossy().into(), + }; // Clean up the property. PropVariantClear(&mut property_value); - name_string + Ok(name_string) } }