WASAPI: Use ActivateAudioInterfaceAsync
with virtual device IDs for default devices
#1027
+206
−59
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #740 (mostly). Updated version of #754 that aims to address the issues mentioned in that PR.
Background
When you currently play some audio on the default device using
cpal
, the default device is fetched once, and then a stream is created from that device to play audio. However, changing the default device will lead to that stream continuing to play on the original device, and removing the device entirely (e.g. unplugging headphones) will result in the stream dying as it no longer has a device to output to.I spent several hours trying to fix this properly - that is, trying to update the device and rebuild the stream when it changes - and stopped when I realised that a significant amount of the WASAPI stream code would need to be revised to handle a changing device. For those curious, you can see my experimental work here. The goal was to replicate the flow of this Chromium code, which implements this logic.
The Imperfect Fix
However, you don't actually need to do any of that if you don't care about supporting <Windows 8. Windows 8 introduced
ActivateAudioInterfaceAsync
, and with it, virtual device interfaces.ActivateAudioInterfaceAsync
can be used instead ofAudio::IMMDevice::Activate
to generate aAudio::IAudioClient
that will automatically reroute audio for you if the device changes.This is what the previous PR did, and it works great. On top of that PR, this PR:
name
use the current default deviceRamifications
I've added a default-on feature,
wasapi-virtual-default-devices
, that adds support for this. Users that need to support older versions of Windows can turn this feature off, and it will behave as the current status quo does. (I'm not actually sure what happens if you have this feature on for an older Windows: in an ideal world, it does nothing, and newer Windows users can enjoy virtual devices. Please let me know if you've tested this.)One downside of this is that I had to bump the minimum
windows
version to 0.61. This is because 0.61 removed the previously-explicitly-requiredimplement
feature in 0.61, and I can't see any way to conditionally include that feature for older versions ofwindows
.cargo
will refuse to resolve thewindows
dependency with a feature that doesn't exist for the version it's resolving to. I'm happy to drop it back down if anyone can think of a workaround for this.