@@ -11,21 +11,20 @@ use std::mem;
1111use std:: os:: windows:: ffi:: OsStringExt ;
1212use std:: ptr;
1313use std:: slice;
14- use std:: sync:: mpsc:: Sender ;
1514use std:: sync:: OnceLock ;
1615use std:: sync:: { Arc , Mutex , MutexGuard } ;
1716use std:: time:: Duration ;
1817
1918use super :: com;
2019use super :: { windows_err_to_cpal_err, windows_err_to_cpal_err_message} ;
20+ use windows:: core:: Interface ;
2121use windows:: core:: GUID ;
22- use windows:: core:: { implement, IUnknown , Interface , HRESULT , PCWSTR } ;
2322use windows:: Win32 :: Devices :: Properties ;
2423use windows:: Win32 :: Foundation ;
2524use windows:: Win32 :: Media :: Audio :: IAudioRenderClient ;
2625use windows:: Win32 :: Media :: { Audio , KernelStreaming , Multimedia } ;
2726use windows:: Win32 :: System :: Com ;
28- use windows:: Win32 :: System :: Com :: { CoTaskMemFree , StringFromIID , StructuredStorage , STGM_READ } ;
27+ use windows:: Win32 :: System :: Com :: { StructuredStorage , STGM_READ } ;
2928use windows:: Win32 :: System :: Threading ;
3029use windows:: Win32 :: System :: Variant :: VT_LPWSTR ;
3130
@@ -274,53 +273,50 @@ unsafe fn format_from_waveformatex_ptr(
274273 Some ( format)
275274}
276275
277- #[ implement( Audio :: IActivateAudioInterfaceCompletionHandler ) ]
278- struct CompletionHandler ( Sender < windows:: core:: Result < IUnknown > > ) ;
276+ #[ cfg( feature = "wasapi-virtual-default-devices" ) ]
277+ unsafe fn activate_audio_interface_sync (
278+ deviceinterfacepath : windows:: core:: PWSTR ,
279+ ) -> windows:: core:: Result < Audio :: IAudioClient > {
280+ use windows:: core:: IUnknown ;
279281
280- fn retrieve_result (
281- operation : & Audio :: IActivateAudioInterfaceAsyncOperation ,
282- ) -> windows:: core:: Result < IUnknown > {
283- let mut result = HRESULT :: default ( ) ;
284- let mut interface: Option < IUnknown > = None ;
285- unsafe {
286- operation. GetActivateResult ( & mut result, & mut interface) ?;
282+ #[ windows:: core:: implement( Audio :: IActivateAudioInterfaceCompletionHandler ) ]
283+ struct CompletionHandler ( std:: sync:: mpsc:: Sender < windows:: core:: Result < IUnknown > > ) ;
284+
285+ fn retrieve_result (
286+ operation : & Audio :: IActivateAudioInterfaceAsyncOperation ,
287+ ) -> windows:: core:: Result < IUnknown > {
288+ let mut result = windows:: core:: HRESULT :: default ( ) ;
289+ let mut interface: Option < IUnknown > = None ;
290+ unsafe {
291+ operation. GetActivateResult ( & mut result, & mut interface) ?;
292+ }
293+ result. ok ( ) ?;
294+ interface. ok_or_else ( || {
295+ windows:: core:: Error :: new (
296+ Audio :: AUDCLNT_E_DEVICE_INVALIDATED ,
297+ "audio interface could not be retrieved during activation" ,
298+ )
299+ } )
287300 }
288- result. ok ( ) ?;
289- interface. ok_or_else ( || {
290- windows:: core:: Error :: new (
291- Audio :: AUDCLNT_E_DEVICE_INVALIDATED ,
292- "audio interface could not be retrieved during activation" ,
293- )
294- } )
295- }
296301
297- impl Audio :: IActivateAudioInterfaceCompletionHandler_Impl for CompletionHandler_Impl {
298- fn ActivateCompleted (
299- & self ,
300- operation : windows:: core:: Ref < Audio :: IActivateAudioInterfaceAsyncOperation > ,
301- ) -> windows:: core:: Result < ( ) > {
302- let result = operation. ok ( ) . and_then ( retrieve_result) ;
303- let _ = self . 0 . send ( result) ;
304- Ok ( ( ) )
302+ impl Audio :: IActivateAudioInterfaceCompletionHandler_Impl for CompletionHandler_Impl {
303+ fn ActivateCompleted (
304+ & self ,
305+ operation : windows:: core:: Ref < Audio :: IActivateAudioInterfaceAsyncOperation > ,
306+ ) -> windows:: core:: Result < ( ) > {
307+ let result = operation. ok ( ) . and_then ( retrieve_result) ;
308+ let _ = self . 0 . send ( result) ;
309+ Ok ( ( ) )
310+ }
305311 }
306- }
307312
308- #[ allow( non_snake_case) ]
309- unsafe fn ActivateAudioInterfaceSync < P0 , T > (
310- deviceinterfacepath : P0 ,
311- activationparams : Option < * const StructuredStorage :: PROPVARIANT > ,
312- ) -> windows:: core:: Result < T >
313- where
314- P0 : windows:: core:: Param < PCWSTR > ,
315- T : Interface ,
316- {
317313 let ( sender, receiver) = std:: sync:: mpsc:: channel ( ) ;
318314 let completion: Audio :: IActivateAudioInterfaceCompletionHandler =
319315 CompletionHandler ( sender) . into ( ) ;
320316 Audio :: ActivateAudioInterfaceAsync (
321317 deviceinterfacepath,
322- & T :: IID ,
323- activationparams ,
318+ & Audio :: IAudioClient :: IID ,
319+ None ,
324320 & completion,
325321 ) ?;
326322 let result = receiver. recv_timeout ( Duration :: from_secs ( 2 ) ) . unwrap ( ) ?;
@@ -439,18 +435,25 @@ impl Device {
439435 return Ok ( lock) ;
440436 }
441437
438+ // When using virtual default devices, we use `ActivateAudioInterfaceAsync` to get
439+ // an `IAudioClient` for the default output or input device. When retrieved this way,
440+ // streams will be automatically rerouted if the default device is changed.
441+ //
442+ // Otherwise, we use `Activate` to get an `IAudioClient` for the current device.
443+
444+ #[ cfg( feature = "wasapi-virtual-default-devices" ) ]
442445 let audio_client: Audio :: IAudioClient = unsafe {
443446 match & self . device {
444447 DeviceType :: DefaultOutput => {
445- let default_audio = StringFromIID ( & Audio :: DEVINTERFACE_AUDIO_RENDER ) ?;
446- let result = ActivateAudioInterfaceSync ( PCWSTR ( default_audio. as_ptr ( ) ) , None ) ;
447- CoTaskMemFree ( Some ( default_audio. as_ptr ( ) as _ ) ) ;
448+ let default_audio = Com :: StringFromIID ( & Audio :: DEVINTERFACE_AUDIO_RENDER ) ?;
449+ let result = activate_audio_interface_sync ( default_audio) ;
450+ Com :: CoTaskMemFree ( Some ( default_audio. as_ptr ( ) as _ ) ) ;
448451 result?
449452 }
450453 DeviceType :: DefaultInput => {
451- let default_audio = StringFromIID ( & Audio :: DEVINTERFACE_AUDIO_CAPTURE ) ?;
452- let result = ActivateAudioInterfaceSync ( PCWSTR ( default_audio. as_ptr ( ) ) , None ) ;
453- CoTaskMemFree ( Some ( default_audio. as_ptr ( ) as _ ) ) ;
454+ let default_audio = Com :: StringFromIID ( & Audio :: DEVINTERFACE_AUDIO_CAPTURE ) ?;
455+ let result = activate_audio_interface_sync ( default_audio) ;
456+ Com :: CoTaskMemFree ( Some ( default_audio. as_ptr ( ) as _ ) ) ;
454457 result?
455458 }
456459 DeviceType :: Specific ( device) => {
@@ -461,6 +464,16 @@ impl Device {
461464 }
462465 } ;
463466
467+ #[ cfg( not( feature = "wasapi-virtual-default-devices" ) ) ]
468+ let audio_client = unsafe {
469+ self . immdevice ( )
470+ . ok_or ( windows:: core:: Error :: new (
471+ Audio :: AUDCLNT_E_DEVICE_INVALIDATED ,
472+ "device not found while getting audio client" ,
473+ ) ) ?
474+ . Activate ( Com :: CLSCTX_ALL , None ) ?
475+ } ;
476+
464477 * lock = Some ( IAudioClientWrapper ( audio_client) ) ;
465478 Ok ( lock)
466479 }
0 commit comments