Quellcode durchsuchen

wasapi: Reworked for new SDL3 audio API, other win32 fixes.

The WinRT code has _also_ be updated, but it has not been
tested or compiled, yet.
Ryan C. Gordon vor 2 Jahren
Ursprung
Commit
c58d95c343

+ 43 - 1
src/audio/SDL_audio.c

@@ -1082,7 +1082,7 @@ int SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec)
     return 0;
     return 0;
 }
 }
 
 
-// this expects the device lock to be held.
+// this expects the device lock to be held.  !!! FIXME: no it doesn't...?
 static void ClosePhysicalAudioDevice(SDL_AudioDevice *device)
 static void ClosePhysicalAudioDevice(SDL_AudioDevice *device)
 {
 {
     SDL_assert(current_audio.impl.ProvidesOwnCallbackThread || ((device->thread == NULL) == (SDL_AtomicGet(&device->thread_alive) == 0)));
     SDL_assert(current_audio.impl.ProvidesOwnCallbackThread || ((device->thread == NULL) == (SDL_AtomicGet(&device->thread_alive) == 0)));
@@ -1688,3 +1688,45 @@ void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device)
         SDL_AudioDeviceDisconnected(current_default_device);  // Call again, now that we're not the default; this will remove from device list, send removal events, and destroy the SDL_AudioDevice.
         SDL_AudioDeviceDisconnected(current_default_device);  // Call again, now that we're not the default; this will remove from device list, send removal events, and destroy the SDL_AudioDevice.
     }
     }
 }
 }
+
+int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames)
+{
+    SDL_bool kill_device = SDL_FALSE;
+
+    const int orig_buffer_size = device->buffer_size;
+    const SDL_bool iscapture = device->iscapture;
+
+    if ((device->spec.format != newspec->format) || (device->spec.channels != newspec->channels) || (device->spec.freq != newspec->freq)) {
+        SDL_memcpy(&device->spec, newspec, sizeof (newspec));
+        for (SDL_LogicalAudioDevice *logdev = device->logical_devices; !kill_device && (logdev != NULL); logdev = logdev->next) {
+            for (SDL_AudioStream *stream = logdev->bound_streams; !kill_device && (stream != NULL); stream = stream->next_binding) {
+                if (SDL_SetAudioStreamFormat(stream, iscapture ? &device->spec : NULL, iscapture ? NULL : &device->spec) == -1) {
+                    kill_device = SDL_TRUE;
+                }
+            }
+        }
+    }
+
+    if (!kill_device) {
+        device->sample_frames = new_sample_frames;
+        SDL_UpdatedAudioDeviceFormat(device);
+        if (device->work_buffer && (device->buffer_size > orig_buffer_size)) {
+            SDL_aligned_free(device->work_buffer);
+            device->work_buffer = (Uint8 *)SDL_aligned_alloc(SDL_SIMDGetAlignment(), device->buffer_size);
+            if (!device->work_buffer) {
+                kill_device = SDL_TRUE;
+            }
+        }
+    }
+
+    return kill_device ? -1 : 0;
+}
+
+int SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames)
+{
+    SDL_LockMutex(device->lock);
+    const int retval = SDL_AudioDeviceFormatChangedAlreadyLocked(device, newspec, new_sample_frames);
+    SDL_UnlockMutex(device->lock);
+    return retval;
+}
+

+ 1 - 1
src/audio/SDL_audiocvt.c

@@ -879,7 +879,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
             // if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame.
             // if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame.
             //  however, if the stream is flushed, we would just be padding any remaining input with silence anyhow, so use it up.
             //  however, if the stream is flushed, we would just be padding any remaining input with silence anyhow, so use it up.
             if (stream->flushed) {
             if (stream->flushed) {
-                SDL_assert(((input_frames * src_sample_frame_size) + future_buffer_filled_frames) <= stream->future_buffer_allocation);
+                SDL_assert(((size_t) ((input_frames * src_sample_frame_size) + future_buffer_filled_frames)) <= stream->future_buffer_allocation);
                 // leave input_frames alone; this will just shuffle what's available from the future buffer and pad with silence as appropriate, below.
                 // leave input_frames alone; this will just shuffle what's available from the future buffer and pad with silence as appropriate, below.
             } else {
             } else {
                 return 0;
                 return 0;

+ 6 - 0
src/audio/SDL_sysaudio.h

@@ -85,6 +85,12 @@ extern void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device);
 // Backends should call this if the system default device changes.
 // Backends should call this if the system default device changes.
 extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device);
 extern void SDL_DefaultAudioDeviceChanged(SDL_AudioDevice *new_default_device);
 
 
+// Backends should call this if a device's format is changing (opened or not); SDL will update state and carry on with the new format.
+extern int SDL_AudioDeviceFormatChanged(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
+
+// Same as above, but assume the device is already locked.
+extern int SDL_AudioDeviceFormatChangedAlreadyLocked(SDL_AudioDevice *device, const SDL_AudioSpec *newspec, int new_sample_frames);
+
 // Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE.
 // Find the SDL_AudioDevice associated with the handle supplied to SDL_AddAudioDevice. NULL if not found. DOES NOT LOCK THE DEVICE.
 extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle);
 extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByHandle(void *handle);
 
 

+ 447 - 317
src/audio/wasapi/SDL_wasapi.c

@@ -24,6 +24,7 @@
 
 
 #include "../../core/windows/SDL_windows.h"
 #include "../../core/windows/SDL_windows.h"
 #include "../../core/windows/SDL_immdevice.h"
 #include "../../core/windows/SDL_immdevice.h"
+#include "../../thread/SDL_systhread.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_sysaudio.h"
 #include "../SDL_sysaudio.h"
 
 
@@ -32,7 +33,7 @@
 
 
 #include "SDL_wasapi.h"
 #include "SDL_wasapi.h"
 
 
-/* These constants aren't available in older SDKs */
+// These constants aren't available in older SDKs
 #ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
 #ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
 #define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
 #define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
 #endif
 #endif
@@ -43,140 +44,367 @@
 #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
 #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
 #endif
 #endif
 
 
-/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
+// 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_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 void WASAPI_DetectDevices(void)
+// 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.
+
+typedef struct ManagementThreadPendingTask
+{
+    ManagementThreadTask fn;
+    void *userdata;
+    int result;
+    SDL_Semaphore *task_complete_sem;
+    char *errorstr;
+    struct ManagementThreadPendingTask *next;
+} ManagementThreadPendingTask;
+
+static SDL_Thread *ManagementThread = NULL;
+static ManagementThreadPendingTask *ManagementThreadPendingTasks = NULL;
+static SDL_Mutex *ManagementThreadLock = NULL;
+static SDL_Condition *ManagementThreadCondition = NULL;
+static SDL_AtomicInt ManagementThreadShutdown;
+
+static void ManagementThreadMainloop(void)
 {
 {
-    WASAPI_EnumerateEndpoints();
+    SDL_LockMutex(ManagementThreadLock);
+    ManagementThreadPendingTask *task;
+    while (((task = SDL_AtomicGetPtr(&ManagementThreadPendingTasks)) != NULL) || !SDL_AtomicGet(&ManagementThreadShutdown)) {
+        if (!task) {
+            SDL_WaitCondition(ManagementThreadCondition, ManagementThreadLock); // block until there's something to do.
+        } else {
+            SDL_AtomicSetPtr(&ManagementThreadPendingTasks, task->next); // take task off the pending list.
+            SDL_UnlockMutex(ManagementThreadLock);                       // let other things add to the list while we chew on this task.
+            task->result = task->fn(task->userdata);                     // run this task.
+            if (task->task_complete_sem) {                               // something waiting on result?
+                task->errorstr = SDL_strdup(SDL_GetError());
+                SDL_PostSemaphore(task->task_complete_sem);
+            } else { // nothing waiting, we're done, free it.
+                SDL_free(task);
+            }
+            SDL_LockMutex(ManagementThreadLock); // regrab the lock so we can get the next task; if nothing to do, we'll release the lock in SDL_WaitCondition.
+        }
+    }
+    SDL_UnlockMutex(ManagementThreadLock); // told to shut down and out of tasks, let go of the lock and return.
 }
 }
 
 
