22 #include "../../SDL_internal.h"
29 #if SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
32 #include <windows.ui.core.h>
33 #include <windows.devices.enumeration.h>
34 #include <windows.media.devices.h>
35 #include <wrl/implements.h>
38 #include "../../core/windows/SDL_windows.h"
41 #include "../SDL_audio_c.h"
42 #include "../SDL_sysaudio.h"
48 #include <mmdeviceapi.h>
49 #include <audioclient.h>
53 using namespace Windows::Devices::Enumeration;
54 using namespace Windows::Media::Devices;
55 using namespace Windows::Foundation;
56 using namespace Microsoft::WRL;
58 class SDL_WasapiDeviceEventHandler
61 SDL_WasapiDeviceEventHandler(
const SDL_bool _iscapture);
62 ~SDL_WasapiDeviceEventHandler();
63 void OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ args);
64 void OnDeviceRemoved(DeviceWatcher^ sender, DeviceInformationUpdate^ args);
65 void OnDeviceUpdated(DeviceWatcher^ sender, DeviceInformationUpdate^ args);
66 void OnEnumerationCompleted(DeviceWatcher^ sender, Platform::Object^ args);
67 void OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args);
68 void OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args);
73 DeviceWatcher^ watcher;
74 Windows::Foundation::EventRegistrationToken added_handler;
75 Windows::Foundation::EventRegistrationToken removed_handler;
76 Windows::Foundation::EventRegistrationToken updated_handler;
77 Windows::Foundation::EventRegistrationToken completed_handler;
78 Windows::Foundation::EventRegistrationToken default_changed_handler;
81 SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(
const SDL_bool _iscapture)
82 : iscapture(_iscapture)
84 , watcher(DeviceInformation::CreateWatcher(_iscapture ? DeviceClass::AudioCapture : DeviceClass::AudioRender))
86 if (!watcher || !completed)
90 added_handler = watcher->Added +=
ref new TypedEventHandler<DeviceWatcher^, DeviceInformation^>([
this](DeviceWatcher^ sender, DeviceInformation^ args) { OnDeviceAdded(sender, args); } );
91 removed_handler = watcher->Removed +=
ref new TypedEventHandler<DeviceWatcher^, DeviceInformationUpdate^>([
this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceRemoved(sender, args); } );
92 updated_handler = watcher->Updated +=
ref new TypedEventHandler<DeviceWatcher^, DeviceInformationUpdate^>([
this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceUpdated(sender, args); } );
93 completed_handler = watcher->EnumerationCompleted +=
ref new TypedEventHandler<DeviceWatcher^, Platform::Object^>([
this](DeviceWatcher^ sender, Platform::Object^ args) { OnEnumerationCompleted(sender, args); } );
95 default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged +=
ref new TypedEventHandler<Platform::Object^, DefaultAudioCaptureDeviceChangedEventArgs^>([
this](Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args) { OnDefaultCaptureDeviceChanged(sender, args); } );
97 default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged +=
ref new TypedEventHandler<Platform::Object^, DefaultAudioRenderDeviceChangedEventArgs^>([
this](Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args) { OnDefaultRenderDeviceChanged(sender, args); } );
102 SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
105 watcher->Added -= added_handler;
106 watcher->Removed -= removed_handler;
107 watcher->Updated -= updated_handler;
108 watcher->EnumerationCompleted -= completed_handler;
118 MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler;
120 MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler;
125 SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ info)
136 SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher^ sender, DeviceInformationUpdate^ info)
143 SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher^ sender, DeviceInformationUpdate^ args)
149 SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher^ sender, Platform::Object^ args)
156 SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args)
163 SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args)
170 static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
171 static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
180 delete playback_device_event_handler;
181 playback_device_event_handler =
nullptr;
182 delete capture_device_event_handler;
183 capture_device_event_handler =
nullptr;
191 playback_device_event_handler =
new SDL_WasapiDeviceEventHandler(
SDL_FALSE);
192 capture_device_event_handler =
new SDL_WasapiDeviceEventHandler(
SDL_TRUE);
193 SDL_SemWait(playback_device_event_handler->completed);
194 SDL_SemWait(capture_device_event_handler->completed);
197 struct SDL_WasapiActivationHandler :
public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler >
199 SDL_WasapiActivationHandler() :
device(nullptr) {}
200 STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
205 SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
216 ((SDL_WasapiActivationHandler *) handler)->Release();
222 LPCWSTR devid =
_this->hidden->devid;
223 Platform::String^ defdevid;
225 if (devid ==
nullptr) {
226 defdevid =
_this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
228 devid = defdevid->Data();
234 ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
235 if (handler ==
nullptr) {
236 return SDL_SetError(
"Failed to allocate WASAPI activation handler");
239 handler.Get()->AddRef();
240 handler.Get()->device =
_this;
241 _this->hidden->activation_handler = handler.Get();
244 IActivateAudioInterfaceAsyncOperation *async =
nullptr;
245 const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient),
nullptr, handler.Get(), &async);
247 if (
FAILED(ret) || async ==
nullptr) {
248 if (async !=
nullptr) {
251 handler.Get()->Release();
268 HRESULT activateRes =
S_OK;
269 IUnknown *iunknown =
nullptr;
270 const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
272 if (
FAILED(getActivateRes)) {
274 }
else if (
FAILED(activateRes)) {
278 iunknown->QueryInterface(IID_PPV_ARGS(&
_this->hidden->client));
279 if (!
_this->hidden->client) {
280 return SDL_SetError(
"Failed to query WASAPI client interface");
302 #endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)