Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge SDL_wasapi_win32 into SDL_wasapi #11431

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion VisualC-GDK/SDL/SDL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,6 @@
<ClCompile Include="..\..\src\audio\SDL_mixer.c" />
<ClCompile Include="..\..\src\audio\SDL_wave.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
<ClCompile Include="..\..\src\core\SDL_core_unsupported.c" />
<ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
<ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
Expand Down
1 change: 0 additions & 1 deletion VisualC-GDK/SDL/SDL.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
<ClCompile Include="..\..\src\audio\SDL_mixer.c" />
<ClCompile Include="..\..\src\audio\SDL_wave.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
<ClCompile Include="..\..\src\core\SDL_core_unsupported.c" />
<ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
<ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
Expand Down
1 change: 0 additions & 1 deletion VisualC/SDL/SDL.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,6 @@
<ClCompile Include="..\..\src\audio\SDL_mixer.c" />
<ClCompile Include="..\..\src\audio\SDL_wave.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c" />
<ClCompile Include="..\..\src\core\SDL_core_unsupported.c" />
<ClCompile Include="..\..\src\core\windows\SDL_hid.c" />
<ClCompile Include="..\..\src\core\windows\SDL_immdevice.c" />
Expand Down
3 changes: 0 additions & 3 deletions VisualC/SDL/SDL.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -1178,9 +1178,6 @@
<ClCompile Include="..\..\src\audio\dummy\SDL_dummyaudio.c">
<Filter>audio\dummy</Filter>
</ClCompile>
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi_win32.c">
<Filter>audio\wasapi</Filter>
</ClCompile>
<ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c">
<Filter>audio\wasapi</Filter>
</ClCompile>
Expand Down
141 changes: 130 additions & 11 deletions src/audio/wasapi/SDL_wasapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,22 @@
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
#endif

// handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency).
static HMODULE libavrt = NULL;
typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD);
typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL;
static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;

// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
#ifdef __IAudioClient3_INTERFACE_DEFINED__
static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } };
#endif //

static bool immdevice_initialized = false;

// WASAPI is _really_ particular about various things happening on the same thread, for COM and such,
// so we proxy various stuff to a single background thread to manage.
Expand Down Expand Up @@ -155,23 +164,91 @@ bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, b
return true; // successfully added (and possibly executed)!
}

static bool mgmtthrtask_AudioDeviceDisconnected(void *userdata)
{
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
SDL_AudioDeviceDisconnected(device);
UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes.
return true;
}

static void AudioDeviceDisconnected(SDL_AudioDevice *device)
{
// don't wait on this, IMMDevice's own thread needs to return or everything will deadlock.
if (device) {
RefPhysicalAudioDevice(device); // make sure this lives until the task completes.
WASAPI_ProxyToManagementThread(mgmtthrtask_AudioDeviceDisconnected, device, NULL);
}
}

static bool mgmtthrtask_DefaultAudioDeviceChanged(void *userdata)
{
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
SDL_DefaultAudioDeviceChanged(device);
UnrefPhysicalAudioDevice(device); // make sure this lived until the task completes.
return true;
}

static void DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
{
// don't wait on this, IMMDevice's own thread needs to return or everything will deadlock.
if (new_default_device) {
RefPhysicalAudioDevice(new_default_device); // make sure this lives until the task completes.
WASAPI_ProxyToManagementThread(mgmtthrtask_DefaultAudioDeviceChanged, new_default_device, NULL);
}
}

static void StopWasapiHotplug(void)
{
if (immdevice_initialized) {
SDL_IMMDevice_Quit();
immdevice_initialized = false;
}
}

static void Deinit(void)
{
if (libavrt) {
FreeLibrary(libavrt);
libavrt = NULL;
}

pAvSetMmThreadCharacteristicsW = NULL;
pAvRevertMmThreadCharacteristics = NULL;

StopWasapiHotplug();

WIN_CoUninitialize();
}

