Commit 4a50a04213b9f2c214702a77af3254f08f550c1b

Ryan C. Gordon 2018-10-21T22:40:17

wasapi/win32: Sort initial device lists by device GUID. This makes an unchanged set of hardware always report devices in the same order on each run.

diff --git a/src/audio/wasapi/SDL_wasapi_win32.c b/src/audio/wasapi/SDL_wasapi_win32.c
index 731dcff..9d7c159 100644
--- a/src/audio/wasapi/SDL_wasapi_win32.c
+++ b/src/audio/wasapi/SDL_wasapi_win32.c
@@ -351,10 +351,42 @@ WASAPI_ActivateDevice(_THIS, const SDL_bool isrecovery)
 }
 
 
+typedef struct
+{
+    LPWSTR devid;
+    char *devname;
+} EndpointItem;
+
+static int sort_endpoints(const void *_a, const void *_b)
+{
+    LPWSTR a = ((const EndpointItem *) _a)->devid;
+    LPWSTR b = ((const EndpointItem *) _b)->devid;
+    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
 WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
 {
     IMMDeviceCollection *collection = NULL;
+    EndpointItem *items;
     UINT i, total;
 
     /* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
@@ -369,22 +401,36 @@ WASAPI_EnumerateEndpointsForFlow(const SDL_bool iscapture)
         return;
     }
 
+    items = (EndpointItem *) SDL_calloc(total, sizeof (EndpointItem));
+    if (!items) {
+        return;  /* oh well. */
+    }
+
     for (i = 0; i < total; i++) {
+        EndpointItem *item = items + i;
         IMMDevice *device = NULL;
         if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &device))) {
-            LPWSTR devid = NULL;
-            if (SUCCEEDED(IMMDevice_GetId(device, &devid))) {
-                char *devname = GetWasapiDeviceName(device);
-                if (devname) {
-                    WASAPI_AddDevice(iscapture, devname, devid);
-                    SDL_free(devname);
-                }
-                CoTaskMemFree(devid);
+            if (SUCCEEDED(IMMDevice_GetId(device, &item->devid))) {
+                item->devname = GetWasapiDeviceName(device);
             }
             IMMDevice_Release(device);
         }
     }
 
+    /* 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)) {
+            WASAPI_AddDevice(iscapture, item->devname, item->devid);
+        }
+        SDL_free(item->devname);
+        CoTaskMemFree(item->devid);
+    }
+
+    SDL_free(items);
     IMMDeviceCollection_Release(collection);
 }