From bf03728873547aa82a19ea624a7f05d698edabc3 Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Mon, 18 May 2026 17:40:20 +0300
Subject: [PATCH] wasapi: workaround that AudioClientProperties->Options not
being available in old SDKs
Closes: https://github.com/libsdl-org/SDL/issues/15641.
---
src/audio/wasapi/SDL_wasapi.c | 36 ++++++++++++++++++++++++----------
src/core/windows/SDL_windows.c | 5 +++++
src/core/windows/SDL_windows.h | 3 +++
3 files changed, 34 insertions(+), 10 deletions(-)
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index 2ef4de82fedd6..bdbbf81772b99 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -64,6 +64,19 @@ static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0
static bool immdevice_initialized = false;
static bool supports_recording_on_playback_devices = false;
+#ifdef __IAudioClient2_INTERFACE_DEFINED__
+#define SDL_AUDCLNT_STREAMOPTIONS_RAW 0x1
+typedef union SDL_AudioClientProperties {
+ AudioClientProperties a;
+ struct _SDL_AudioClientProperties {
+ UINT32 cbSize;
+ BOOL bIsOffload;
+ AUDIO_STREAM_CATEGORY eCategory;
+ int Options; // AUDCLNT_STREAMOPTIONS
+ } s;
+} SDL_AudioClientProperties;
+#endif //
+
// 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.
@@ -741,10 +754,10 @@ static bool mgmtthrtask_PrepDevice(void *userdata)
IAudioClient2 *client2 = NULL;
ret = IAudioClient_QueryInterface(client, &SDL_IID_IAudioClient2, (void **)&client2);
if (SUCCEEDED(ret)) {
- AudioClientProperties audioProps;
+ SDL_AudioClientProperties audioProps;
SDL_zero(audioProps);
- audioProps.cbSize = sizeof(audioProps);
+ audioProps.a.cbSize = sizeof(audioProps);
// Setting AudioCategory_GameChat breaks audio on several devices, including Behringer U-PHORIA UM2 and RODE NT-USB Mini.
// We'll disable this for now until we understand more about what's happening.
@@ -752,25 +765,28 @@ static bool mgmtthrtask_PrepDevice(void *userdata)
const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE);
if (hint && *hint) {
if (SDL_strcasecmp(hint, "Communications") == 0) {
- audioProps.eCategory = AudioCategory_Communications;
+ audioProps.a.eCategory = AudioCategory_Communications;
} else if (SDL_strcasecmp(hint, "Game") == 0) {
// We'll add support for GameEffects as distinct from GameMedia later when we add stream roles
- audioProps.eCategory = AudioCategory_GameEffects;
+ audioProps.a.eCategory = AudioCategory_GameEffects;
} else if (SDL_strcasecmp(hint, "GameChat") == 0) {
- audioProps.eCategory = AudioCategory_GameChat;
+ audioProps.a.eCategory = AudioCategory_GameChat;
} else if (SDL_strcasecmp(hint, "Movie") == 0) {
- audioProps.eCategory = AudioCategory_Movie;
+ audioProps.a.eCategory = AudioCategory_Movie;
} else if (SDL_strcasecmp(hint, "Media") == 0) {
- audioProps.eCategory = AudioCategory_Media;
+ audioProps.a.eCategory = AudioCategory_Media;
}
}
#endif
- if (SDL_GetHintBoolean(SDL_HINT_AUDIO_DEVICE_RAW_STREAM, false)) {
- audioProps.Options = AUDCLNT_STREAMOPTIONS_RAW;
+ if (WIN_IsWindows81OrGreater() &&
+ SDL_GetHintBoolean(SDL_HINT_AUDIO_DEVICE_RAW_STREAM, false)) {
+ audioProps.s.Options = SDL_AUDCLNT_STREAMOPTIONS_RAW;
+ } else {
+ audioProps.a.cbSize = sizeof (AudioClientProperties);
}
- ret = IAudioClient2_SetClientProperties(client2, &audioProps);
+ ret = IAudioClient2_SetClientProperties(client2, (AudioClientProperties *)&audioProps);
if (FAILED(ret)) {
// This isn't fatal, let's log it instead of failing
SDL_LogWarn(SDL_LOG_CATEGORY_AUDIO, "IAudioClient2_SetClientProperties failed: 0x%" SDL_PRIxSLONG, ret);
diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c
index eaedd59e96169..0be19bd2ec5a3 100644
--- a/src/core/windows/SDL_windows.c
+++ b/src/core/windows/SDL_windows.c
@@ -374,6 +374,11 @@ BOOL WIN_IsWindows8OrGreater(void)
CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0));
}
+BOOL WIN_IsWindows81OrGreater(void)
+{
+ CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0));
+}
+
BOOL WIN_IsWindows11OrGreater(void)
{
return IsWindowsBuildVersionAtLeast(22000);
diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h
index 62c1c3a9afb5d..0a7c78549353c 100644
--- a/src/core/windows/SDL_windows.h
+++ b/src/core/windows/SDL_windows.h
@@ -193,6 +193,9 @@ extern BOOL WIN_IsWindows7OrGreater(void);
// Returns true if we're running on Windows 8 and newer
extern BOOL WIN_IsWindows8OrGreater(void);
+// Returns true if we're running on Windows 8.1 and newer
+extern BOOL WIN_IsWindows81OrGreater(void);
+
// Returns true if we're running on Windows 11 and newer
extern BOOL WIN_IsWindows11OrGreater(void);