Atlas - SDL_wasapi_winrt.cpp

Home / ext / SDL2 / src / audio / wasapi Lines: 1 | Size: 10090 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "../../SDL_internal.h" 23 24// This is C++/CX code that the WinRT port uses to talk to WASAPI-related 25// system APIs. The C implementation of these functions, for non-WinRT apps, 26// is in SDL_wasapi_win32.c. The code in SDL_wasapi.c is used by both standard 27// Windows and WinRT builds to deal with audio and calls into these functions. 28 29#if SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__) 30 31#include <Windows.h> 32#include <windows.ui.core.h> 33#include <windows.devices.enumeration.h> 34#include <windows.media.devices.h> 35#include <wrl/implements.h> 36 37extern "C" { 38#include "../../core/windows/SDL_windows.h" 39#include "SDL_audio.h" 40#include "SDL_timer.h" 41#include "../SDL_audio_c.h" 42#include "../SDL_sysaudio.h" 43#include "SDL_assert.h" 44#include "SDL_log.h" 45} 46 47#define COBJMACROS 48#include <mmdeviceapi.h> 49#include <audioclient.h> 50 51#include "SDL_wasapi.h" 52 53using namespace Windows::Devices::Enumeration; 54using namespace Windows::Media::Devices; 55using namespace Windows::Foundation; 56using namespace Microsoft::WRL; 57 58class SDL_WasapiDeviceEventHandler 59{ 60public: 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 OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args); 67 void OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args); 68 69private: 70 const SDL_bool iscapture; 71 DeviceWatcher^ watcher; 72 Windows::Foundation::EventRegistrationToken added_handler; 73 Windows::Foundation::EventRegistrationToken removed_handler; 74 Windows::Foundation::EventRegistrationToken updated_handler; 75 Windows::Foundation::EventRegistrationToken default_changed_handler; 76}; 77 78SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture) 79 : iscapture(_iscapture) 80 , watcher(DeviceInformation::CreateWatcher(_iscapture ? DeviceClass::AudioCapture : DeviceClass::AudioRender)) 81{ 82 if (!watcher) 83 return; // uhoh. 84 85 // !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan. 86 added_handler = watcher->Added += ref new TypedEventHandler<DeviceWatcher^, DeviceInformation^>([this](DeviceWatcher^ sender, DeviceInformation^ args) { OnDeviceAdded(sender, args); } ); 87 removed_handler = watcher->Removed += ref new TypedEventHandler<DeviceWatcher^, DeviceInformationUpdate^>([this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceRemoved(sender, args); } ); 88 updated_handler = watcher->Updated += ref new TypedEventHandler<DeviceWatcher^, DeviceInformationUpdate^>([this](DeviceWatcher^ sender, DeviceInformationUpdate^ args) { OnDeviceUpdated(sender, args); } ); 89 if (iscapture) { 90 default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged += ref new TypedEventHandler<Platform::Object^, DefaultAudioCaptureDeviceChangedEventArgs^>([this](Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args) { OnDefaultCaptureDeviceChanged(sender, args); } ); 91 } else { 92 default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged += ref new TypedEventHandler<Platform::Object^, DefaultAudioRenderDeviceChangedEventArgs^>([this](Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args) { OnDefaultRenderDeviceChanged(sender, args); } ); 93 } 94 watcher->Start(); 95} 96 97SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler() 98{ 99 if (watcher) { 100 watcher->Added -= added_handler; 101 watcher->Removed -= removed_handler; 102 watcher->Updated -= updated_handler; 103 watcher->Stop(); 104 watcher = nullptr; 105 } 106 107 if (iscapture) { 108 MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler; 109 } else { 110 MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler; 111 } 112} 113 114void 115SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher^ sender, DeviceInformation^ info) 116{ 117 SDL_assert(sender == this->watcher); 118 char *utf8dev = WIN_StringToUTF8(info->Name->Data()); 119 if (utf8dev) { 120 WASAPI_AddDevice(this->iscapture, utf8dev, info->Id->Data()); 121 SDL_free(utf8dev); 122 } 123} 124 125void 126SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher^ sender, DeviceInformationUpdate^ info) 127{ 128 SDL_assert(sender == this->watcher); 129 WASAPI_RemoveDevice(this->iscapture, info->Id->Data()); 130} 131 132void 133SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher^ sender, DeviceInformationUpdate^ args) 134{ 135 SDL_assert(sender == this->watcher); 136} 137 138void 139SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object^ sender, DefaultAudioRenderDeviceChangedEventArgs^ args) 140{ 141 SDL_assert(this->iscapture); 142 SDL_AtomicAdd(&WASAPI_DefaultPlaybackGeneration, 1); 143} 144 145void 146SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object^ sender, DefaultAudioCaptureDeviceChangedEventArgs^ args) 147{ 148 SDL_assert(!this->iscapture); 149 SDL_AtomicAdd(&WASAPI_DefaultCaptureGeneration, 1); 150} 151 152 153static SDL_WasapiDeviceEventHandler *playback_device_event_handler; 154static SDL_WasapiDeviceEventHandler *capture_device_event_handler; 155 156int WASAPI_PlatformInit(void) 157{ 158 return 0; 159} 160 161void WASAPI_PlatformDeinit(void) 162{ 163 delete playback_device_event_handler; 164 playback_device_event_handler = nullptr; 165 delete capture_device_event_handler; 166 capture_device_event_handler = nullptr; 167} 168 169void WASAPI_EnumerateEndpoints(void) 170{ 171 // DeviceWatchers will fire an Added event for each existing device at 172 // startup, so we don't need to enumerate them separately before 173 // listening for updates. 174 playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE); 175 capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE); 176} 177 178struct SDL_WasapiActivationHandler : public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > 179{ 180 SDL_WasapiActivationHandler() : device(nullptr) {} 181 STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation); 182 SDL_AudioDevice *device; 183}; 184 185HRESULT 186SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async) 187{ 188 HRESULT result = S_OK; 189 IUnknown *iunknown = nullptr; 190 const HRESULT ret = async->GetActivateResult(&result, &iunknown); 191 192 if (SUCCEEDED(ret) && SUCCEEDED(result)) { 193 iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client)); 194 if (device->hidden->client) { 195 // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races. 196 SDL_AtomicSet(&device->hidden->just_activated, 1); 197 } 198 } 199 200 WASAPI_UnrefDevice(device); 201 202 return S_OK; 203} 204 205void 206WASAPI_PlatformDeleteActivationHandler(void *handler) 207{ 208 ((SDL_WasapiActivationHandler *) handler)->Release(); 209} 210 211int 212WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery) 213{ 214 LPCWSTR devid = _this->hidden->devid; 215 Platform::String^ defdevid; 216 217 if (devid == nullptr) { 218 defdevid = _this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default); 219 if (defdevid) { 220 devid = defdevid->Data(); 221 } 222 } 223 224 SDL_AtomicSet(&_this->hidden->just_activated, 0); 225 226 ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>(); 227 if (handler == nullptr) { 228 return SDL_SetError("Failed to allocate WASAPI activation handler"); 229 } 230 231 handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc. 232 handler.Get()->device = _this; 233 _this->hidden->activation_handler = handler.Get(); 234 235 WASAPI_RefDevice(_this); /* completion handler will unref it. */ 236 IActivateAudioInterfaceAsyncOperation *async = nullptr; 237 const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async); 238 239 if (async != nullptr) { 240 async->Release(); 241 } 242 243 if (FAILED(ret)) { 244 handler.Get()->Release(); 245 WASAPI_UnrefDevice(_this); 246 return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret); 247 } 248 249 return 0; 250} 251 252void 253WASAPI_BeginLoopIteration(_THIS) 254{ 255 if (SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) { 256 if (WASAPI_PrepDevice(_this, SDL_TRUE) == -1) { 257 SDL_OpenedAudioDeviceDisconnected(_this); 258 } 259 } 260} 261 262void 263WASAPI_PlatformThreadInit(_THIS) 264{ 265 // !!! FIXME: set this thread to "Pro Audio" priority. 266} 267 268void 269WASAPI_PlatformThreadDeinit(_THIS) 270{ 271 // !!! FIXME: set this thread to "Pro Audio" priority. 272} 273 274#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__) 275 276/* vi: set ts=4 sw=4 expandtab: */ 277
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.