diff --git a/src/wasapi/device.rs b/src/wasapi/device.rs index 7331381..3ba7dd2 100644 --- a/src/wasapi/device.rs +++ b/src/wasapi/device.rs @@ -20,6 +20,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::{ @@ -31,6 +32,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, @@ -532,7 +536,41 @@ impl Device { impl PartialEq for Device { #[inline] fn eq(&self, other: &Device) -> bool { - self.device == other.device + // 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 + 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); + // 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; + } + } } } diff --git a/src/wasapi/stream.rs b/src/wasapi/stream.rs index 2d7f9cd..8e62589 100644 --- a/src/wasapi/stream.rs +++ b/src/wasapi/stream.rs @@ -500,6 +500,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 };