-static SDL_INLINE SDL_bool WasapiFailed(SDL_AudioDevice *_this, const HRESULT err)
+int WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, int *wait_on_result)
 {
 {
-    if (err == S_OK) {
-        return SDL_FALSE;
+    // We want to block for a result, but we are already running from the management thread! Just run the task now so we don't deadlock.
+    if ((wait_on_result != NULL) && (SDL_ThreadID() == SDL_GetThreadID(ManagementThread))) {
+        *wait_on_result = task(userdata);
+        return 0;  // completed!
     }
     }
 
 
-    if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
-        _this->hidden->device_lost = SDL_TRUE;
-    } else if (SDL_AtomicGet(&_this->enabled)) {
-        IAudioClient_Stop(_this->hidden->client);
-        SDL_OpenedAudioDeviceDisconnected(_this);
-        SDL_assert(!SDL_AtomicGet(&_this->enabled));
+    if (SDL_AtomicGet(&ManagementThreadShutdown)) {
+        return SDL_SetError("Can't add task, we're shutting down");
     }
     }
 
 
-    return SDL_TRUE;
-}
+    ManagementThreadPendingTask *pending = SDL_calloc(1, sizeof(ManagementThreadPendingTask));
+    if (!pending) {
+        return SDL_OutOfMemory();
+    }
 
 
-static int UpdateAudioStream(SDL_AudioDevice *_this, const SDL_AudioSpec *oldspec)
-{
-    /* Since WASAPI requires us to handle all audio conversion, and our
-       device format might have changed, we might have to add/remove/change
-       the audio stream that the higher level uses to convert data, so
-       SDL keeps firing the callback as if nothing happened here. */
-
-    if ((_this->callbackspec.channels == _this->spec.channels) &&
-        (_this->callbackspec.format == _this->spec.format) &&
-        (_this->callbackspec.freq == _this->spec.freq) &&
-        (_this->callbackspec.samples == _this->spec.samples)) {
-        /* no need to buffer/convert in an AudioStream! */
-        SDL_DestroyAudioStream(_this->stream);
-        _this->stream = NULL;
-    } else if ((oldspec->channels == _this->spec.channels) &&
-               (oldspec->format == _this->spec.format) &&
-               (oldspec->freq == _this->spec.freq)) {
-        /* The existing audio stream is okay to keep using. */
-    } else {
-        /* replace the audiostream for new format */
-        SDL_DestroyAudioStream(_this->stream);
-        if (_this->iscapture) {
-            _this->stream = SDL_CreateAudioStream(_this->spec.format,
-                                              _this->spec.channels, _this->spec.freq,
-                                              _this->callbackspec.format,
-                                              _this->callbackspec.channels,
-                                              _this->callbackspec.freq);
-        } else {
-            _this->stream = SDL_CreateAudioStream(_this->callbackspec.format,
-                                              _this->callbackspec.channels,
-                                              _this->callbackspec.freq, _this->spec.format,
-                                              _this->spec.channels, _this->spec.freq);
-        }
+    pending->fn = task;
+    pending->userdata = userdata;
 
 
-        if (!_this->stream) {
-            return -1; /* SDL_CreateAudioStream should have called SDL_SetError. */
+    if (wait_on_result) {
+        pending->task_complete_sem = SDL_CreateSemaphore(0);
+        if (!pending->task_complete_sem) {
+            SDL_free(pending);
+            return -1;
         }
         }
     }
     }
 
 
-    /* make sure our scratch buffer can cover the new device spec. */
-    if (_this->spec.size > _this->work_buffer_len) {
-        Uint8 *ptr = (Uint8 *)SDL_realloc(_this->work_buffer, _this->spec.size);
-        if (ptr == NULL) {
-            return SDL_OutOfMemory();
+    pending->next = NULL;
+
+    SDL_LockMutex(ManagementThreadLock);
+
+    // add to end of task list.
+    ManagementThreadPendingTask *prev = NULL;
+    for (ManagementThreadPendingTask *i = SDL_AtomicGetPtr(&ManagementThreadPendingTasks); i != NULL; i = i->next) {
+        prev = i;
+    }
+
+    if (prev != NULL) {
+        prev->next = pending;
+    } else {
+        SDL_AtomicSetPtr(&ManagementThreadPendingTasks, pending);
+    }
+
+    // task is added to the end of the pending list, let management thread rip!
+    SDL_SignalCondition(ManagementThreadCondition);
+    SDL_UnlockMutex(ManagementThreadLock);
+
+    if (wait_on_result) {
+        SDL_WaitSemaphore(pending->task_complete_sem);
+        SDL_DestroySemaphore(pending->task_complete_sem);
+        *wait_on_result = pending->result;
+        if (pending->errorstr) {
+            SDL_SetError("%s", pending->errorstr);
+            SDL_free(pending->errorstr);
         }
         }
-        _this->work_buffer = ptr;
-        _this->work_buffer_len = _this->spec.size;
+        SDL_free(pending);
+    }
+
+    return 0; // successfully added (and possibly executed)!
+}
+
+static int ManagementThreadPrepare(void)
+{
+    if (WASAPI_PlatformInit() == -1) {
+        return -1;
+    }
+
+    ManagementThreadLock = SDL_CreateMutex();
+    if (!ManagementThreadLock) {
+        WASAPI_PlatformDeinit();
+        return -1;
+    }
+
+    ManagementThreadCondition = SDL_CreateCondition();
+    if (!ManagementThreadCondition) {
+        SDL_DestroyMutex(ManagementThreadLock);
+        ManagementThreadLock = NULL;
+        WASAPI_PlatformDeinit();
+        return -1;
     }
     }
 
 
     return 0;
     return 0;
 }
 }
 
 
-static void ReleaseWasapiDevice(SDL_AudioDevice *_this);
+typedef struct
+{
+    char *errorstr;
+    SDL_Semaphore *ready_sem;
+} ManagementThreadEntryData;
+
+static int ManagementThreadEntry(void *userdata)
+{
+    ManagementThreadEntryData *data = (ManagementThreadEntryData *)userdata;
+
+    if (ManagementThreadPrepare() < 0) {
+        data->errorstr = SDL_strdup(SDL_GetError());
+        SDL_PostSemaphore(data->ready_sem); // unblock calling thread.
+        return 0;
+    }
+
+    SDL_PostSemaphore(data->ready_sem); // unblock calling thread.
+    ManagementThreadMainloop();
+
+    WASAPI_PlatformDeinit();
+    return 0;
+}
 
 
-static SDL_bool RecoverWasapiDevice(SDL_AudioDevice *_this)
+static int InitManagementThread(void)
 {
 {
-    ReleaseWasapiDevice(_this); /* dump the lost device's handles. */
+    ManagementThreadEntryData mgmtdata;
+    SDL_zero(mgmtdata);
+    mgmtdata.ready_sem = SDL_CreateSemaphore(0);
+    if (!mgmtdata.ready_sem) {
+        return -1;
+    }
+
+    SDL_AtomicSetPtr(&ManagementThreadPendingTasks, NULL);
+    SDL_AtomicSet(&ManagementThreadShutdown, 0);
+    ManagementThread = SDL_CreateThreadInternal(ManagementThreadEntry, "SDLWASAPIMgmt", 256 * 1024, &mgmtdata); // !!! FIXME: maybe even smaller stack size?
+    if (!ManagementThread) {
+        return -1;
+    }
+
+    SDL_WaitSemaphore(mgmtdata.ready_sem);
+    SDL_DestroySemaphore(mgmtdata.ready_sem);
 
 
-    if (_this->hidden->default_device_generation) {
-        _this->hidden->default_device_generation = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
+    if (mgmtdata.errorstr) {
+        SDL_WaitThread(ManagementThread, NULL);
+        ManagementThread = NULL;
+        SDL_SetError("%s", mgmtdata.errorstr);
+        SDL_free(mgmtdata.errorstr);
+        return -1;
     }
     }
 
 
-    /* this can fail for lots of reasons, but the most likely is we had a
-       non-default device that was disconnected, so we can't recover. Default
-       devices try to reinitialize whatever the new default is, so it's more
-       likely to carry on here, but this handles a non-default device that
-       simply had its format changed in the Windows Control Panel. */
-    if (WASAPI_ActivateDevice(_this, SDL_TRUE) == -1) {
-        SDL_OpenedAudioDeviceDisconnected(_this);
+    return 0;
+}
+
+static void DeinitManagementThread(void)
+{
+    if (ManagementThread) {
+        SDL_AtomicSet(&ManagementThreadShutdown, 1);
+        SDL_LockMutex(ManagementThreadLock);
+        SDL_SignalCondition(ManagementThreadCondition);
+        SDL_UnlockMutex(ManagementThreadLock);
+        SDL_WaitThread(ManagementThread, NULL);
+        ManagementThread = NULL;
+    }
+
+    SDL_assert(SDL_AtomicGetPtr(&ManagementThreadPendingTasks) == NULL);
+
+    SDL_DestroyCondition(ManagementThreadCondition);
+    SDL_DestroyMutex(ManagementThreadLock);
+    ManagementThreadCondition = NULL;
+    ManagementThreadLock = NULL;
+    SDL_AtomicSet(&ManagementThreadShutdown, 0);
+}
+
+typedef struct
+{
+    SDL_AudioDevice **default_output;
+    SDL_AudioDevice **default_capture;
+} mgmtthrtask_DetectDevicesData;
+
+static int mgmtthrtask_DetectDevices(void *userdata)
+{
+    mgmtthrtask_DetectDevicesData *data = (mgmtthrtask_DetectDevicesData *)userdata;
+    WASAPI_EnumerateEndpoints(data->default_output, data->default_capture);
+    return 0;
+}
+
+static void WASAPI_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
+{
+    int rc;
+    mgmtthrtask_DetectDevicesData data = { default_output, default_capture };
+    WASAPI_ProxyToManagementThread(mgmtthrtask_DetectDevices, &data, &rc);
+}
+
+static int mgmtthrtask_DisconnectDevice(void *userdata)
+{
+    SDL_AudioDeviceDisconnected((SDL_AudioDevice *)userdata);
+    return 0;
+}
+
+void WASAPI_DisconnectDevice(SDL_AudioDevice *device)
+{
+    // this runs async, so it can hold the device lock from the management thread.
+    WASAPI_ProxyToManagementThread(mgmtthrtask_DisconnectDevice, device, NULL);
+}
+
+static SDL_bool WasapiFailed(SDL_AudioDevice *device, const HRESULT err)
+{
+    if (err == S_OK) {
         return SDL_FALSE;
         return SDL_FALSE;
     }
     }
 
 
-    _this->hidden->device_lost = SDL_FALSE;
+    if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
+        device->hidden->device_lost = SDL_TRUE;
+    } else {
+        IAudioClient_Stop(device->hidden->client);
+        WASAPI_DisconnectDevice(device);
+    }
+
+    return SDL_TRUE;
+}
+
+static int mgmtthrtask_ReleaseCaptureClient(void *userdata)
+{
+    IAudioCaptureClient_Release((IAudioCaptureClient *)userdata);
+    return 0;
+}
 
 
-    return SDL_TRUE; /* okay, carry on with new device details! */
+static int mgmtthrtask_ReleaseRenderClient(void *userdata)
+{
+    IAudioRenderClient_Release((IAudioRenderClient *)userdata);
+    return 0;
 }
 }
 
 
