Added WASAPI support for SDL_HINT_AUDIO_DEVICE_STREAM_ROLE

Also added SDL_HINT_AUDIO_DEVICE_RAW_STREAM

Fixes https://github.com/libsdl-org/SDL/issues/14091
This commit is contained in:
Sam Lantinga
2025-10-13 11:53:10 -07:00
parent 20206b8e66
commit a58ae3a94f
3 changed files with 76 additions and 2 deletions

View File

@@ -391,12 +391,41 @@ extern "C" {
* concept, so it applies to a physical audio device in this case, and not an * concept, so it applies to a physical audio device in this case, and not an
* SDL_AudioStream, nor an SDL logical audio device. * SDL_AudioStream, nor an SDL logical audio device.
* *
* For Windows WASAPI audio, the following roles are supported, and map to `AUDIO_STREAM_CATEGORY`:
*
* - "Other" (default)
* - "Communications" - Real-time communications, such as VOIP or chat
* - "Game" - Game audio
* - "GameChat" - Game chat audio, similar to "Communications" except that this will not attenuate other audio streams
* - "Movie" - Music or sound with dialog
* - "Media" - Music or sound without dialog
*
* If your application applies its own echo cancellation, gain control, and noise reduction it should also set SDL_HINT_AUDIO_DEVICE_RAW_STREAM.
*
* This hint should be set before an audio device is opened. * This hint should be set before an audio device is opened.
* *
* \since This hint is available since SDL 3.2.0. * \since This hint is available since SDL 3.2.0.
*/ */
#define SDL_HINT_AUDIO_DEVICE_STREAM_ROLE "SDL_AUDIO_DEVICE_STREAM_ROLE" #define SDL_HINT_AUDIO_DEVICE_STREAM_ROLE "SDL_AUDIO_DEVICE_STREAM_ROLE"
/**
* Specify whether this audio device should do audio processing.
*
* Some operating systems perform echo cancellation, gain control, and noise reduction as needed. If your application already handles these, you can set this hint to prevent the OS from doing additional audio processing.
*
* This corresponds to the WASAPI audio option `AUDCLNT_STREAMOPTIONS_RAW`.
*
* The variable can be set to the following values:
*
* - "0": audio processing can be done by the OS. (default)
* - "1": audio processing is done by the application.
*
* This hint should be set before an audio device is opened.
*
* \since This hint is available since SDL 3.4.0.
*/
#define SDL_HINT_AUDIO_DEVICE_RAW_STREAM "SDL_AUDIO_DEVICE_RAW_STREAM"
/** /**
* Specify the input file when recording audio using the disk audio driver. * Specify the input file when recording audio using the disk audio driver.
* *

View File

@@ -54,6 +54,9 @@ static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NUL
static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; 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_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 } }; static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
#ifdef __IAudioClient2_INTERFACE_DEFINED__
static const IID SDL_IID_IAudioClient2 = { 0x726778cd, 0xf60a, 0x4EDA, { 0x82, 0xde, 0xe4, 0x76, 0x10, 0xcd, 0x78, 0xaa } };
#endif //
#ifdef __IAudioClient3_INTERFACE_DEFINED__ #ifdef __IAudioClient3_INTERFACE_DEFINED__
static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } }; static const IID SDL_IID_IAudioClient3 = { 0x7ed4ee07, 0x8e67, 0x4cd4, { 0x8c, 0x1a, 0x2b, 0x7a, 0x59, 0x87, 0xad, 0x42 } };
#endif // #endif //
@@ -727,6 +730,44 @@ static bool mgmtthrtask_PrepDevice(void *userdata)
int new_sample_frames = 0; int new_sample_frames = 0;
bool iaudioclient3_initialized = false; bool iaudioclient3_initialized = false;
#ifdef __IAudioClient2_INTERFACE_DEFINED__
IAudioClient2 *client2 = NULL;
ret = IAudioClient_QueryInterface(client, &SDL_IID_IAudioClient2, (void **)&client2);
if (SUCCEEDED(ret)) {
AudioClientProperties audioProps;
SDL_zero(audioProps);
audioProps.cbSize = sizeof(audioProps);
const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE);
if (hint && *hint) {
if (SDL_strcasecmp(hint, "Communications") == 0) {
audioProps.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;
} else if (SDL_strcasecmp(hint, "GameChat") == 0) {
audioProps.eCategory = AudioCategory_GameChat;
} else if (SDL_strcasecmp(hint, "Movie") == 0) {
audioProps.eCategory = AudioCategory_Movie;
} else if (SDL_strcasecmp(hint, "Media") == 0) {
audioProps.eCategory = AudioCategory_Media;
}
}
if (SDL_GetHintBoolean(SDL_HINT_AUDIO_DEVICE_RAW_STREAM, false)) {
audioProps.Options = AUDCLNT_STREAMOPTIONS_RAW;
}
ret = IAudioClient2_SetClientProperties(client2, &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%lx", ret);
}
IAudioClient2_Release(client2);
}
#endif
#ifdef __IAudioClient3_INTERFACE_DEFINED__ #ifdef __IAudioClient3_INTERFACE_DEFINED__
// Try querying IAudioClient3 if sharemode is AUDCLNT_SHAREMODE_SHARED // Try querying IAudioClient3 if sharemode is AUDCLNT_SHAREMODE_SHARED
if (sharemode == AUDCLNT_SHAREMODE_SHARED) { if (sharemode == AUDCLNT_SHAREMODE_SHARED) {

View File

@@ -62,13 +62,17 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
consumed = SDLTest_CommonArg(state, i); consumed = SDLTest_CommonArg(state, i);
if (!consumed) { if (!consumed) {
if (!filename) { if (SDL_strcmp(argv[i], "--role") == 0 && argv[i + 1]) {
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE, argv[i + 1]);
++i;
consumed = 1;
} else if (!filename) {
filename = argv[i]; filename = argv[i];
consumed = 1; consumed = 1;
} }
} }
if (consumed <= 0) { if (consumed <= 0) {
static const char *options[] = { "[sample.wav]", NULL }; static const char *options[] = { "[--role ROLE]", "[sample.wav]", NULL };
SDLTest_CommonLogUsage(state, argv[0], options); SDLTest_CommonLogUsage(state, argv[0], options);
exit(1); exit(1);
} }