From 513071b60adee710486b31e74be38b0b0f761abc Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 14 Jul 2018 15:41:30 +0200 Subject: [PATCH 1/3] Don't panic when an audio device is disconnected. --- src/wasapi/stream.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wasapi/stream.rs b/src/wasapi/stream.rs index 1b30c97..0f19157 100644 --- a/src/wasapi/stream.rs +++ b/src/wasapi/stream.rs @@ -494,6 +494,13 @@ impl EventLoop { let mut frames_available = { let mut padding = mem::uninitialized(); let hresult = (*stream.audio_client).GetCurrentPadding(&mut padding); + // Happens when a bluetooth headset was turned off, for example. + if hresult == AUDCLNT_E_DEVICE_INVALIDATED { + // The client code should switch to a different device eventually. + // For now let's just skip the invalidated device. + // Would be nice to inform the client code about the invalidation, + // but throwing a panic isn't the most ergonomic way to do so. + continue} check_result(hresult).unwrap(); stream.max_frames_in_buffer - padding }; From e959c770e2fb377cba769093d3f68b6611481975 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 14 Jul 2018 15:42:48 +0200 Subject: [PATCH 2/3] Compare the Windows devices by ID, allowing the user to detect if the default device has been changed. --- src/wasapi/device.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index 0ef90eb..ae5b43b 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -19,6 +19,7 @@ use COMMON_SAMPLE_RATES; use super::check_result; use super::com; use super::winapi::Interface; +use super::winapi::ctypes::c_void; use super::winapi::shared::devpkey; use super::winapi::shared::ksmedia; use super::winapi::shared::guiddef::{ @@ -30,6 +31,9 @@ use super::winapi::shared::minwindef::{ }; use super::winapi::shared::mmreg; use super::winapi::shared::wtypes; +// https://msdn.microsoft.com/en-us/library/cc230355.aspx +use super::winapi::um::winnt::LPWSTR; +use super::winapi::um::winnt::WCHAR; use super::winapi::um::coml2api; use super::winapi::um::audioclient::{ IAudioClient, @@ -531,6 +535,45 @@ impl Device { impl PartialEq for Device { #[inline] fn eq(&self, other: &Device) -> bool { + // Use case: In oder to check whether the default device has changed + // the client code might need to compare the previous default device with the current one. + // The pointer comparison (`self.device == other.device`) don't work there, + // because the pointers are different even when the default device stays the same. + // + // In this code section we're trying to use the GetId method for the device comparison, cf. + // https://docs.microsoft.com/en-us/windows/desktop/api/mmdeviceapi/nf-mmdeviceapi-immdevice-getid + // + // If `GetId` mysteriously fails then we fall back to pointer comparison + // (we could panic, treating the `GetId` failure as something unexpected, + // but I simply don't know how reliable the `GetId` on Windows is). + unsafe { + struct IdRAII (LPWSTR); + /// RAII for device IDs. + impl Drop for IdRAII { + fn drop(&mut self) { + unsafe {CoTaskMemFree(self.0 as *mut c_void)} + } + } + let mut id1: LPWSTR = ptr::null_mut(); + let rc1 = (*self.device).GetId(&mut id1); + if rc1 == winerror::S_OK { + let id1 = IdRAII(id1); + let mut id2: LPWSTR = ptr::null_mut(); + let rc2 = (*other.device).GetId(&mut id2); + if rc2 == winerror::S_OK { + let id2 = IdRAII(id2); + // 16-bit null-terminated comparison. + let mut offset = 0; + loop { + let w1: WCHAR = *id1.0.offset(offset); + let w2: WCHAR = *id2.0.offset(offset); + if w1 == 0 && w2 == 0 {return true} + if w1 != w2 {return false} + offset += 1; + } + } + } + } self.device == other.device } } From 71a5a43e3367e0eeb36d1cbcba09d0cb719a1000 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 14 Jul 2018 16:22:56 +0200 Subject: [PATCH 3/3] Come to think of it, changing comparison logic unexpectedly is a bad idea. --- src/wasapi/device.rs | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index ae5b43b..6616968 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -542,10 +542,6 @@ impl PartialEq for Device { // // In this code section we're trying to use the GetId method for the device comparison, cf. // https://docs.microsoft.com/en-us/windows/desktop/api/mmdeviceapi/nf-mmdeviceapi-immdevice-getid - // - // If `GetId` mysteriously fails then we fall back to pointer comparison - // (we could panic, treating the `GetId` failure as something unexpected, - // but I simply don't know how reliable the `GetId` on Windows is). unsafe { struct IdRAII (LPWSTR); /// RAII for device IDs. @@ -556,25 +552,24 @@ impl PartialEq for Device { } let mut id1: LPWSTR = ptr::null_mut(); let rc1 = (*self.device).GetId(&mut id1); - if rc1 == winerror::S_OK { - let id1 = IdRAII(id1); - let mut id2: LPWSTR = ptr::null_mut(); - let rc2 = (*other.device).GetId(&mut id2); - if rc2 == winerror::S_OK { - let id2 = IdRAII(id2); - // 16-bit null-terminated comparison. - let mut offset = 0; - loop { - let w1: WCHAR = *id1.0.offset(offset); - let w2: WCHAR = *id2.0.offset(offset); - if w1 == 0 && w2 == 0 {return true} - if w1 != w2 {return false} - offset += 1; - } - } + // GetId only fails with E_OUTOFMEMORY and if it does, we're probably dead already. + // Plus it won't do to change the device comparison logic unexpectedly. + if rc1 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)} + let id1 = IdRAII(id1); + let mut id2: LPWSTR = ptr::null_mut(); + let rc2 = (*other.device).GetId(&mut id2); + if rc2 != winerror::S_OK {panic! ("cpal: GetId failure: {}", rc1)} + let id2 = IdRAII(id2); + // 16-bit null-terminated comparison. + let mut offset = 0; + loop { + let w1: WCHAR = *id1.0.offset(offset); + let w2: WCHAR = *id2.0.offset(offset); + if w1 == 0 && w2 == 0 {return true} + if w1 != w2 {return false} + offset += 1; } } - self.device == other.device } }