-static SDL_bool RecoverWasapiIfLost(SDL_AudioDevice *_this)
+static int mgmtthrtask_ResetWasapiDevice(void *userdata)
 {
 {
-    const int generation = _this->hidden->default_device_generation;
-    SDL_bool lost = _this->hidden->device_lost;
+    SDL_AudioDevice *device = (SDL_AudioDevice *)userdata;
 
 
-    if (!SDL_AtomicGet(&_this->enabled)) {
-        return SDL_FALSE; /* already failed. */
+    if (!device || !device->hidden) {
+        return 0;
     }
     }
 
 
-    if (!_this->hidden->client) {
-        return SDL_TRUE; /* still waiting for activation. */
+    if (device->hidden->client) {
+        IAudioClient_Stop(device->hidden->client);
+        IAudioClient_Release(device->hidden->client);
+        device->hidden->client = NULL;
     }
     }
 
 
-    if (!lost && (generation > 0)) { /* is a default device? */
-        const int newgen = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
-        if (generation != newgen) { /* the desired default device was changed, jump over to it. */
-            lost = SDL_TRUE;
-        }
+    if (device->hidden->render) {
+        // this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
+        //  proxy this to the management thread to be released later.
+        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseRenderClient, device->hidden->render, NULL);
+        device->hidden->render = NULL;
     }
     }
 
 
-    return lost ? RecoverWasapiDevice(_this) : SDL_TRUE;
+    if (device->hidden->capture) {
+        // this is silly, but this will block indefinitely if you call it from SDLMMNotificationClient_OnDefaultDeviceChanged, so
+        //  proxy this to the management thread to be released later.
+        WASAPI_ProxyToManagementThread(mgmtthrtask_ReleaseCaptureClient, device->hidden->capture, NULL);
+        device->hidden->capture = NULL;
+    }
+
+    if (device->hidden->waveformat) {
+        CoTaskMemFree(device->hidden->waveformat);
+        device->hidden->waveformat = NULL;
+    }
+
+    if (device->hidden->activation_handler) {
+        WASAPI_PlatformDeleteActivationHandler(device->hidden->activation_handler);
+        device->hidden->activation_handler = NULL;
+    }
+
+    if (device->hidden->event) {
+        CloseHandle(device->hidden->event);
+        device->hidden->event = NULL;
+    }
+
+    return 0;
+}
+
+static void ResetWasapiDevice(SDL_AudioDevice *device)
+{
+    int rc;
+    WASAPI_ProxyToManagementThread(mgmtthrtask_ResetWasapiDevice, device, &rc);
 }
 }
 
 
-static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *_this)
+static int mgmtthrtask_ActivateDevice(void *userdata)
 {
 {
-    /* get an endpoint buffer from WASAPI. */
+    return WASAPI_ActivateDevice((SDL_AudioDevice *)userdata);
+}
+
+static int ActivateWasapiDevice(SDL_AudioDevice *device)
+{
+    int rc;
+    return ((WASAPI_ProxyToManagementThread(mgmtthrtask_ActivateDevice, device, &rc) < 0) || (rc < 0)) ? -1 : 0;
+}
+
+static SDL_bool RecoverWasapiDevice(SDL_AudioDevice *device)
+{
+    ResetWasapiDevice(device); // dump the lost device's handles.
+
+    // This handles a non-default device that simply had its format changed in the Windows Control Panel.
+    if (ActivateWasapiDevice(device) == -1) {
+        WASAPI_DisconnectDevice(device);
+        return SDL_FALSE;
+    }
+
+    device->hidden->device_lost = SDL_FALSE;
+
+    return SDL_TRUE; // okay, carry on with new device details!
+}
+
+static SDL_bool RecoverWasapiIfLost(SDL_AudioDevice *device)
+{
+    if (SDL_AtomicGet(&device->shutdown)) {
+        return SDL_FALSE; // already failed.
+    } else if (!device->hidden->client) {
+        return SDL_TRUE; // still waiting for activation.
+    }
+
+    return device->hidden->device_lost ? RecoverWasapiDevice(device) : SDL_TRUE;
+}
+
+static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
+{
+    // get an endpoint buffer from WASAPI.
     BYTE *buffer = NULL;
     BYTE *buffer = NULL;
 
 
-    while (RecoverWasapiIfLost(_this) && _this->hidden->render) {
-        if (!WasapiFailed(_this, IAudioRenderClient_GetBuffer(_this->hidden->render, _this->spec.samples, &buffer))) {
+    while (RecoverWasapiIfLost(device) && device->hidden->render) {
+        if (!WasapiFailed(device, IAudioRenderClient_GetBuffer(device->hidden->render, device->sample_frames, &buffer))) {
             return (Uint8 *)buffer;
             return (Uint8 *)buffer;
         }
         }
         SDL_assert(buffer == NULL);
         SDL_assert(buffer == NULL);
@@ -185,197 +413,108 @@ static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *_this)
     return (Uint8 *)buffer;
     return (Uint8 *)buffer;
 }
 }
 
 
-static void WASAPI_PlayDevice(SDL_AudioDevice *_this)
+static void WASAPI_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
 {
 {
-    if (_this->hidden->render != NULL) { /* definitely activated? */
-        /* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
-        WasapiFailed(_this, IAudioRenderClient_ReleaseBuffer(_this->hidden->render, _this->spec.samples, 0));
+    if (device->hidden->render != NULL) { // definitely activated?
+        // WasapiFailed() will mark the device for reacquisition or removal elsewhere.
+        WasapiFailed(device, IAudioRenderClient_ReleaseBuffer(device->hidden->render, device->sample_frames, 0));
     }
     }
 }
 }
 
 
-static void WASAPI_WaitDevice(SDL_AudioDevice *_this)
+static void WASAPI_WaitDevice(SDL_AudioDevice *device)
 {
 {
-    while (RecoverWasapiIfLost(_this) && _this->hidden->client && _this->hidden->event) {
-        DWORD waitResult = WaitForSingleObjectEx(_this->hidden->event, 200, FALSE);
+    while (RecoverWasapiIfLost(device) && device->hidden->client && device->hidden->event) {
+        DWORD waitResult = WaitForSingleObjectEx(device->hidden->event, 200, FALSE);
         if (waitResult == WAIT_OBJECT_0) {
         if (waitResult == WAIT_OBJECT_0) {
-            const UINT32 maxpadding = _this->spec.samples;
+            const UINT32 maxpadding = device->sample_frames;
             UINT32 padding = 0;
             UINT32 padding = 0;
-            if (!WasapiFailed(_this, IAudioClient_GetCurrentPadding(_this->hidden->client, &padding))) {
-                /*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
-                if (_this->iscapture) {
-                    if (padding > 0) {
-                        break;
-                    }
-                } else {
-                    if (padding <= maxpadding) {
-                        break;
-                    }
+            if (!WasapiFailed(device, IAudioClient_GetCurrentPadding(device->hidden->client, &padding))) {
+                //SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
+                if (device->iscapture && (padding > 0)) {
+                    break;
+                } else if (!device->iscapture && (padding <= maxpadding)) {
+                    break;
                 }
                 }
             }
             }
         } else if (waitResult != WAIT_TIMEOUT) {
         } else if (waitResult != WAIT_TIMEOUT) {
-            /*SDL_Log("WASAPI FAILED EVENT!");*/
-            IAudioClient_Stop(_this->hidden->client);
-            SDL_OpenedAudioDeviceDisconnected(_this);
+            //SDL_Log("WASAPI FAILED EVENT!");*/
+            IAudioClient_Stop(device->hidden->client);
+            WASAPI_DisconnectDevice(device);
         }
         }
     }
     }
 }
 }
 
 