static bool ManagementThreadPrepare(void)
{
if (!WASAPI_PlatformInit()) {
return false;
const SDL_IMMDevice_callbacks callbacks = { AudioDeviceDisconnected, DefaultAudioDeviceChanged };
if (FAILED(WIN_CoInitialize())) {
return SDL_SetError("CoInitialize() failed");
} else if (!SDL_IMMDevice_Init(&callbacks)) {
return false; // Error string is set by SDL_IMMDevice_Init
}

immdevice_initialized = true;

libavrt = LoadLibrary(TEXT("avrt.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now!
if (libavrt) {
pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW");
pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics");
}

ManagementThreadLock = SDL_CreateMutex();
if (!ManagementThreadLock) {
WASAPI_PlatformDeinit();
Deinit();
return false;
}

ManagementThreadCondition = SDL_CreateCondition();
if (!ManagementThreadCondition) {
SDL_DestroyMutex(ManagementThreadLock);
ManagementThreadLock = NULL;
WASAPI_PlatformDeinit();
Deinit();
return false;
}

Expand All @@ -197,7 +274,7 @@ static int ManagementThreadEntry(void *userdata)
SDL_SignalSemaphore(data->ready_sem); // unblock calling thread.
ManagementThreadMainloop();

WASAPI_PlatformDeinit();
Deinit();
return 0;
}

Expand Down Expand Up @@ -260,7 +337,7 @@ typedef struct
static bool mgmtthrtask_DetectDevices(void *userdata)
{
mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata;
WASAPI_EnumerateEndpoints(data->default_playback, data->default_recording);
SDL_IMMDevice_EnumerateEndpoints(data->default_playback, data->default_recording);
return true;
}

Expand Down Expand Up @@ -373,7 +450,29 @@ static void ResetWasapiDevice(SDL_AudioDevice *device)

static bool mgmtthrtask_ActivateDevice(void *userdata)
{
return WASAPI_ActivateDevice((SDL_AudioDevice *)userdata);
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;

IMMDevice *immdevice = NULL;
if (!SDL_IMMDevice_Get(device, &immdevice, device->recording)) {
device->hidden->client = NULL;
return false; // This is already set by SDL_IMMDevice_Get
}

// this is _not_ async in standard win32, yay!
HRESULT ret = IMMDevice_Activate(immdevice, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&device->hidden->client);
IMMDevice_Release(immdevice);

if (FAILED(ret)) {
SDL_assert(device->hidden->client == NULL);
return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
}

SDL_assert(device->hidden->client != NULL);
if (!WASAPI_PrepDevice(device)) { // not async, fire it right away.
return false;
}

return true; // good to go.
}

static bool ActivateWasapiDevice(SDL_AudioDevice *device)
Expand Down Expand Up @@ -773,17 +872,37 @@ static bool WASAPI_OpenDevice(SDL_AudioDevice *device)

static void WASAPI_ThreadInit(SDL_AudioDevice *device)
{
WASAPI_PlatformThreadInit(device);
// this thread uses COM.
if (SUCCEEDED(WIN_CoInitialize())) { // can't report errors, hope it worked!
device->hidden->coinitialized = true;
}

// Set this thread to very high "Pro Audio" priority.
if (pAvSetMmThreadCharacteristicsW) {
DWORD idx = 0;
device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
} else {
SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL);
}
}

static void WASAPI_ThreadDeinit(SDL_AudioDevice *device)
{
WASAPI_PlatformThreadDeinit(device);
// Set this thread back to normal priority.
if (device->hidden->task && pAvRevertMmThreadCharacteristics) {
pAvRevertMmThreadCharacteristics(device->hidden->task);
device->hidden->task = NULL;
}

if (device->hidden->coinitialized) {
WIN_CoUninitialize();
device->hidden->coinitialized = false;
}
}

static bool mgmtthrtask_FreeDeviceHandle(void *userdata)
{
WASAPI_PlatformFreeDeviceHandle((SDL_AudioDevice *)userdata);
SDL_IMMDevice_FreeDeviceHandle((SDL_AudioDevice *) userdata);
return true;
}

Expand All @@ -795,7 +914,7 @@ static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device)

static bool mgmtthrtask_DeinitializeStart(void *userdata)
{
WASAPI_PlatformDeinitializeStart();
StopWasapiHotplug();
return true;
}

Expand Down
11 changes: 0 additions & 11 deletions src/audio/wasapi/SDL_wasapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,6 @@ void WASAPI_DisconnectDevice(SDL_AudioDevice *device); // don't hold the device
typedef bool (*ManagementThreadTask)(void *userdata);
bool WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, bool *wait_until_complete);

// These are functions that are (were...?) implemented differently for various Windows versions.
// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD.
bool WASAPI_PlatformInit(void);
void WASAPI_PlatformDeinit(void);
void WASAPI_PlatformDeinitializeStart(void);
void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
bool WASAPI_ActivateDevice(SDL_AudioDevice *device);
void WASAPI_PlatformThreadInit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device); // this happens on the audio device thread, not the management thread.
void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device);

#ifdef __cplusplus
}
#endif
Expand Down
Loading
Loading