update sdl Merge commit '4d48f9d23713d94b861da7b5d41baf2a41334994'
This commit is contained in:
131
external/sdl/SDL/src/core/android/SDL_android.c
vendored
131
external/sdl/SDL/src/core/android/SDL_android.c
vendored
@ -233,7 +233,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
|
||||
JNIEnv *env, jclass jcls);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture, jstring name,
|
||||
jint device_id);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
@ -242,7 +242,7 @@ JNIEXPORT void JNICALL
|
||||
|
||||
static JNINativeMethod SDLAudioManager_tab[] = {
|
||||
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) },
|
||||
{ "addAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
|
||||
{ "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) },
|
||||
{ "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) }
|
||||
};
|
||||
|
||||
@ -350,8 +350,8 @@ static jmethodID midSupportsRelativeMouse;
|
||||
static jclass mAudioManagerClass;
|
||||
|
||||
/* method signatures */
|
||||
static jmethodID midGetAudioOutputDevices;
|
||||
static jmethodID midGetAudioInputDevices;
|
||||
static jmethodID midRegisterAudioDeviceCallback;
|
||||
static jmethodID midUnregisterAudioDeviceCallback;
|
||||
static jmethodID midAudioOpen;
|
||||
static jmethodID midAudioWriteByteBuffer;
|
||||
static jmethodID midAudioWriteShortBuffer;
|
||||
@ -681,12 +681,12 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
|
||||
|
||||
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
|
||||
|
||||
midGetAudioOutputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"getAudioOutputDevices",
|
||||
"()[I");
|
||||
midGetAudioInputDevices = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"getAudioInputDevices",
|
||||
"()[I");
|
||||
midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"registerAudioDeviceCallback",
|
||||
"()V");
|
||||
midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"unregisterAudioDeviceCallback",
|
||||
"()V");
|
||||
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"audioOpen", "(IIIII)[I");
|
||||
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
@ -710,7 +710,7 @@ JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jcl
|
||||
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
|
||||
"audioSetThreadPriority", "(ZI)V");
|
||||
|
||||
if (!midGetAudioOutputDevices || !midGetAudioInputDevices || !midAudioOpen ||
|
||||
if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioOpen ||
|
||||
!midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer ||
|
||||
!midAudioClose ||
|
||||
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer ||
|
||||
@ -1002,18 +1002,17 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
|
||||
SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
|
||||
}
|
||||
|
||||
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
|
||||
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean is_capture,
|
||||
jint device_id)
|
||||
jstring name, jint device_id)
|
||||
{
|
||||
if (SDL_GetCurrentAudioDriver() != NULL) {
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding device with name %s, capture %d", device_name, is_capture);
|
||||
SDL_AddAudioDevice(is_capture, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
void *handle = (void *)((size_t)device_id);
|
||||
if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) {
|
||||
const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL);
|
||||
SDL_AddAudioDevice(is_capture ? SDL_TRUE : SDL_FALSE, SDL_strdup(utf8name), NULL, handle);
|
||||
(*env)->ReleaseStringUTFChars(env, name, utf8name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1022,8 +1021,8 @@ SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean i
|
||||
jint device_id)
|
||||
{
|
||||
if (SDL_GetCurrentAudioDriver() != NULL) {
|
||||
SDL_Log("Removing device with handle %d, capture %d", device_id + 1, is_capture);
|
||||
SDL_RemoveAudioDevice(is_capture, (void *)((size_t)device_id + 1));
|
||||
SDL_Log("Removing device with handle %d, capture %d", device_id, is_capture);
|
||||
SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1152,7 +1151,6 @@ retry:
|
||||
SDL_LockMutex(Android_ActivityMutex);
|
||||
|
||||
if (Android_Window) {
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
SDL_WindowData *data = Android_Window->driverdata;
|
||||
|
||||
/* Wait for Main thread being paused and context un-activated to release 'egl_surface' */
|
||||
@ -1169,7 +1167,7 @@ retry:
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL_EGL
|
||||
if (data->egl_surface != EGL_NO_SURFACE) {
|
||||
SDL_EGL_DestroySurface(_this, data->egl_surface);
|
||||
SDL_EGL_DestroySurface(SDL_GetVideoDevice(), data->egl_surface);
|
||||
data->egl_surface = EGL_NO_SURFACE;
|
||||
}
|
||||
#endif
|
||||
@ -1564,58 +1562,25 @@ static void *audioBufferPinned = NULL;
|
||||
static int captureBufferFormat = 0;
|
||||
static jobject captureBuffer = NULL;
|
||||
|
||||
static void Android_JNI_GetAudioDevices(int *devices, int *length, int max_len, int is_input)
|
||||
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
jintArray result;
|
||||
|
||||
if (is_input) {
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioInputDevices);
|
||||
} else {
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midGetAudioOutputDevices);
|
||||
}
|
||||
|
||||
*length = (*env)->GetArrayLength(env, result);
|
||||
|
||||
*length = SDL_min(*length, max_len);
|
||||
|
||||
(*env)->GetIntArrayRegion(env, result, 0, *length, devices);
|
||||
// this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change.
|
||||
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback);
|
||||
*default_output = *default_capture = NULL; // !!! FIXME: how do you decide the default device id?
|
||||
}
|
||||
|
||||
void Android_DetectDevices(void)
|
||||
void Android_StopAudioHotplug(void)
|
||||
{
|
||||
int inputs[100];
|
||||
int outputs[100];
|
||||
int inputs_length = 0;
|
||||
int outputs_length = 0;
|
||||
|
||||
SDL_zeroa(inputs);
|
||||
|
||||
Android_JNI_GetAudioDevices(inputs, &inputs_length, 100, 1 /* input devices */);
|
||||
|
||||
for (int i = 0; i < inputs_length; ++i) {
|
||||
int device_id = inputs[i];
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding input device with name %s", device_name);
|
||||
SDL_AddAudioDevice(SDL_TRUE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
}
|
||||
|
||||
SDL_zeroa(outputs);
|
||||
|
||||
Android_JNI_GetAudioDevices(outputs, &outputs_length, 100, 0 /* output devices */);
|
||||
|
||||
for (int i = 0; i < outputs_length; ++i) {
|
||||
int device_id = outputs[i];
|
||||
char device_name[64];
|
||||
SDL_snprintf(device_name, sizeof(device_name), "%d", device_id);
|
||||
SDL_Log("Adding output device with name %s", device_name);
|
||||
SDL_AddAudioDevice(SDL_FALSE, SDL_strdup(device_name), NULL, (void *)((size_t)device_id + 1));
|
||||
}
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback);
|
||||
}
|
||||
|
||||
int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec)
|
||||
int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device)
|
||||
{
|
||||
const SDL_bool iscapture = device->iscapture;
|
||||
SDL_AudioSpec *spec = &device->spec;
|
||||
const int device_id = (int) ((size_t) device->handle);
|
||||
int audioformat;
|
||||
jobject jbufobj = NULL;
|
||||
jobject result;
|
||||
@ -1640,10 +1605,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
||||
|
||||
if (iscapture) {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
|
||||
} else {
|
||||
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples, device_id);
|
||||
result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, device->sample_frames, device_id);
|
||||
}
|
||||
if (result == NULL) {
|
||||
/* Error during audio initialization, error printed from Java */
|
||||
@ -1668,10 +1633,10 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
||||
spec->format = SDL_AUDIO_F32;
|
||||
break;
|
||||
default:
|
||||
return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
|
||||
return SDL_SetError("Unexpected audio format from Java: %d", audioformat);
|
||||
}
|
||||
spec->channels = resultElements[2];
|
||||
spec->samples = resultElements[3];
|
||||
device->sample_frames = resultElements[3];
|
||||
(*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
|
||||
(*env)->DeleteLocalRef(env, result);
|
||||
|
||||
@ -1680,7 +1645,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
||||
switch (audioformat) {
|
||||
case ENCODING_PCM_8BIT:
|
||||
{
|
||||
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
|
||||
jbyteArray audioBufferLocal = (*env)->NewByteArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
@ -1688,7 +1653,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
||||
} break;
|
||||
case ENCODING_PCM_16BIT:
|
||||
{
|
||||
jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
|
||||
jshortArray audioBufferLocal = (*env)->NewShortArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
@ -1696,7 +1661,7 @@ int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spe
|
||||
} break;
|
||||
case ENCODING_PCM_FLOAT:
|
||||
{
|
||||
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
|
||||
jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, device->sample_frames * spec->channels);
|
||||
if (audioBufferLocal) {
|
||||
jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
|
||||
(*env)->DeleteLocalRef(env, audioBufferLocal);
|
||||
@ -1887,12 +1852,17 @@ void Android_JNI_CloseAudioDevice(const int iscapture)
|
||||
}
|
||||
}
|
||||
|
||||
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
|
||||
static void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
|
||||
{
|
||||
JNIEnv *env = Android_JNI_GetEnv();
|
||||
(*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
|
||||
}
|
||||
|
||||
void Android_AudioThreadInit(SDL_AudioDevice *device)
|
||||
{
|
||||
Android_JNI_AudioSetThreadPriority((int) device->iscapture, (int)device->instance_id);
|
||||
}
|
||||
|
||||
/* Test for an exception and call SDL_SetError with its detail if one occurs */
|
||||
/* If the parameter silent is truthy then SDL_SetError() will not be called. */
|
||||
static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
|
||||
@ -2013,13 +1983,18 @@ int Android_JNI_FileOpen(SDL_RWops *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size)
|
||||
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size)
|
||||
{
|
||||
AAsset *asset = (AAsset *)ctx->hidden.androidio.asset;
|
||||
return (Sint64) AAsset_read(asset, buffer, (size_t) size);
|
||||
int bytes = AAsset_read(asset, buffer, size);
|
||||
if (bytes < 0) {
|
||||
SDL_SetError("AAsset_read() failed");
|
||||
return 0;
|
||||
}
|
||||
return (size_t)bytes;
|
||||
}
|
||||
|
||||
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size)
|
||||
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size)
|
||||
{
|
||||
return SDL_SetError("Cannot write to Android package filesystem");
|
||||
}
|
||||
|
13
external/sdl/SDL/src/core/android/SDL_android.h
vendored
13
external/sdl/SDL/src/core/android/SDL_android.h
vendored
@ -30,6 +30,8 @@ extern "C" {
|
||||
#include <EGL/eglplatform.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
#include "../../audio/SDL_sysaudio.h"
|
||||
|
||||
/* Interface from the SDL library into the Android Java activity */
|
||||
extern void Android_JNI_SetActivityTitle(const char *title);
|
||||
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen);
|
||||
@ -47,14 +49,15 @@ extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
|
||||
|
||||
/* Audio support */
|
||||
extern void Android_DetectDevices(void);
|
||||
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
|
||||
void Android_StartAudioHotplug(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
void Android_StopAudioHotplug(void);
|
||||
extern void Android_AudioThreadInit(SDL_AudioDevice *device);
|
||||
extern int Android_JNI_OpenAudioDevice(SDL_AudioDevice *device);
|
||||
extern void *Android_JNI_GetAudioBuffer(void);
|
||||
extern void Android_JNI_WriteAudioBuffer(void);
|
||||
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
|
||||
extern void Android_JNI_FlushCapturedAudio(void);
|
||||
extern void Android_JNI_CloseAudioDevice(const int iscapture);
|
||||
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
|
||||
|
||||
/* Detecting device type */
|
||||
extern SDL_bool Android_IsDeXMode(void);
|
||||
@ -63,8 +66,8 @@ extern SDL_bool Android_IsChromebook(void);
|
||||
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode);
|
||||
Sint64 Android_JNI_FileSize(SDL_RWops *ctx);
|
||||
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence);
|
||||
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size);
|
||||
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size);
|
||||
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size);
|
||||
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size);
|
||||
int Android_JNI_FileClose(SDL_RWops *ctx);
|
||||
|
||||
/* Environment support */
|
||||
|
429
external/sdl/SDL/src/core/windows/SDL_immdevice.c
vendored
429
external/sdl/SDL/src/core/windows/SDL_immdevice.c
vendored
@ -27,6 +27,12 @@
|
||||
#include "../../audio/SDL_sysaudio.h"
|
||||
#include <objbase.h> /* For CLSIDFromString */
|
||||
|
||||
typedef struct SDL_IMMDevice_HandleData
|
||||
{
|
||||
LPWSTR immdevice_id;
|
||||
GUID directsound_guid;
|
||||
} SDL_IMMDevice_HandleData;
|
||||
|
||||
static const ERole SDL_IMMDevice_role = eConsole; /* !!! FIXME: should this be eMultimedia? Should be a hint? */
|
||||
|
||||
/* This is global to the WASAPI target, to handle hotplug and default device lookup. */
|
||||
@ -47,13 +53,28 @@ static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86
|
||||
static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
|
||||
static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
|
||||
static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
|
||||
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
|
||||
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
|
||||
static SDL_bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
|
||||
{
|
||||
const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *) device->handle;
|
||||
return (SDL_wcscmp(handle->immdevice_id, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
|
||||
{
|
||||
return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
|
||||
}
|
||||
|
||||
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
|
||||
{
|
||||
return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
|
||||
}
|
||||
|
||||
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
|
||||
{
|
||||
return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
|
||||
}
|
||||
|
||||
static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
|
||||
{
|
||||
@ -82,94 +103,54 @@ static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSI
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
|
||||
typedef struct DevIdList
|
||||
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
|
||||
{
|
||||
LPWSTR str;
|
||||
LPGUID guid;
|
||||
struct DevIdList *next;
|
||||
} DevIdList;
|
||||
|
||||
static DevIdList *deviceid_list = NULL;
|
||||
|
||||
static void SDL_IMMDevice_Remove(const SDL_bool iscapture, LPCWSTR devid, SDL_bool useguid)
|
||||
{
|
||||
DevIdList *i;
|
||||
DevIdList *next;
|
||||
DevIdList *prev = NULL;
|
||||
for (i = deviceid_list; i; i = next) {
|
||||
next = i->next;
|
||||
if (SDL_wcscmp(i->str, devid) == 0) {
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
deviceid_list = next;
|
||||
}
|
||||
SDL_RemoveAudioDevice(iscapture, useguid ? ((void *)i->guid) : ((void *)i->str));
|
||||
SDL_free(i->str);
|
||||
SDL_free(i);
|
||||
} else {
|
||||
prev = i;
|
||||
}
|
||||
if (device && device->handle) {
|
||||
SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
|
||||
SDL_free(handle->immdevice_id);
|
||||
SDL_free(handle);
|
||||
device->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid, SDL_bool useguid)
|
||||
static SDL_AudioDevice *SDL_IMMDevice_Add(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
|
||||
{
|
||||
DevIdList *devidlist;
|
||||
SDL_AudioSpec spec;
|
||||
LPWSTR devidcopy;
|
||||
LPGUID cpyguid;
|
||||
LPVOID driverdata;
|
||||
|
||||
/* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
|
||||
In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
|
||||
phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
|
||||
available and switch automatically. (!!! FIXME...?) */
|
||||
|
||||
/* see if we already have this one. */
|
||||
for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
|
||||
if (SDL_wcscmp(devidlist->str, devid) == 0) {
|
||||
return; /* we already have this. */
|
||||
if (!devname) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// see if we already have this one first.
|
||||
SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
|
||||
if (!device) {
|
||||
// handle is freed by SDL_IMMDevice_FreeDeviceHandle!
|
||||
SDL_IMMDevice_HandleData *handle = SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
|
||||
if (!handle) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist));
|
||||
if (devidlist == NULL) {
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
devidcopy = SDL_wcsdup(devid);
|
||||
if (!devidcopy) {
|
||||
SDL_free(devidlist);
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
if (useguid) {
|
||||
/* This is freed by DSOUND_FreeDeviceData! */
|
||||
cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
|
||||
if (!cpyguid) {
|
||||
SDL_free(devidlist);
|
||||
SDL_free(devidcopy);
|
||||
return; /* oh well. */
|
||||
handle->immdevice_id = SDL_wcsdup(devid);
|
||||
if (!handle->immdevice_id) {
|
||||
SDL_OutOfMemory();
|
||||
SDL_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
SDL_memcpy(cpyguid, dsoundguid, sizeof(GUID));
|
||||
driverdata = cpyguid;
|
||||
} else {
|
||||
cpyguid = NULL;
|
||||
driverdata = devidcopy;
|
||||
SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
SDL_zero(spec);
|
||||
spec.channels = (Uint8)fmt->Format.nChannels;
|
||||
spec.freq = fmt->Format.nSamplesPerSec;
|
||||
spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
|
||||
|
||||
device = SDL_AddAudioDevice(iscapture, devname, &spec, handle);
|
||||
}
|
||||
|
||||
devidlist->str = devidcopy;
|
||||
devidlist->guid = cpyguid;
|
||||
devidlist->next = deviceid_list;
|
||||
deviceid_list = devidlist;
|
||||
|
||||
SDL_zero(spec);
|
||||
spec.channels = (Uint8)fmt->Format.nChannels;
|
||||
spec.freq = fmt->Format.nSamplesPerSec;
|
||||
spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt);
|
||||
SDL_AddAudioDevice(iscapture, devname, &spec, driverdata);
|
||||
return device;
|
||||
}
|
||||
|
||||
/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
|
||||
@ -181,14 +162,13 @@ typedef struct SDLMMNotificationClient
|
||||
{
|
||||
const IMMNotificationClientVtbl *lpVtbl;
|
||||
SDL_AtomicInt refcount;
|
||||
SDL_bool useguid;
|
||||
} SDLMMNotificationClient;
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *this, REFIID iid, void **ppv)
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
|
||||
{
|
||||
if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
|
||||
*ppv = this;
|
||||
this->lpVtbl->AddRef(this);
|
||||
*ppv = client;
|
||||
client->lpVtbl->AddRef(client);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -196,55 +176,34 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotif
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *ithis)
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
|
||||
{
|
||||
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis;
|
||||
return (ULONG)(SDL_AtomicIncRef(&this->refcount) + 1);
|
||||
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
|
||||
return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
|
||||
}
|
||||
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *ithis)
|
||||
static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
|
||||
{
|
||||
/* this is a static object; we don't ever free it. */
|
||||
SDLMMNotificationClient *this = (SDLMMNotificationClient *)ithis;
|
||||
const ULONG retval = SDL_AtomicDecRef(&this->refcount);
|
||||
/* client is a static object; we don't ever free it. */
|
||||
SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
|
||||
const ULONG retval = SDL_AtomicDecRef(&client->refcount);
|
||||
if (retval == 0) {
|
||||
SDL_AtomicSet(&this->refcount, 0); /* uhh... */
|
||||
SDL_AtomicSet(&client->refcount, 0); /* uhh... */
|
||||
return 0;
|
||||
}
|
||||
return retval - 1;
|
||||
}
|
||||
|
||||
/* These are the entry points called when WASAPI device endpoints change. */
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *ithis, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
|
||||
// These are the entry points called when WASAPI device endpoints change.
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
if (role != SDL_IMMDevice_role) {
|
||||
return S_OK; /* ignore it. */
|
||||
if (role == SDL_IMMDevice_role) {
|
||||
SDL_DefaultAudioDeviceChanged(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
|
||||
}
|
||||
|
||||
/* Increment the "generation," so opened devices will pick this up in their threads. */
|
||||
switch (flow) {
|
||||
case eRender:
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
break;
|
||||
|
||||
case eCapture:
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
break;
|
||||
|
||||
case eAll:
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
SDL_assert(!"uhoh, unexpected OnDefaultDeviceChange flow!");
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
/* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
|
||||
OnDeviceStateChange, making that a better place to deal with device adds. More
|
||||
@ -254,13 +213,12 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotifi
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId)
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
/* See notes in OnDeviceAdded handler about why we ignore this. */
|
||||
return S_OK;
|
||||
return S_OK; // See notes in OnDeviceAdded handler about why we ignore this.
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *ithis, LPCWSTR pwstrDeviceId, DWORD dwNewState)
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
|
||||
{
|
||||
IMMDevice *device = NULL;
|
||||
|
||||
@ -270,18 +228,17 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
|
||||
EDataFlow flow;
|
||||
if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
|
||||
const SDL_bool iscapture = (flow == eCapture);
|
||||
const SDLMMNotificationClient *client = (SDLMMNotificationClient *)ithis;
|
||||
if (dwNewState == DEVICE_STATE_ACTIVE) {
|
||||
char *utf8dev;
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
GUID dsoundguid;
|
||||
GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
|
||||
if (utf8dev) {
|
||||
SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid, client->useguid);
|
||||
SDL_IMMDevice_Add(iscapture, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
|
||||
SDL_free(utf8dev);
|
||||
}
|
||||
} else {
|
||||
SDL_IMMDevice_Remove(iscapture, pwstrDeviceId, client->useguid);
|
||||
SDL_AudioDeviceDisconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
|
||||
}
|
||||
}
|
||||
IMMEndpoint_Release(endpoint);
|
||||
@ -292,9 +249,9 @@ static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IM
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *this, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
|
||||
static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
|
||||
{
|
||||
return S_OK; /* we don't care about these. */
|
||||
return S_OK; // we don't care about these.
|
||||
}
|
||||
|
||||
static const IMMNotificationClientVtbl notification_client_vtbl = {
|
||||
@ -314,31 +271,25 @@ int SDL_IMMDevice_Init(void)
|
||||
{
|
||||
HRESULT ret;
|
||||
|
||||
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
|
||||
/* just skip the discussion with COM here. */
|
||||
if (!WIN_IsWindowsVistaOrGreater()) {
|
||||
return SDL_SetError("WASAPI support requires Windows Vista or later");
|
||||
return SDL_SetError("IMMDevice support requires Windows Vista or later");
|
||||
}
|
||||
|
||||
if (FAILED(WIN_CoInitialize())) {
|
||||
return SDL_SetError("WASAPI: CoInitialize() failed");
|
||||
return SDL_SetError("IMMDevice: CoInitialize() failed");
|
||||
}
|
||||
|
||||
ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
|
||||
if (FAILED(ret)) {
|
||||
WIN_CoUninitialize();
|
||||
return WIN_SetErrorFromHRESULT("WASAPI CoCreateInstance(MMDeviceEnumerator)", ret);
|
||||
return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDL_IMMDevice_Quit(void)
|
||||
{
|
||||
DevIdList *devidlist;
|
||||
DevIdList *next;
|
||||
|
||||
if (enumerator) {
|
||||
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
||||
IMMDeviceEnumerator_Release(enumerator);
|
||||
@ -346,197 +297,99 @@ void SDL_IMMDevice_Quit(void)
|
||||
}
|
||||
|
||||
WIN_CoUninitialize();
|
||||
|
||||
for (devidlist = deviceid_list; devidlist; devidlist = next) {
|
||||
next = devidlist->next;
|
||||
SDL_free(devidlist->str);
|
||||
SDL_free(devidlist);
|
||||
}
|
||||
deviceid_list = NULL;
|
||||
}
|
||||
|
||||
int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture)
|
||||
int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture)
|
||||
{
|
||||
const Uint64 timeout = SDL_GetTicks() + 8000; /* intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep. */
|
||||
HRESULT ret;
|
||||
|
||||
SDL_assert(device != NULL);
|
||||
SDL_assert(immdevice != NULL);
|
||||
|
||||
while (SDL_TRUE) {
|
||||
if (devid == NULL) {
|
||||
const EDataFlow dataflow = iscapture ? eCapture : eRender;
|
||||
ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, device);
|
||||
} else {
|
||||
ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, device);
|
||||
LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
|
||||
SDL_assert(devid != NULL);
|
||||
|
||||
HRESULT ret;
|
||||
while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
|
||||
const Uint64 now = SDL_GetTicks();
|
||||
if (timeout > now) {
|
||||
const Uint64 ticksleft = timeout - now;
|
||||
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(ret)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == E_NOTFOUND) {
|
||||
const Uint64 now = SDL_GetTicks();
|
||||
if (timeout > now) {
|
||||
const Uint64 ticksleft = timeout - now;
|
||||
SDL_Delay((Uint32)SDL_min(ticksleft, 300)); /* wait awhile and try again. */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return SUCCEEDED(ret) ? 0 : WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
|
||||
|
||||
}
|
||||
|
||||
typedef struct
|
||||
static void EnumerateEndpointsForFlow(const SDL_bool iscapture, SDL_AudioDevice **default_device)
|
||||
{
|
||||
LPWSTR devid;
|
||||
char *devname;
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
GUID dsoundguid;
|
||||
} EndpointItem;
|
||||
|
||||
static int SDLCALL sort_endpoints(const void *_a, const void *_b)
|
||||
{
|
||||
LPWSTR a = ((const EndpointItem *)_a)->devid;
|
||||
LPWSTR b = ((const EndpointItem *)_b)->devid;
|
||||
if (!a && !b) {
|
||||
return 0;
|
||||
} else if (!a && b) {
|
||||
return -1;
|
||||
} else if (a && !b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (SDL_TRUE) {
|
||||
if (*a < *b) {
|
||||
return -1;
|
||||
} else if (*a > *b) {
|
||||
return 1;
|
||||
} else if (*a == 0) {
|
||||
break;
|
||||
}
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void EnumerateEndpointsForFlow(const SDL_bool iscapture)
|
||||
{
|
||||
IMMDeviceCollection *collection = NULL;
|
||||
EndpointItem *items;
|
||||
UINT i, total;
|
||||
|
||||
/* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
|
||||
...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
|
||||
|
||||
IMMDeviceCollection *collection = NULL;
|
||||
if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, iscapture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
|
||||
return;
|
||||
}
|
||||
|
||||
UINT total = 0;
|
||||
if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
|
||||
IMMDeviceCollection_Release(collection);
|
||||
return;
|
||||
}
|
||||
|
||||
items = (EndpointItem *)SDL_calloc(total, sizeof(EndpointItem));
|
||||
if (items == NULL) {
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
for (i = 0; i < total; i++) {
|
||||
EndpointItem *item = items + i;
|
||||
IMMDevice *device = NULL;
|
||||
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
|
||||
if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
|
||||
GetMMDeviceInfo(device, &item->devname, &item->fmt, &item->dsoundguid);
|
||||
LPWSTR default_devid = NULL;
|
||||
if (default_device) {
|
||||
IMMDevice *default_immdevice = NULL;
|
||||
const EDataFlow dataflow = iscapture ? eCapture : eRender;
|
||||
if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
|
||||
LPWSTR devid = NULL;
|
||||
if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
|
||||
default_devid = SDL_wcsdup(devid); // if this fails, oh well.
|
||||
CoTaskMemFree(devid);
|
||||
}
|
||||
IMMDevice_Release(device);
|
||||
IMMDevice_Release(default_immdevice);
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the list of devices by their guid so list is consistent between runs */
|
||||
SDL_qsort(items, total, sizeof(*items), sort_endpoints);
|
||||
|
||||
/* Send the sorted list on to the SDL's higher level. */
|
||||
for (i = 0; i < total; i++) {
|
||||
EndpointItem *item = items + i;
|
||||
if ((item->devid) && (item->devname)) {
|
||||
SDL_IMMDevice_Add(iscapture, item->devname, &item->fmt, item->devid, &item->dsoundguid, notification_client.useguid);
|
||||
for (UINT i = 0; i < total; i++) {
|
||||
IMMDevice *immdevice = NULL;
|
||||
if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
|
||||
LPWSTR devid = NULL;
|
||||
if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
|
||||
char *devname = NULL;
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
GUID dsoundguid;
|
||||
SDL_zero(fmt);
|
||||
SDL_zero(dsoundguid);
|
||||
GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
|
||||
if (devname) {
|
||||
SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(iscapture, devname, &fmt, devid, &dsoundguid);
|
||||
if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
|
||||
*default_device = sdldevice;
|
||||
}
|
||||
SDL_free(devname);
|
||||
}
|
||||
CoTaskMemFree(devid);
|
||||
}
|
||||
IMMDevice_Release(immdevice);
|
||||
}
|
||||
SDL_free(item->devname);
|
||||
CoTaskMemFree(item->devid);
|
||||
}
|
||||
|
||||
SDL_free(items);
|
||||
SDL_free(default_devid);
|
||||
|
||||
IMMDeviceCollection_Release(collection);
|
||||
}
|
||||
|
||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid)
|
||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
|
||||
{
|
||||
notification_client.useguid = useguid;
|
||||
|
||||
EnumerateEndpointsForFlow(SDL_FALSE); /* playback */
|
||||
EnumerateEndpointsForFlow(SDL_TRUE); /* capture */
|
||||
EnumerateEndpointsForFlow(SDL_FALSE, default_output); /* playback */
|
||||
EnumerateEndpointsForFlow(SDL_TRUE, default_capture); /* capture */
|
||||
|
||||
/* if this fails, we just won't get hotplug events. Carry on anyhow. */
|
||||
IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)¬ification_client);
|
||||
}
|
||||
|
||||
int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
IMMDevice *device = NULL;
|
||||
char *filler;
|
||||
GUID morefiller;
|
||||
const EDataFlow dataflow = iscapture ? eCapture : eRender;
|
||||
HRESULT ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &device);
|
||||
|
||||
if (FAILED(ret)) {
|
||||
SDL_assert(device == NULL);
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't find default audio endpoint", ret);
|
||||
}
|
||||
|
||||
if (name == NULL) {
|
||||
name = &filler;
|
||||
}
|
||||
|
||||
SDL_zero(fmt);
|
||||
GetMMDeviceInfo(device, name, &fmt, &morefiller);
|
||||
IMMDevice_Release(device);
|
||||
|
||||
if (name == &filler) {
|
||||
SDL_free(filler);
|
||||
}
|
||||
|
||||
SDL_zerop(spec);
|
||||
spec->channels = (Uint8)fmt.Format.nChannels;
|
||||
spec->freq = fmt.Format.nSamplesPerSec;
|
||||
spec->format = WaveFormatToSDLFormat((WAVEFORMATEX *)&fmt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
|
||||
{
|
||||
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
} else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
|
||||
if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* (defined(__WIN32__) || defined(__GDK__)) && defined(HAVE_MMDEVICEAPI_H) */
|
||||
|
@ -26,16 +26,14 @@
|
||||
#include <mmdeviceapi.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
typedef struct SDL_AudioDevice SDL_AudioDevice; // this is defined in src/audio/SDL_sysaudio.h
|
||||
|
||||
int SDL_IMMDevice_Init(void);
|
||||
void SDL_IMMDevice_Quit(void);
|
||||
int SDL_IMMDevice_Get(LPCWSTR devid, IMMDevice **device, SDL_bool iscapture);
|
||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_bool useguid);
|
||||
int SDL_IMMDevice_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
|
||||
|
||||
SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat);
|
||||
|
||||
/* these increment as default devices change. Opened default devices pick up changes in their threads. */
|
||||
extern SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
|
||||
extern SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
|
||||
int SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, SDL_bool iscapture);
|
||||
void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
|
||||
LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device);
|
||||
LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device);
|
||||
void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device);
|
||||
|
||||
#endif /* SDL_IMMDEVICE_H */
|
||||
|
29
external/sdl/SDL/src/core/windows/SDL_windows.c
vendored
29
external/sdl/SDL/src/core/windows/SDL_windows.c
vendored
@ -335,6 +335,33 @@ BOOL WIN_IsRectEmpty(const RECT *rect)
|
||||
return (rect->right <= rect->left) || (rect->bottom <= rect->top);
|
||||
}
|
||||
|
||||
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
|
||||
{
|
||||
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
} else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
|
||||
if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Win32-specific SDL_RunApp(), which does most of the SDL_main work,
|
||||
based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
|
||||
#ifdef __WIN32__
|
||||
@ -348,7 +375,7 @@ static int OutOfMemory(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
DECLSPEC int SDL_RunApp(int _argc, char* _argv[], SDL_main_func mainFunction, void * reserved)
|
||||
DECLSPEC int MINGW32_FORCEALIGN SDL_RunApp(int _argc, char* _argv[], SDL_main_func mainFunction, void * reserved)
|
||||
{
|
||||
|
||||
/* Gets the arguments with GetCommandLine, converts them to argc and argv
|
||||
|
16
external/sdl/SDL/src/core/windows/SDL_windows.h
vendored
16
external/sdl/SDL/src/core/windows/SDL_windows.h
vendored
@ -76,8 +76,22 @@
|
||||
#define WINVER _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
/* See https://github.com/libsdl-org/SDL/pull/7607 */
|
||||
/* force_align_arg_pointer attribute requires gcc >= 4.2.x. */
|
||||
#if defined(__clang__)
|
||||
#define HAVE_FORCE_ALIGN_ARG_POINTER
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
|
||||
#define HAVE_FORCE_ALIGN_ARG_POINTER
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
|
||||
#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define MINGW32_FORCEALIGN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <basetyps.h> /* for REFIID with broken mingw.org headers */
|
||||
#include <mmreg.h>
|
||||
|
||||
/* Older Visual C++ headers don't have the Win64-compatible typedefs... */
|
||||
#if defined(_MSC_VER) && (_MSC_VER <= 1200)
|
||||
@ -154,6 +168,8 @@ extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
|
||||
/* Returns SDL_TRUE if the rect is empty */
|
||||
extern BOOL WIN_IsRectEmpty(const RECT *rect);
|
||||
|
||||
extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Reference in New Issue
Block a user