-static int WASAPI_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
+static int WASAPI_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen)
 {
 {
-    SDL_AudioStream *stream = _this->hidden->capturestream;
-    const int avail = SDL_GetAudioStreamAvailable(stream);
-    if (avail > 0) {
-        const int cpy = SDL_min(buflen, avail);
-        SDL_GetAudioStreamData(stream, buffer, cpy);
-        return cpy;
-    }
-
-    while (RecoverWasapiIfLost(_this)) {
-        HRESULT ret;
-        BYTE *ptr = NULL;
-        UINT32 frames = 0;
-        DWORD flags = 0;
+    BYTE *ptr = NULL;
+    UINT32 frames = 0;
+    DWORD flags = 0;
 
 
-        /* uhoh, client isn't activated yet, just return silence. */
-        if (!_this->hidden->capture) {
-            /* Delay so we run at about the speed that audio would be arriving. */
-            SDL_Delay(((_this->spec.samples * 1000) / _this->spec.freq));
-            SDL_memset(buffer, _this->spec.silence, buflen);
-            return buflen;
+    while (RecoverWasapiIfLost(device) && device->hidden->capture) {
+        HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
+        if (ret == AUDCLNT_S_BUFFER_EMPTY) {
+            return 0;  // in theory we should have waited until there was data, but oh well, we'll go back to waiting. Returning 0 is safe in SDL
         }
         }
 
 
-        ret = IAudioCaptureClient_GetBuffer(_this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
-        if (ret != AUDCLNT_S_BUFFER_EMPTY) {
-            WasapiFailed(_this, ret); /* mark device lost/failed if necessary. */
-        }
+        WasapiFailed(device, ret); // mark device lost/failed if necessary.
 
 
-        if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
-            WASAPI_WaitDevice(_this);
-        } else if (ret == S_OK) {
-            const int total = ((int)frames) * _this->hidden->framesize;
+        if (ret == S_OK) {
+            const int total = ((int)frames) * device->hidden->framesize;
             const int cpy = SDL_min(buflen, total);
             const int cpy = SDL_min(buflen, total);
             const int leftover = total - cpy;
             const int leftover = total - cpy;
             const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
             const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
 
 
+            SDL_assert(leftover == 0);  // according to MSDN, this isn't everything available, just one "packet" of data per-GetBuffer call.
+
             if (silent) {
             if (silent) {
-                SDL_memset(buffer, _this->spec.silence, cpy);
+                SDL_memset(buffer, device->silence_value, cpy);
             } else {
             } else {
                 SDL_memcpy(buffer, ptr, cpy);
                 SDL_memcpy(buffer, ptr, cpy);
             }
             }
 
 
-            if (leftover > 0) {
-                ptr += cpy;
-                if (silent) {
-                    SDL_memset(ptr, _this->spec.silence, leftover); /* I guess this is safe? */
-                }
-
-                if (SDL_PutAudioStreamData(stream, ptr, leftover) == -1) {
-                    return -1; /* uhoh, out of memory, etc. Kill device.  :( */
-                }
-            }
-
-            ret = IAudioCaptureClient_ReleaseBuffer(_this->hidden->capture, frames);
-            WasapiFailed(_this, ret); /* mark device lost/failed if necessary. */
+            ret = IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames);
+            WasapiFailed(device, ret); // mark device lost/failed if necessary.
 
 
             return cpy;
             return cpy;
         }
         }
     }
     }
 
 
-    return -1; /* unrecoverable error. */
+    return -1; // unrecoverable error.
 }
 }
 
 
-static void WASAPI_FlushCapture(SDL_AudioDevice *_this)
+static void WASAPI_FlushCapture(SDL_AudioDevice *device)
 {
 {
     BYTE *ptr = NULL;
     BYTE *ptr = NULL;
     UINT32 frames = 0;
     UINT32 frames = 0;
     DWORD flags = 0;
     DWORD flags = 0;
 
 
-    if (!_this->hidden->capture) {
-        return; /* not activated yet? */
-    }
-
-    /* just read until we stop getting packets, throwing them away. */
-    while (SDL_TRUE) {
-        const HRESULT ret = IAudioCaptureClient_GetBuffer(_this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
+    // just read until we stop getting packets, throwing them away.
+    while (!SDL_AtomicGet(&device->shutdown) && device->hidden->capture) {
+        const HRESULT ret = IAudioCaptureClient_GetBuffer(device->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
         if (ret == AUDCLNT_S_BUFFER_EMPTY) {
         if (ret == AUDCLNT_S_BUFFER_EMPTY) {
-            break; /* no more buffered data; we're done. */
-        } else if (WasapiFailed(_this, ret)) {
-            break; /* failed for some other reason, abort. */
-        } else if (WasapiFailed(_this, IAudioCaptureClient_ReleaseBuffer(_this->hidden->capture, frames))) {
-            break; /* something broke. */
+            break; // no more buffered data; we're done.
+        } else if (WasapiFailed(device, ret)) {
+            break; // failed for some other reason, abort.
+        } else if (WasapiFailed(device, IAudioCaptureClient_ReleaseBuffer(device->hidden->capture, frames))) {
+            break; // something broke.
         }
         }
     }
     }
-    SDL_ClearAudioStream(_this->hidden->capturestream);
 }
 }
 
 
-static void ReleaseWasapiDevice(SDL_AudioDevice *_this)
+static void WASAPI_CloseDevice(SDL_AudioDevice *device)
 {
 {
-    if (_this->hidden->client) {
-        IAudioClient_Stop(_this->hidden->client);
-        IAudioClient_Release(_this->hidden->client);
-        _this->hidden->client = NULL;
-    }
-
-    if (_this->hidden->render) {
-        IAudioRenderClient_Release(_this->hidden->render);
-        _this->hidden->render = NULL;
+    if (device->hidden) {
+        ResetWasapiDevice(device);
+        SDL_free(device->hidden->devid);
+        SDL_free(device->hidden);
+        device->hidden = NULL;
     }
     }
-
-    if (_this->hidden->capture) {
-        IAudioCaptureClient_Release(_this->hidden->capture);
-        _this->hidden->capture = NULL;
-    }
-
-    if (_this->hidden->waveformat) {
-        CoTaskMemFree(_this->hidden->waveformat);
-        _this->hidden->waveformat = NULL;
-    }
-
-    if (_this->hidden->capturestream) {
-        SDL_DestroyAudioStream(_this->hidden->capturestream);
-        _this->hidden->capturestream = NULL;
-    }
-
-    if (_this->hidden->activation_handler) {
-        WASAPI_PlatformDeleteActivationHandler(_this->hidden->activation_handler);
-        _this->hidden->activation_handler = NULL;
-    }
-
-    if (_this->hidden->event) {
-        CloseHandle(_this->hidden->event);
-        _this->hidden->event = NULL;
-    }
-}
-
-static void WASAPI_CloseDevice(SDL_AudioDevice *_this)
-{
-    WASAPI_UnrefDevice(_this);
-}
-
-void WASAPI_RefDevice(SDL_AudioDevice *_this)
-{
-    SDL_AtomicIncRef(&_this->hidden->refcount);
 }
 }
 
 
-void WASAPI_UnrefDevice(SDL_AudioDevice *_this)
+static int mgmtthrtask_PrepDevice(void *userdata)
 {
 {
-    if (!SDL_AtomicDecRef(&_this->hidden->refcount)) {
-        return;
-    }
-
-    /* actual closing happens here. */
+    SDL_AudioDevice *device = (SDL_AudioDevice *)userdata;
 
 
-    /* don't touch _this->hidden->task in here; it has to be reverted from
-       our callback thread. We do that in WASAPI_ThreadDeinit().
-       (likewise for _this->hidden->coinitialized). */
-    ReleaseWasapiDevice(_this);
-    SDL_free(_this->hidden->devid);
-    SDL_free(_this->hidden);
-}
-
-/* This is called once a device is activated, possibly asynchronously. */
-int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream)
-{
     /* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
     /* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
        !!!  it will write into the kernel's audio buffer directly instead of
        !!!  it will write into the kernel's audio buffer directly instead of
        !!!  shared memory that a user-mode mixer then writes to the kernel with
        !!!  shared memory that a user-mode mixer then writes to the kernel with
@@ -387,49 +526,42 @@ int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream)
        !!!  wins actually look like. Maybe add a hint to force exclusive mode at
        !!!  wins actually look like. Maybe add a hint to force exclusive mode at
        !!!  some point. To be sure, defaulting to shared mode is the right thing to
        !!!  some point. To be sure, defaulting to shared mode is the right thing to
        !!!  do in any case. */
        !!!  do in any case. */
-    const SDL_AudioSpec oldspec = _this->spec;
     const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
     const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
-    UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
-    REFERENCE_TIME default_period = 0;
-    IAudioClient *client = _this->hidden->client;
-    IAudioRenderClient *render = NULL;
-    IAudioCaptureClient *capture = NULL;
-    WAVEFORMATEX *waveformat = NULL;
-    SDL_AudioFormat test_format;
-    SDL_AudioFormat wasapi_format = 0;
-    const SDL_AudioFormat *closefmts;
-    HRESULT ret = S_OK;
-    DWORD streamflags = 0;
 
 
+    IAudioClient *client = device->hidden->client;
     SDL_assert(client != NULL);
     SDL_assert(client != NULL);
 
 
-#if defined(__WINRT__) || defined(__GDK__) /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
-    _this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
+#if defined(__WINRT__) || defined(__GDK__) // CreateEventEx() arrived in Vista, so we need an #ifdef for XP.
+    device->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
 #else
 #else
-    _this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
+    device->hidden->event = CreateEventW(NULL, 0, 0, NULL);
 #endif
 #endif
 
 
-    if (_this->hidden->event == NULL) {
+    if (device->hidden->event == NULL) {
         return WIN_SetError("WASAPI can't create an event handle");
         return WIN_SetError("WASAPI can't create an event handle");
     }
     }
 
 
+    HRESULT ret;
+
+    WAVEFORMATEX *waveformat = NULL;
     ret = IAudioClient_GetMixFormat(client, &waveformat);
     ret = IAudioClient_GetMixFormat(client, &waveformat);
     if (FAILED(ret)) {
     if (FAILED(ret)) {
         return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
     }
     }
-
     SDL_assert(waveformat != NULL);
     SDL_assert(waveformat != NULL);
-    _this->hidden->waveformat = waveformat;
+    device->hidden->waveformat = waveformat;
 
 
-    _this->spec.channels = (Uint8)waveformat->nChannels;
+    SDL_AudioSpec newspec;
+    newspec.channels = (Uint8)waveformat->nChannels;
 
 
-    /* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
-    wasapi_format = WaveFormatToSDLFormat(waveformat);
+    // Make sure we have a valid format that we can convert to whatever WASAPI wants.
+    const SDL_AudioFormat wasapi_format = SDL_WaveFormatExToSDLFormat(waveformat);
 
 
-    closefmts = SDL_ClosestAudioFormats(_this->spec.format);
+    SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
     while ((test_format = *(closefmts++)) != 0) {
     while ((test_format = *(closefmts++)) != 0) {
         if (test_format == wasapi_format) {
         if (test_format == wasapi_format) {
-            _this->spec.format = test_format;
+            newspec.format = test_format;
             break;
             break;
         }
         }
     }
     }
@@ -438,36 +570,40 @@ int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream)
         return SDL_SetError("%s: Unsupported audio format", "wasapi");
         return SDL_SetError("%s: Unsupported audio format", "wasapi");
     }
     }
 
 
+    REFERENCE_TIME default_period = 0;
     ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
     ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
     if (FAILED(ret)) {
     if (FAILED(ret)) {
         return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
     }
     }
 
 
+    DWORD streamflags = 0;
+
     /* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term
     /* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term
        it fixes some other WASAPI-specific quirks we haven't quite tracked down.
        it fixes some other WASAPI-specific quirks we haven't quite tracked down.
        Refer to bug #6326 for the immediate concern. */
        Refer to bug #6326 for the immediate concern. */
-#if 0
-    _this->spec.freq = waveformat->nSamplesPerSec;  /* force sampling rate so our resampler kicks in, if necessary. */
-#else
-    /* favor WASAPI's resampler over our own */
-    if ((DWORD)_this->spec.freq != waveformat->nSamplesPerSec) {
+#if 1
+    // favor WASAPI's resampler over our own
+    if ((DWORD)device->spec.freq != waveformat->nSamplesPerSec) {
         streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
         streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
-        waveformat->nSamplesPerSec = _this->spec.freq;
+        waveformat->nSamplesPerSec = device->spec.freq;
         waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
         waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
     }
     }
 #endif
 #endif
 
 
+    newspec.freq = waveformat->nSamplesPerSec;
+
     streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
     streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
     ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
     ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
     if (FAILED(ret)) {
     if (FAILED(ret)) {
         return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
     }
     }
 
 
-    ret = IAudioClient_SetEventHandle(client, _this->hidden->event);
+    ret = IAudioClient_SetEventHandle(client, device->hidden->event);
     if (FAILED(ret)) {
     if (FAILED(ret)) {
         return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
     }
     }
 
 
+    UINT32 bufsize = 0; // this is in sample frames, not samples, not bytes.
     ret = IAudioClient_GetBufferSize(client, &bufsize);
     ret = IAudioClient_GetBufferSize(client, &bufsize);
     if (FAILED(ret)) {
     if (FAILED(ret)) {
         return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
@@ -475,116 +611,108 @@ int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream)
 
 
     /* Match the callback size to the period size to cut down on the number of
     /* Match the callback size to the period size to cut down on the number of
        interrupts waited for in each call to WaitDevice */
        interrupts waited for in each call to WaitDevice */
-    {
-        const float period_millis = default_period / 10000.0f;
-        const float period_frames = period_millis * _this->spec.freq / 1000.0f;
-        _this->spec.samples = (Uint16)SDL_ceilf(period_frames);
-    }
-
-    /* Update the fragment size as size in bytes */
-    SDL_CalculateAudioSpec(&_this->spec);
+    const float period_millis = default_period / 10000.0f;
+    const float period_frames = period_millis * newspec.freq / 1000.0f;
+    const int new_sample_frames = (int) SDL_ceilf(period_frames);
 
 
-    _this->hidden->framesize = (SDL_AUDIO_BITSIZE(_this->spec.format) / 8) * _this->spec.channels;
+    // Update the fragment size as size in bytes
+    if (SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames) < 0) {
+        return -1;
+    }
 
 
-    if (_this->iscapture) {
-        _this->hidden->capturestream = SDL_CreateAudioStream(_this->spec.format, _this->spec.channels, _this->spec.freq, _this->spec.format, _this->spec.channels, _this->spec.freq);
-        if (!_this->hidden->capturestream) {
-            return -1; /* already set SDL_Error */
-        }
+    device->hidden->framesize = (SDL_AUDIO_BITSIZE(device->spec.format) / 8) * device->spec.channels;
 
 
+    if (device->iscapture) {
+        IAudioCaptureClient *capture = NULL;
         ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture);
         ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture);
         if (FAILED(ret)) {
         if (FAILED(ret)) {
             return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
             return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
         }
         }
 
 
         SDL_assert(capture != NULL);
         SDL_assert(capture != NULL);
-        _this->hidden->capture = capture;
+        device->hidden->capture = capture;
         ret = IAudioClient_Start(client);
         ret = IAudioClient_Start(client);
         if (FAILED(ret)) {
         if (FAILED(ret)) {
             return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
             return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
         }
         }
 
 
-        WASAPI_FlushCapture(_this); /* MSDN says you should flush capture endpoint right after startup. */
+        WASAPI_FlushCapture(device); // MSDN says you should flush capture endpoint right after startup.
     } else {
     } else {
+        IAudioRenderClient *render = NULL;
         ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render);
         ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render);
         if (FAILED(ret)) {
         if (FAILED(ret)) {
             return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
             return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
         }
         }
 
 
         SDL_assert(render != NULL);
         SDL_assert(render != NULL);
-        _this->hidden->render = render;
+        device->hidden->render = render;
         ret = IAudioClient_Start(client);
         ret = IAudioClient_Start(client);
         if (FAILED(ret)) {
         if (FAILED(ret)) {
             return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
             return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
         }
         }
     }
     }
 
 
-    if (updatestream) {
-        return UpdateAudioStream(_this, &oldspec);
-    }
-
-    return 0; /* good to go. */
+    return 0; // good to go.
 }
 }
 
 
-static int WASAPI_OpenDevice(SDL_AudioDevice *_this, const char *devname)
+// This is called once a device is activated, possibly asynchronously.
+int WASAPI_PrepDevice(SDL_AudioDevice *device)
 {
 {
-    LPCWSTR devid = (LPCWSTR)_this->handle;
+    int rc;
+    return (WASAPI_ProxyToManagementThread(mgmtthrtask_PrepDevice, device, &rc) < 0) ? -1 : rc;
+}
 
 
-    /* Initialize all variables that we clean on shutdown */
-    _this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
-    if (_this->hidden == NULL) {
+static int WASAPI_OpenDevice(SDL_AudioDevice *device)
+{
+    // Initialize all variables that we clean on shutdown
+    device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden));
+    if (device->hidden == NULL) {
         return SDL_OutOfMemory();
         return SDL_OutOfMemory();
-    }
-    SDL_zerop(_this->hidden);
-
-    WASAPI_RefDevice(_this); /* so CloseDevice() will unref to zero. */
-
-    if (!devid) { /* is default device? */
-        _this->hidden->default_device_generation = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
-    } else {
-        _this->hidden->devid = SDL_wcsdup(devid);
-        if (!_this->hidden->devid) {
-            return SDL_OutOfMemory();
-        }
-    }
-
-    if (WASAPI_ActivateDevice(_this, SDL_FALSE) == -1) {
-        return -1; /* already set error. */
+    } else if (ActivateWasapiDevice(device) < 0) {
+        return -1; // already set error.
     }
     }
 
 
-    /* Ready, but waiting for async device activation.
+    /* Ready, but possibly waiting for async device activation.
        Until activation is successful, we will report silence from capture
        Until activation is successful, we will report silence from capture
-       devices and ignore data on playback devices.
-       Also, since we don't know the _actual_ device format until after
-       activation, we let the app have whatever it asks for. We set up
-       an SDL_AudioStream to convert, if necessary, once the activation
-       completes. */
+       devices and ignore data on playback devices. Upon activation, we'll make
+       sure any bound audio streams are adjusted for the final device format. */
 
 
     return 0;
     return 0;
 }
 }
 
 
-static void WASAPI_ThreadInit(SDL_AudioDevice *_this)
+static void WASAPI_ThreadInit(SDL_AudioDevice *device)
+{
+    WASAPI_PlatformThreadInit(device);
+}
+
+static void WASAPI_ThreadDeinit(SDL_AudioDevice *device)
+{
+    WASAPI_PlatformThreadDeinit(device);
+}
+
+static int mgmtthrtask_FreeDeviceHandle(void *userdata)
 {
 {
-    WASAPI_PlatformThreadInit(_this);
+    WASAPI_PlatformFreeDeviceHandle((SDL_AudioDevice *)userdata);
+    return 0;
 }
 }
 
 
-static void WASAPI_ThreadDeinit(SDL_AudioDevice *_this)
+static void WASAPI_FreeDeviceHandle(SDL_AudioDevice *device)
 {
 {
-    WASAPI_PlatformThreadDeinit(_this);
+    int rc;
+    WASAPI_ProxyToManagementThread(mgmtthrtask_FreeDeviceHandle, device, &rc);
 }
 }
 
 
 static void WASAPI_Deinitialize(void)
 static void WASAPI_Deinitialize(void)
 {
 {
-    WASAPI_PlatformDeinit();
+    DeinitManagementThread();
 }
 }
 
 
 static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
 static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
 {
 {
-    if (WASAPI_PlatformInit() == -1) {
+    if (InitManagementThread() < 0) {
         return SDL_FALSE;
         return SDL_FALSE;
     }
     }
 
 
-    /* Set the function pointers */
     impl->DetectDevices = WASAPI_DetectDevices;
     impl->DetectDevices = WASAPI_DetectDevices;
     impl->ThreadInit = WASAPI_ThreadInit;
     impl->ThreadInit = WASAPI_ThreadInit;
     impl->ThreadDeinit = WASAPI_ThreadDeinit;
     impl->ThreadDeinit = WASAPI_ThreadDeinit;
@@ -592,18 +720,20 @@ static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
     impl->PlayDevice = WASAPI_PlayDevice;
     impl->PlayDevice = WASAPI_PlayDevice;
     impl->WaitDevice = WASAPI_WaitDevice;
     impl->WaitDevice = WASAPI_WaitDevice;
     impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
     impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
+    impl->WaitCaptureDevice = WASAPI_WaitDevice;
     impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
     impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
     impl->FlushCapture = WASAPI_FlushCapture;
     impl->FlushCapture = WASAPI_FlushCapture;
     impl->CloseDevice = WASAPI_CloseDevice;
     impl->CloseDevice = WASAPI_CloseDevice;
     impl->Deinitialize = WASAPI_Deinitialize;
     impl->Deinitialize = WASAPI_Deinitialize;
-    impl->GetDefaultAudioInfo = WASAPI_GetDefaultAudioInfo;
+    impl->FreeDeviceHandle = WASAPI_FreeDeviceHandle;
+
     impl->HasCaptureSupport = SDL_TRUE;
     impl->HasCaptureSupport = SDL_TRUE;
 
 
-    return SDL_TRUE; /* this audio target is available. */
+    return SDL_TRUE;
 }
 }
 
 
 AudioBootStrap WASAPI_bootstrap = {
 AudioBootStrap WASAPI_bootstrap = {
     "wasapi", "WASAPI", WASAPI_Init, SDL_FALSE
     "wasapi", "WASAPI", WASAPI_Init, SDL_FALSE
 };
 };
 
 
-#endif /* SDL_AUDIO_DRIVER_WASAPI */
+#endif // SDL_AUDIO_DRIVER_WASAPI

+ 13 - 12
src/audio/wasapi/SDL_wasapi.h

@@ -31,37 +31,38 @@ extern "C" {
 
 
 struct SDL_PrivateAudioData
 struct SDL_PrivateAudioData
 {
 {
-    SDL_AtomicInt refcount;
     WCHAR *devid;
     WCHAR *devid;
     WAVEFORMATEX *waveformat;
     WAVEFORMATEX *waveformat;
     IAudioClient *client;
     IAudioClient *client;
     IAudioRenderClient *render;
     IAudioRenderClient *render;
     IAudioCaptureClient *capture;
     IAudioCaptureClient *capture;
-    SDL_AudioStream *capturestream;
     HANDLE event;
     HANDLE event;
     HANDLE task;
     HANDLE task;
     SDL_bool coinitialized;
     SDL_bool coinitialized;
     int framesize;
     int framesize;
-    int default_device_generation;
     SDL_bool device_lost;
     SDL_bool device_lost;
     void *activation_handler;
     void *activation_handler;
-    SDL_AtomicInt just_activated;
 };
 };
 
 
 /* win32 and winrt implementations call into these. */
 /* win32 and winrt implementations call into these. */
-int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream);
-void WASAPI_RefDevice(SDL_AudioDevice *_this);
-void WASAPI_UnrefDevice(SDL_AudioDevice *_this);
+int WASAPI_PrepDevice(SDL_AudioDevice *device);
+void WASAPI_DisconnectDevice(SDL_AudioDevice *device);
+
+
+// BE CAREFUL: if you are holding the device lock and proxy to the management thread with wait_until_complete, and grab the lock again, you will deadlock.
+typedef int (*ManagementThreadTask)(void *userdata);
+int WASAPI_ProxyToManagementThread(ManagementThreadTask task, void *userdata, int *wait_until_complete);
 
 
 /* These are functions that are implemented differently for Windows vs WinRT. */
 /* These are functions that are implemented differently for Windows vs WinRT. */
+// UNLESS OTHERWISE NOTED THESE ALL HAPPEN ON THE MANAGEMENT THREAD.
 int WASAPI_PlatformInit(void);
 int WASAPI_PlatformInit(void);
 void WASAPI_PlatformDeinit(void);
 void WASAPI_PlatformDeinit(void);
-void WASAPI_EnumerateEndpoints(void);
-int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
-int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery);
-void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this);
-void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this);
+void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture);
+int 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_PlatformDeleteActivationHandler(void *handler);
 void WASAPI_PlatformDeleteActivationHandler(void *handler);
+void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 28 - 30
src/audio/wasapi/SDL_wasapi_win32.c

@@ -49,7 +49,7 @@ static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x
 
 
 int WASAPI_PlatformInit(void)
 int WASAPI_PlatformInit(void)
 {
 {
-    if (SDL_IMMDevice_Init() < 0) {
+    if (SDL_IMMDevice_Init() < 0) {   // this will call WIN_CoInitialize for us!
         return -1; /* This is set by SDL_IMMDevice_Init */
         return -1; /* This is set by SDL_IMMDevice_Init */
     }
     }
 
 
@@ -72,72 +72,65 @@ void WASAPI_PlatformDeinit(void)
     pAvSetMmThreadCharacteristicsW = NULL;
     pAvSetMmThreadCharacteristicsW = NULL;
     pAvRevertMmThreadCharacteristics = NULL;
     pAvRevertMmThreadCharacteristics = NULL;
 
 
-    SDL_IMMDevice_Quit();
+    SDL_IMMDevice_Quit();  // This will call WIN_CoUninitialize for us!
 }
 }
 
 
-void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this)
+void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
 {
 {
     /* this thread uses COM. */
     /* this thread uses COM. */
     if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */
     if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */
-        _this->hidden->coinitialized = SDL_TRUE;
+        device->hidden->coinitialized = SDL_TRUE;
     }
     }
 
 
     /* Set this thread to very high "Pro Audio" priority. */
     /* Set this thread to very high "Pro Audio" priority. */
     if (pAvSetMmThreadCharacteristicsW) {
     if (pAvSetMmThreadCharacteristicsW) {
         DWORD idx = 0;
         DWORD idx = 0;
-        _this->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
+        device->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
     }
     }
 }
 }
 
 
-void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this)
+void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
 {
 {
     /* Set this thread back to normal priority. */
     /* Set this thread back to normal priority. */
-    if (_this->hidden->task && pAvRevertMmThreadCharacteristics) {
-        pAvRevertMmThreadCharacteristics(_this->hidden->task);
-        _this->hidden->task = NULL;
+    if (device->hidden->task && pAvRevertMmThreadCharacteristics) {
+        pAvRevertMmThreadCharacteristics(device->hidden->task);
+        device->hidden->task = NULL;
     }
     }
 
 
-    if (_this->hidden->coinitialized) {
+    if (device->hidden->coinitialized) {
         WIN_CoUninitialize();
         WIN_CoUninitialize();
-        _this->hidden->coinitialized = SDL_FALSE;
+        device->hidden->coinitialized = SDL_FALSE;
     }
     }
 }
 }
 
 
-int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
+int WASAPI_ActivateDevice(SDL_AudioDevice *device)
 {
 {
-    IMMDevice *device = NULL;
-    HRESULT ret;
-
-    if (SDL_IMMDevice_Get(_this->hidden->devid, &device, _this->iscapture) < 0) {
-        _this->hidden->client = NULL;
+    IMMDevice *immdevice = NULL;
+    if (SDL_IMMDevice_Get(device, &immdevice, device->iscapture) < 0) {
+        device->hidden->client = NULL;
         return -1; /* This is already set by SDL_IMMDevice_Get */
         return -1; /* This is already set by SDL_IMMDevice_Get */
     }
     }
 
 
-    /* this is not async in standard win32, yay! */
-    ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&_this->hidden->client);
-    IMMDevice_Release(device);
+    /* 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)) {
     if (FAILED(ret)) {
-        SDL_assert(_this->hidden->client == NULL);
+        SDL_assert(device->hidden->client == NULL);
         return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
     }
     }
 
 
-    SDL_assert(_this->hidden->client != NULL);
-    if (WASAPI_PrepDevice(_this, isrecovery) == -1) { /* not async, fire it right away. */
+    SDL_assert(device->hidden->client != NULL);
+    if (WASAPI_PrepDevice(device) == -1) { /* not async, fire it right away. */
         return -1;
         return -1;
     }
     }
 
 
     return 0; /* good to go. */
     return 0; /* good to go. */
 }
 }
 
 
-void WASAPI_EnumerateEndpoints(void)
-{
-    SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE);
-}
-
-int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
+void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
 {
 {
-    return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
+    SDL_IMMDevice_EnumerateEndpoints(default_output, default_capture);
 }
 }
 
 
 void WASAPI_PlatformDeleteActivationHandler(void *handler)
 void WASAPI_PlatformDeleteActivationHandler(void *handler)
@@ -146,4 +139,9 @@ void WASAPI_PlatformDeleteActivationHandler(void *handler)
     SDL_assert(!"This function should have only been called on WinRT.");
     SDL_assert(!"This function should have only been called on WinRT.");
 }
 }
 
 
+void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
+{
+    SDL_IMMDevice_FreeDeviceHandle(device);
+}
+
 #endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */
 #endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */

+ 92 - 170
src/audio/wasapi/SDL_wasapi_winrt.cpp

@@ -53,21 +53,16 @@ using namespace Microsoft::WRL;
 
 
 static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
 static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
 
 
-static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid);
-static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid);
-extern "C" {
-SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
-SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
-}
 
 
-/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
-typedef struct DevIdList
+static SDL_bool FindWinRTAudioDeviceCallback(SDL_AudioDevice *device, void *userdata)
 {
 {
-    WCHAR *str;
-    struct DevIdList *next;
-} DevIdList;
+    return (SDL_wcscmp((LPCWSTR) device->handle, (LPCWSTR) userdata) == 0) ? SDL_TRUE : SDL_FALSE;
+}
 
 
-static DevIdList *deviceid_list = NULL;
+static SDL_AudioDevice *FindWinRTAudioDevice(LPCWSTR devid)
+{
+    return SDL_FindPhysicalAudioDeviceByCallback(FindWinRTAudioDeviceCallback, (void *) devid);
+}
 
 
 class SDL_WasapiDeviceEventHandler
 class SDL_WasapiDeviceEventHandler
 {
 {
@@ -80,9 +75,10 @@ class SDL_WasapiDeviceEventHandler
     void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
     void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
     void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
     void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
     void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
     void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
-    SDL_Semaphore *completed;
+    void WaitForCompletion();
 
 
   private:
   private:
+    SDL_Semaphore *completed_semaphore;
     const SDL_bool iscapture;
     const SDL_bool iscapture;
     DeviceWatcher ^ watcher;
     DeviceWatcher ^ watcher;
     Windows::Foundation::EventRegistrationToken added_handler;
     Windows::Foundation::EventRegistrationToken added_handler;
@@ -93,10 +89,11 @@ class SDL_WasapiDeviceEventHandler
 };
 };
 
 
 SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
 SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
-    : iscapture(_iscapture), completed(SDL_CreateSemaphore(0))
+    : iscapture(_iscapture), completed_semaphore(SDL_CreateSemaphore(0))
 {
 {
-    if (!completed)
+    if (!completed_semaphore) {
         return; // uhoh.
         return; // uhoh.
+    }
 
 
     Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
     Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
     Platform::Collections::Vector<Platform::String ^> properties;
     Platform::Collections::Vector<Platform::String ^> properties;
@@ -128,9 +125,10 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
         watcher->Stop();
         watcher->Stop();
         watcher = nullptr;
         watcher = nullptr;
     }
     }
-    if (completed) {
-        SDL_DestroySemaphore(completed);
-        completed = nullptr;
+
+    if (completed_semaphore) {
+        SDL_DestroySemaphore(completed_semaphore);
+        completed_semaphore = nullptr;
     }
     }
 
 
     if (iscapture) {
     if (iscapture) {
@@ -142,21 +140,34 @@ SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
 
 
 void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
 void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
 {
 {
+    /* 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...?) */
+
     SDL_assert(sender == this->watcher);
     SDL_assert(sender == this->watcher);
     char *utf8dev = WIN_StringToUTF8(info->Name->Data());
     char *utf8dev = WIN_StringToUTF8(info->Name->Data());
     if (utf8dev) {
     if (utf8dev) {
-        WAVEFORMATEXTENSIBLE fmt;
+        SDL_AudioSpec spec;
+        SDL_zero(spec);
+
         Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
         Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
         if (obj) {
         if (obj) {
             IPropertyValue ^ property = (IPropertyValue ^) obj;
             IPropertyValue ^ property = (IPropertyValue ^) obj;
             Platform::Array<unsigned char> ^ data;
             Platform::Array<unsigned char> ^ data;
             property->GetUInt8Array(&data);
             property->GetUInt8Array(&data);
-            SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
-        } else {
+            WAVEFORMATEXTENSIBLE fmt;
             SDL_zero(fmt);
             SDL_zero(fmt);
+            SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
+            spec.channels = (Uint8)fmt->Format.nChannels;
+            spec.freq = fmt->Format.nSamplesPerSec;
+            spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
         }
         }
 
 
-        WASAPI_AddDevice(this->iscapture, utf8dev, &fmt, info->Id->Data());
+        LPWSTR devid = SDL_wcsdup(devid);
+        if (devid) {
+            SDL_AddAudioDevice(this->iscapture, utf8dev, spec.channels ? &spec : NULL, devid);
+        }
         SDL_free(utf8dev);
         SDL_free(utf8dev);
     }
     }
 }
 }
@@ -164,7 +175,7 @@ void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceI
 void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
 void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
 {
 {
     SDL_assert(sender == this->watcher);
     SDL_assert(sender == this->watcher);
-    WASAPI_RemoveDevice(this->iscapture, info->Id->Data());
+    WASAPI_DisconnectDevice(FindWinRTAudioDevice(info->Id->Data()));
 }
 }
 
 
 void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
 void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
@@ -175,19 +186,30 @@ void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, Devic
 void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
 void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
 {
 {
     SDL_assert(sender == this->watcher);
     SDL_assert(sender == this->watcher);
-    SDL_PostSemaphore(this->completed);
+    if (this->completed_semaphore) {
+        SDL_PostSemaphore(this->completed_semaphore);
+    }
 }
 }
 
 
 void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
 void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
 {
 {
     SDL_assert(!this->iscapture);
     SDL_assert(!this->iscapture);
-    SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
+    SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
 }
 }
 
 
 void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
 void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
 {
 {
     SDL_assert(this->iscapture);
     SDL_assert(this->iscapture);
-    SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
+    SDL_DefaultAudioDeviceChanged(FindWinRTAudioDevice(args->Id->Data()));
+}
+
+void SDL_WasapiDeviceEventHandler::WaitForCompletion()
+{
+    if (this->completed_semaphore) {
+        SDL_WaitSemaphore(this->completed_semaphore);
+        SDL_DestroySemaphore(this->completed_semaphore);
+        this->completed_semaphore = nullptr;
+    }
 }
 }
 
 
 static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
 static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
@@ -195,54 +217,63 @@ static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
 
 
 int WASAPI_PlatformInit(void)
 int WASAPI_PlatformInit(void)
 {
 {
-    SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
-    SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
     return 0;
     return 0;
 }
 }
 
 
 void WASAPI_PlatformDeinit(void)
 void WASAPI_PlatformDeinit(void)
 {
 {
-    DevIdList *devidlist;
-    DevIdList *next;
-
     delete playback_device_event_handler;
     delete playback_device_event_handler;
     playback_device_event_handler = nullptr;
     playback_device_event_handler = nullptr;
     delete capture_device_event_handler;
     delete capture_device_event_handler;
     capture_device_event_handler = nullptr;
     capture_device_event_handler = nullptr;
-
-    for (devidlist = deviceid_list; devidlist; devidlist = next) {
-        next = devidlist->next;
-        SDL_free(devidlist->str);
-        SDL_free(devidlist);
-    }
-    deviceid_list = NULL;
 }
 }
 
 
-void WASAPI_EnumerateEndpoints(void)
+void WASAPI_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
 {
 {
+    Platform::String ^ defdevid;
+
     // DeviceWatchers will fire an Added event for each existing device at
     // DeviceWatchers will fire an Added event for each existing device at
     //  startup, so we don't need to enumerate them separately before
     //  startup, so we don't need to enumerate them separately before
     //  listening for updates.
     //  listening for updates.
     playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
     playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
+    playback_device_event_handler->WaitForCompletion();
+    defdevid = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
+    if (defdevid) {
+        *default_output = FindWinRTAudioDevice(defdevid->Data());
+    }
+
     capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
     capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
-    SDL_WaitSemaphore(playback_device_event_handler->completed);
-    SDL_WaitSemaphore(capture_device_event_handler->completed);
+    capture_device_event_handler->WaitForCompletion();
+    defdevid = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default)
+    if (defdevid) {
+        *default_capture = FindWinRTAudioDevice(defdevid->Data());
+    }
 }
 }
 
 
-struct SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
+class SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
 {
 {
-    SDL_WasapiActivationHandler() : device(nullptr) {}
-    STDMETHOD(ActivateCompleted)
-    (IActivateAudioInterfaceAsyncOperation *operation);
-    SDL_AudioDevice *device;
+public:
+    SDL_WasapiActivationHandler() : completion_semaphore(SDL_CreateSemaphore(0)) { SDL_assert(completion_semaphore != NULL); }
+    STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
+    void WaitForCompletion();
+private:
+    SDL_Semaphore *completion_semaphore;
 };
 };
 
 
+void SDL_WasapiActivationHandler::WaitForCompletion()
+{
+    if (completion_semaphore) {
+        SDL_WaitSemaphore(completion_semaphore);
+        SDL_DestroySemaphore(completion_semaphore);
+        completion_semaphore = NULL;
+    }
+}
+
 HRESULT
 HRESULT
 SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
 SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
 {
 {
     // 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.
     // 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.
-    SDL_AtomicSet(&device->hidden->just_activated, 1);
-    WASAPI_UnrefDevice(device);
+    SDL_PostSemaphore(completion_semaphore);
     return S_OK;
     return S_OK;
 }
 }
 
 
@@ -251,24 +282,10 @@ void WASAPI_PlatformDeleteActivationHandler(void *handler)
     ((SDL_WasapiActivationHandler *)handler)->Release();
     ((SDL_WasapiActivationHandler *)handler)->Release();
 }
 }
 
 
-int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
+int WASAPI_ActivateDevice(SDL_AudioDevice *device)
 {
 {
-    return SDL_Unsupported();
-}
-
-int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
-{
-    LPCWSTR devid = _this->hidden->devid;
-    Platform::String ^ defdevid;
-
-    if (devid == nullptr) {
-        defdevid = _this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
-        if (defdevid) {
-            devid = defdevid->Data();
-        }
-    }
-
-    SDL_AtomicSet(&_this->hidden->just_activated, 0);
+    LPCWSTR devid = (LPCWSTR) device->handle;
+    SDL_assert(devid != NULL);
 
 
     ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
     ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
     if (handler == nullptr) {
     if (handler == nullptr) {
@@ -276,10 +293,8 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
     }
     }
 
 
     handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc.
     handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc.
-    handler.Get()->device = _this;
-    _this->hidden->activation_handler = handler.Get();
+    device->hidden->activation_handler = handler.Get();
 
 
-    WASAPI_RefDevice(_this); /* completion handler will unref it. */
     IActivateAudioInterfaceAsyncOperation *async = nullptr;
     IActivateAudioInterfaceAsyncOperation *async = nullptr;
     const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
     const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
 
 
@@ -288,21 +303,11 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
             async->Release();
             async->Release();
         }
         }
         handler.Get()->Release();
         handler.Get()->Release();
-        WASAPI_UnrefDevice(_this);
         return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
         return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
     }
     }
 
 
-    /* Spin until the async operation is complete.
-     * If we don't PrepDevice before leaving this function, the bug list gets LONG:
-     * - device.spec is not filled with the correct information
-     * - The 'obtained' spec will be wrong for ALLOW_CHANGE properties
-     * - SDL_AudioStreams will/will not be allocated at the right time
-     * - SDL_assert(device->callbackspec.size == device->spec.size) will fail
-     * - When the assert is ignored, skipping or a buffer overflow will occur
-     */
-    while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
-        SDL_Delay(1);
-    }
+    // !!! FIXME: the problems in SDL2 that needed this to be synchronous are _probably_ solved by SDL3, and this can block indefinitely if a user prompt is shown to get permission to use a microphone.
+    handler.WaitForCompletion();  // block here until we have an answer, so this is synchronous to us after all.
 
 
     HRESULT activateRes = S_OK;
     HRESULT activateRes = S_OK;
     IUnknown *iunknown = nullptr;
     IUnknown *iunknown = nullptr;
@@ -314,114 +319,31 @@ int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
         return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
         return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
     }
     }
 
 
-    iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
-    if (!_this->hidden->client) {
+    iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
+    if (!device->hidden->client) {
         return SDL_SetError("Failed to query WASAPI client interface");
         return SDL_SetError("Failed to query WASAPI client interface");
     }
     }
 
 
-    if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
+    if (WASAPI_PrepDevice(device) == -1) {
         return -1;
         return -1;
     }
     }
 
 
     return 0;
     return 0;
 }
 }
 
 
-void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this)
+void WASAPI_PlatformThreadInit(SDL_AudioDevice *device)
 {
 {
     // !!! FIXME: set this thread to "Pro Audio" priority.
     // !!! FIXME: set this thread to "Pro Audio" priority.
 }
 }
 
 
-void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this)
+void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *device)
 {
 {
     // !!! FIXME: set this thread to "Pro Audio" priority.
     // !!! FIXME: set this thread to "Pro Audio" priority.
 }
 }
 
 
-/* Everything below was copied from SDL_wasapi.c, before it got moved to SDL_immdevice.c! */
-
-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 } };
-
-extern "C" 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;
-}
-
-static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
-{
-    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, i->str);
-            SDL_free(i->str);
-            SDL_free(i);
-        } else {
-            prev = i;
-        }
-    }
-}
-
-static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid)
+void WASAPI_PlatformFreeDeviceHandle(SDL_AudioDevice *device)
 {
 {
-    DevIdList *devidlist;
-    SDL_AudioSpec spec;
-
-    /* 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. */
-        }
-    }
-
-    devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist));
-    if (devidlist == NULL) {
-        return; /* oh well. */
-    }
-
-    devid = SDL_wcsdup(devid);
-    if (!devid) {
-        SDL_free(devidlist);
-        return; /* oh well. */
-    }
-
-    devidlist->str = (WCHAR *)devid;
-    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, (void *)devid);
+    SDL_free(device->handle);
 }
 }
 
 
 #endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
 #endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)

+ 3 - 4
src/core/windows/SDL_immdevice.c

@@ -147,7 +147,7 @@ static SDL_AudioDevice *SDL_IMMDevice_Add(const SDL_bool iscapture, const char *
         SDL_zero(spec);
         SDL_zero(spec);
         spec.channels = (Uint8)fmt->Format.nChannels;
         spec.channels = (Uint8)fmt->Format.nChannels;
         spec.freq = fmt->Format.nSamplesPerSec;
         spec.freq = fmt->Format.nSamplesPerSec;
-        spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt);
+        spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
 
 
         device = SDL_AddAudioDevice(iscapture, devname, &spec, handle);
         device = SDL_AddAudioDevice(iscapture, devname, &spec, handle);
     }
     }
@@ -164,7 +164,6 @@ typedef struct SDLMMNotificationClient
 {
 {
     const IMMNotificationClientVtbl *lpVtbl;
     const IMMNotificationClientVtbl *lpVtbl;
     SDL_AtomicInt refcount;
     SDL_AtomicInt refcount;
-    SDL_bool useguid;
 } SDLMMNotificationClient;
 } SDLMMNotificationClient;
 
 
 static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
 static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
@@ -370,7 +369,7 @@ static void EnumerateEndpointsForFlow(const SDL_bool iscapture, SDL_AudioDevice
                 GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
                 GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
                 if (devname) {
                 if (devname) {
                     SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(iscapture, devname, &fmt, devid, &dsoundguid);
                     SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(iscapture, devname, &fmt, devid, &dsoundguid);
-                    if (default_device && default_devid && SDL_wcscmp(default_devid, devid)) {
+                    if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
                         *default_device = sdldevice;
                         *default_device = sdldevice;
                     }
                     }
                     SDL_free(devname);
                     SDL_free(devname);
@@ -395,7 +394,7 @@ void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_Audi
     IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
     IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
 }
 }
 
 
-SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
+SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
 {
 {
     if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
     if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
         return SDL_AUDIO_F32SYS;
         return SDL_AUDIO_F32SYS;

+ 1 - 1
src/core/windows/SDL_immdevice.h

@@ -35,6 +35,6 @@ void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_output, SDL_Audi
 LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device);
 LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device);
 LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device);
 LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device);
 void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device);
 void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device);
-SDL_AudioFormat WaveFormatToSDLFormat(WAVEFORMATEX *waveformat);
+SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
 
 
 #endif /* SDL_IMMDEVICE_H */
 #endif /* SDL_IMMDEVICE_H */

+ 1 - 1
test/loopwave.c

@@ -39,7 +39,7 @@ static SDL_AudioStream *stream;
 
 
 static void fillerup(void)
 static void fillerup(void)
 {
 {
-    if (SDL_GetAudioStreamAvailable(stream) < (wave.soundlen / 2)) {
+    if (SDL_GetAudioStreamAvailable(stream) < (int) ((wave.soundlen / 2))) {
         SDL_PutAudioStreamData(stream, wave.sound, wave.soundlen);
         SDL_PutAudioStreamData(stream, wave.sound, wave.soundlen);
     }
     }
 }
 }