SDL: coreaudio: rewritten for SDL3 audio redesign!

From c653e57768aa78ac93d58e380b98d1706dd0d355 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 3 Jul 2023 22:00:58 -0400
Subject: [PATCH] coreaudio: rewritten for SDL3 audio redesign!

---
 src/audio/coreaudio/SDL_coreaudio.h |    5 +-
 src/audio/coreaudio/SDL_coreaudio.m | 1048 +++++++++------------------
 2 files changed, 350 insertions(+), 703 deletions(-)

diff --git a/src/audio/coreaudio/SDL_coreaudio.h b/src/audio/coreaudio/SDL_coreaudio.h
index 989ed61d2316..4b7f81bf0671 100644
--- a/src/audio/coreaudio/SDL_coreaudio.h
+++ b/src/audio/coreaudio/SDL_coreaudio.h
@@ -53,15 +53,12 @@ struct SDL_PrivateAudioData
     AudioQueueRef audioQueue;
     int numAudioBuffers;
     AudioQueueBufferRef *audioBuffer;
-    void *buffer;
-    UInt32 bufferOffset;
-    UInt32 bufferSize;
+    AudioQueueBufferRef current_buffer;
     AudioStreamBasicDescription strdesc;
     SDL_Semaphore *ready_semaphore;
     char *thread_error;
 #ifdef MACOSX_COREAUDIO
     AudioDeviceID deviceID;
-    SDL_AtomicInt device_change_flag;
 #else
     SDL_bool interrupted;
     CFTypeRef interruption_listener;
diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m
index 20ac3bf5df7c..bd05c1a10ced 100644
--- a/src/audio/coreaudio/SDL_coreaudio.m
+++ b/src/audio/coreaudio/SDL_coreaudio.m
@@ -22,28 +22,24 @@
 
 #ifdef SDL_AUDIO_DRIVER_COREAUDIO
 
-/* !!! FIXME: clean out some of the macro salsa in here. */
-
 #include "../SDL_audio_c.h"
 #include "../SDL_sysaudio.h"
 #include "SDL_coreaudio.h"
 #include "../../thread/SDL_systhread.h"
 
-#define DEBUG_COREAUDIO 0
+#define DEBUG_COREAUDIO 1
 
 #if DEBUG_COREAUDIO
-#define CHECK_RESULT(msg)                                                 \
-    if (result != noErr) {                                                \
-        printf("COREAUDIO: Got error %d from '%s'!\n", (int)result, msg); \
-        SDL_SetError("CoreAudio error (%s): %d", msg, (int)result);       \
-        return 0;                                                         \
-    }
+    #define CHECK_RESULT(msg) \
+        if (result != noErr) { \
+            SDL_Log("COREAUDIO: Got error %d from '%s'!\n", (int)result, msg); \
+            return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \
+        }
 #else
-#define CHECK_RESULT(msg)                                           \
-    if (result != noErr) {                                          \
-        SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \
-        return 0;                                                   \
-    }
+    #define CHECK_RESULT(msg) \
+        if (result != noErr) { \
+            return SDL_SetError("CoreAudio error (%s): %d", msg, (int)result); \
+        }
 #endif
 
 #ifdef MACOSX_COREAUDIO
@@ -53,78 +49,84 @@
     kAudioObjectPropertyElementMain
 };
 
-typedef void (*addDevFn)(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data);
+static const AudioObjectPropertyAddress default_output_device_address = {
+    kAudioHardwarePropertyDefaultOutputDevice,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMain
+};
 
-typedef struct AudioDeviceList
-{
-    AudioDeviceID devid;
-    SDL_bool alive;
-    struct AudioDeviceList *next;
-} AudioDeviceList;
+static const AudioObjectPropertyAddress default_input_device_address = {
+    kAudioHardwarePropertyDefaultInputDevice,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMain
+};
+
+static const AudioObjectPropertyAddress alive_address = {
+    kAudioDevicePropertyDeviceIsAlive,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMain
+};
 
-static AudioDeviceList *output_devs = NULL;
-static AudioDeviceList *capture_devs = NULL;
 
-static SDL_bool add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
+static OSStatus DeviceAliveNotification(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
 {
-    AudioDeviceList *item = (AudioDeviceList *)SDL_malloc(sizeof(AudioDeviceList));
-    if (item == NULL) {
-        return SDL_FALSE;
+    SDL_AudioDevice *device = (SDL_AudioDevice *)data;
+    SDL_assert(((AudioObjectID)(size_t)device->handle) == devid);
+
+    UInt32 alive = 1;
+    UInt32 size = sizeof(alive);
+    const OSStatus error = AudioObjectGetPropertyData(devid, addrs, 0, NULL, &size, &alive);
+
+    SDL_bool dead = SDL_FALSE;
+    if (error == kAudioHardwareBadDeviceError) {
+        dead = SDL_TRUE; /* device was unplugged. */
+    } else if ((error == kAudioHardwareNoError) && (!alive)) {
+        dead = SDL_TRUE; /* device died in some other way. */
     }
-    item->devid = devId;
-    item->alive = SDL_TRUE;
-    item->next = iscapture ? capture_devs : output_devs;
-    if (iscapture) {
-        capture_devs = item;
-    } else {
-        output_devs = item;
+
+    if (dead) {
+        #if DEBUG_COREAUDIO
+        SDL_Log("COREAUDIO: device '%s' is lost!", device->name);
+        #endif
+        SDL_AudioDeviceDisconnected(device);
     }
 
-    return SDL_TRUE;
+    return noErr;
 }
 
-static void addToDevList(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data)
+static void COREAUDIO_FreeDeviceHandle(SDL_AudioDevice *device)
 {
-    if (add_to_internal_dev_list(iscapture, devId)) {
-        SDL_AddAudioDevice(iscapture, name, spec, (void *)((size_t)devId));
-    }
+    const AudioDeviceID devid = (AudioDeviceID)(size_t)device->handle;
+    AudioObjectRemovePropertyListener(devid, &alive_address, DeviceAliveNotification, device);
 }
 
-static void build_device_list(int iscapture, addDevFn addfn, void *addfndata)
+// This only _adds_ new devices. Removal is handled by devices triggering kAudioDevicePropertyDeviceIsAlive property changes.
+static void RefreshPhysicalDevices(void)
 {
-    OSStatus result = noErr;
     UInt32 size = 0;
     AudioDeviceID *devs = NULL;
-    UInt32 i = 0;
-    UInt32 max = 0;
+    SDL_bool isstack;
 
-    result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
-                                            &devlist_address, 0, NULL, &size);
-    if (result != kAudioHardwareNoError) {
+    if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size) != kAudioHardwareNoError) {
         return;
-    }
-
-    devs = (AudioDeviceID *)alloca(size);
-    if (devs == NULL) {
+    } else if ((devs = (AudioDeviceID *) SDL_small_alloc(Uint8, size, &isstack)) == NULL) {
+        return;
+    } else if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &devlist_address, 0, NULL, &size, devs) != kAudioHardwareNoError) {
+        SDL_small_free(devs, isstack);
         return;
     }
 
-    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
-                                        &devlist_address, 0, NULL, &size, devs);
-    if (result != kAudioHardwareNoError) {
-        return;
+    const UInt32 total_devices = (UInt32) (size / sizeof(AudioDeviceID));
+    for (UInt32 i = 0; i < total_devices; i++) {
+        SDL_AudioDevice *device = SDL_ObtainPhysicalAudioDeviceByHandle((void *)((size_t)devs[i]));
+        if (device) {
+            SDL_UnlockMutex(device->lock);
+            devs[i] = 0;  // The system and SDL both agree it's already here, don't check it again.
+        }
     }
 
-    max = size / sizeof(AudioDeviceID);
-    for (i = 0; i < max; i++) {
-        CFStringRef cfstr = NULL;
-        char *ptr = NULL;
-        AudioDeviceID dev = devs[i];
-        AudioBufferList *buflist = NULL;
-        int usable = 0;
-        CFIndex len = 0;
-        double sampleRate = 0;
-        SDL_AudioSpec spec;
+    // any non-zero items remaining in `devs` are new devices to be added.
+    for (int iscapture = 0; iscapture < 2; iscapture++) {
         const AudioObjectPropertyAddress addr = {
             kAudioDevicePropertyStreamConfiguration,
             iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
@@ -141,165 +143,167 @@ static void build_device_list(int iscapture, addDevFn addfn, void *addfndata)
             kAudioObjectPropertyElementMain
         };
 
-        result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
-        if (result != noErr) {
-            continue;
-        }
-
-        buflist = (AudioBufferList *)SDL_malloc(size);
-        if (buflist == NULL) {
-            continue;
-        }
+        for (UInt32 i = 0; i < total_devices; i++) {
+            const AudioDeviceID dev = devs[i];
+            if (!dev) {
+                continue;  // already added.
+            }
 
-        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
-                                            &size, buflist);
+            AudioBufferList *buflist = NULL;
+            double sampleRate = 0;
 
-        SDL_zero(spec);
-        if (result == noErr) {
-            UInt32 j;
-            for (j = 0; j < buflist->mNumberBuffers; j++) {
-                spec.channels += buflist->mBuffers[j].mNumberChannels;
+            if (AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size) != noErr) {
+                continue;
+            } else if ((buflist = (AudioBufferList *)SDL_malloc(size)) == NULL) {
+                continue;
             }
-        }
 
-        SDL_free(buflist);
+            OSStatus result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, buflist);
 
-        if (spec.channels == 0) {
-            continue;
-        }
+            SDL_AudioSpec spec;
+            SDL_zero(spec);
+            if (result == noErr) {
+                for (Uint32 j = 0; j < buflist->mNumberBuffers; j++) {
+                    spec.channels += buflist->mBuffers[j].mNumberChannels;
+                }
+            }
 
-        size = sizeof(sampleRate);
-        result = AudioObjectGetPropertyData(dev, &freqaddr, 0, NULL, &size, &sampleRate);
-        if (result == noErr) {
-            spec.freq = (int)sampleRate;
-        }
+            SDL_free(buflist);
 
-        size = sizeof(CFStringRef);
-        result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
-        if (result != kAudioHardwareNoError) {
-            continue;
-        }
+            if (spec.channels == 0) {
+                continue;
+            }
+
+            size = sizeof(sampleRate);
+            if (AudioObjectGetPropertyData(dev, &freqaddr, 0, NULL, &size, &sampleRate) == noErr) {
+                spec.freq = (int)sampleRate;
+            }
 
-        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
-                                                kCFStringEncodingUTF8);
+            CFStringRef cfstr = NULL;
+            size = sizeof(CFStringRef);
+            if (AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr) != kAudioHardwareNoError) {
+                continue;
+            }
 
-        ptr = (char *)SDL_malloc(len + 1);
-        usable = ((ptr != NULL) &&
-                  (CFStringGetCString(cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
+            CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), kCFStringEncodingUTF8);
+            char *name = (char *)SDL_malloc(len + 1);
+            SDL_bool usable = ((name != NULL) && (CFStringGetCString(cfstr, name, len + 1, kCFStringEncodingUTF8))) ? SDL_TRUE : SDL_FALSE;
 
-        CFRelease(cfstr);
+            CFRelease(cfstr);
 
-        if (usable) {
-            len = SDL_strlen(ptr);
-            /* Some devices have whitespace at the end...trim it. */
-            while ((len > 0) && (ptr[len - 1] == ' ')) {
-                len--;
+            if (usable) {
+                // Some devices have whitespace at the end...trim it.
+                len = (CFIndex) SDL_strlen(name);
+                while ((len > 0) && (name[len - 1] == ' ')) {
+                    len--;
+                }
+                usable = (len > 0);
             }
-            usable = (len > 0);
-        }
 
-        if (usable) {
-            ptr[len] = '\0';
+            if (usable) {
+                name[len] = '\0';
 
-#if DEBUG_COREAUDIO
-            printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
-                   ((iscapture) ? "capture" : "output"),
-                   (int)i, ptr, (int)dev);
-#endif
-            addfn(ptr, &spec, iscapture, dev, addfndata);
+                #if DEBUG_COREAUDIO
+                SDL_Log("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
+                       ((iscapture) ? "capture" : "output"),
+                       (int)i, name, (int)dev);
+                #endif
+
+                devs[i] = 0;  // don't bother checking this one on the next iscapture iteration of the loop
+
+                SDL_AudioDevice *device = SDL_AddAudioDevice(iscapture ? SDL_TRUE : SDL_FALSE, name, &spec, (void *)((size_t)dev));
+                if (device) {
+                    AudioObjectAddPropertyListener(dev, &alive_address, DeviceAliveNotification, device);
+                }
+            }
+            SDL_free(name); // SDL_AddAudioDevice() would have copied the string.
         }
-        SDL_free(ptr); /* addfn() would have copied the string. */
     }
-}
 
-static void free_audio_device_list(AudioDeviceList **list)
-{
-    AudioDeviceList *item = *list;
-    while (item) {
-        AudioDeviceList *next = item->next;
-        SDL_free(item);
-        item = next;
-    }
-    *list = NULL;
+    SDL_small_free(devs, isstack);
 }
 
-static void COREAUDIO_DetectDevices(void)
+// this is called when the system's list of available audio devices changes.
+static OSStatus DeviceListChangedNotification(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
 {
-    build_device_list(SDL_TRUE, addToDevList, NULL);
-    build_device_list(SDL_FALSE, addToDevList, NULL);
+    RefreshPhysicalDevices();
+    return noErr;
 }
 
-static void build_device_change_list(const char *name, SDL_AudioSpec *spec, const int iscapture, AudioDeviceID devId, void *data)
+static OSStatus DefaultAudioDeviceChangedNotification(AudioObjectID inObjectID, const AudioObjectPropertyAddress *addr)
 {
-    AudioDeviceList **list = (AudioDeviceList **)data;
-    AudioDeviceList *item;
-    for (item = *list; item != NULL; item = item->next) {
-        if (item->devid == devId) {
-            item->alive = SDL_TRUE;
-            return;
+    AudioDeviceID devid;
+    Uint32 size = sizeof(devid);
+    if (AudioObjectGetPropertyData(inObjectID, addr, 0, NULL, &size, &devid) == noErr) {
+        SDL_AudioDevice *device = SDL_ObtainPhysicalAudioDeviceByHandle((void *)((size_t)devid));
+        if (device) {
+            SDL_UnlockMutex(device->lock);
+            SDL_DefaultAudioDeviceChanged(device);
         }
     }
+    return noErr;
+}
 
-    add_to_internal_dev_list(iscapture, devId); /* new device, add it. */
-    SDL_AddAudioDevice(iscapture, name, spec, (void *)((size_t)devId));
+static OSStatus DefaultOutputDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
+{
+    #if DEBUG_COREAUDIO
+    SDL_Log("COREAUDIO: default output device changed!");
+    #endif
+    SDL_assert(inNumberAddresses == 1);
+    return DefaultAudioDeviceChangedNotification(inObjectID, inAddresses);
 }
 
-static void reprocess_device_list(const int iscapture, AudioDeviceList **list)
+static OSStatus DefaultInputDeviceChangedNotification(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
 {
-    AudioDeviceList *item;
-    AudioDeviceList *prev = NULL;
-    for (item = *list; item != NULL; item = item->next) {
-        item->alive = SDL_FALSE;
-    }
+    #if DEBUG_COREAUDIO
+    SDL_Log("COREAUDIO: default input device changed!");
+    #endif
+    SDL_assert(inNumberAddresses == 1);
+    return DefaultAudioDeviceChangedNotification(inObjectID, inAddresses);
+}
+
+static void COREAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture)
+{
+    RefreshPhysicalDevices();
 
-    build_device_list(iscapture, build_device_change_list, list);
+    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, DeviceListChangedNotification, NULL);
 
-    /* free items in the list that aren't still alive. */
-    item = *list;
-    while (item != NULL) {
-        AudioDeviceList *next = item->next;
-        if (item->alive) {
-            prev = item;
-        } else {
-            SDL_RemoveAudioDevice(iscapture, (void *)((size_t)item->devid));
-            if (prev) {
-                prev->next = item->next;
-            } else {
-                *list = item->next;
-            }
-            SDL_free(item);
+    /* Get the Device ID */
+    UInt32 size;
+    AudioDeviceID devid;
+
+    size = sizeof(AudioDeviceID);
+    if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_output_device_address, 0, NULL, &size, &devid) == noErr) {
+        SDL_AudioDevice *device = SDL_ObtainPhysicalAudioDeviceByHandle((void *)((size_t)devid));
+        if (device) {
+            SDL_UnlockMutex(device->lock);
+            *default_output = device;
         }
-        item = next;
     }
-}
+    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_output_device_address, DefaultOutputDeviceChangedNotification, NULL);
 
-/* this is called when the system's list of available audio devices changes. */
-static OSStatus device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
-{
-    reprocess_device_list(SDL_TRUE, &capture_devs);
-    reprocess_device_list(SDL_FALSE, &output_devs);
-    return 0;
+    size = sizeof(AudioDeviceID);
+    if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &default_input_device_address, 0, NULL, &size, &devid) == noErr) {
+        SDL_AudioDevice *device = SDL_ObtainPhysicalAudioDeviceByHandle((void *)((size_t)devid));
+        if (device) {
+            SDL_UnlockMutex(device->lock);
+            *default_capture = device;
+        }
+    }
+    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &default_input_device_address, DefaultInputDeviceChangedNotification, NULL);
 }
-#endif
 
-static int open_playback_devices;
-static int open_capture_devices;
-static int num_open_devices;
-static SDL_AudioDevice **open_devices;
+#else  // iOS-specific section follows.
 
-#ifndef MACOSX_COREAUDIO
+static SDL_bool session_active = SDL_FALSE;
 
-static BOOL session_active = NO;
-
-static void pause_audio_devices(void)
+static void PauseAudioDevices(void)  // !!! FIXME: this needs to be updated, and we need a method to access SDL_audio.c's device lists.
 {
-    int i;
-
     if (!open_devices) {
         return;
     }
 
-    for (i = 0; i < num_open_devices; ++i) {
+    for (int i = 0; i < num_open_devices; ++i) {
         SDL_AudioDevice *device = open_devices[i];
         if (device->hidden->audioQueue && !device->hidden->interrupted) {
             AudioQueuePause(device->hidden->audioQueue);
@@ -307,15 +311,13 @@ static void pause_audio_devices(void)
     }
 }
 
-static void resume_audio_devices(void)
+static void ResumeAudioDevices(void)  // !!! FIXME: this needs to be updated, and we need a method to access SDL_audio.c's device lists.
 {
-    int i;
-
     if (!open_devices) {
         return;
     }
 
-    for (i = 0; i < num_open_devices; ++i) {
+    for (int i = 0; i < num_open_devices; ++i) {
         SDL_AudioDevice *device = open_devices[i];
         if (device->hidden->audioQueue && !device->hidden->interrupted) {
             AudioQueueStart(device->hidden->audioQueue, NULL);
@@ -323,7 +325,7 @@ static void resume_audio_devices(void)
     }
 }
 
-static void interruption_begin(SDL_AudioDevice *device)
+static void InterruptionBegin(SDL_AudioDevice *device)
 {
     if (device != NULL && device->hidden->audioQueue != NULL) {
         device->hidden->interrupted = SDL_TRUE;
@@ -331,7 +333,7 @@ static void interruption_begin(SDL_AudioDevice *device)
     }
 }
 
-static void interruption_end(SDL_AudioDevice *device)
+static void InterruptionEnd(SDL_AudioDevice *device)
 {
     if (device != NULL && device->hidden != NULL && device->hidden->audioQueue != NULL && device->hidden->interrupted && AudioQueueStart(device->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
         device->hidden->interrupted = SDL_FALSE;
@@ -351,9 +353,9 @@ - (void)audioSessionInterruption:(NSNotification *)note
     @synchronized(self) {
         NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
         if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
-            interruption_begin(self.device);
+            InterruptionBegin(self.device);
         } else {
-            interruption_end(self.device);
+            InterruptionEnd(self.device);
         }
     }
 }
@@ -361,13 +363,13 @@ - (void)audioSessionInterruption:(NSNotification *)note
 - (void)applicationBecameActive:(NSNotification *)note
 {
     @synchronized(self) {
-        interruption_end(self.device);
+        InterruptionEnd(self.device);
     }
 }
 
 @end
 
-static BOOL update_audio_session(SDL_AudioDevice *device, SDL_bool open, SDL_bool allow_playandrecord)
+static SDL_bool UpdateAudioSession(SDL_AudioDevice *device, SDL_bool open, SDL_bool allow_playandrecord)
 {
     @autoreleasepool {
         AVAudioSession *session = [AVAudioSession sharedInstance];
@@ -402,11 +404,11 @@ static BOOL update_audio_session(SDL_AudioDevice *device, SDL_bool open, SDL_boo
             category = AVAudioSessionCategoryRecord;
         }
 
-#if !TARGET_OS_TV
+        #if !TARGET_OS_TV
         if (category == AVAudioSessionCategoryPlayAndRecord) {
             options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
         }
-#endif
+        #endif
         if (category == AVAudioSessionCategoryRecord ||
             category == AVAudioSessionCategoryPlayAndRecord) {
             /* AVAudioSessionCategoryOptionAllowBluetooth isn't available in the SDK for
@@ -426,27 +428,27 @@ static BOOL update_audio_session(SDL_AudioDevice *device, SDL_bool open, SDL_boo
         if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
             if (![session.category isEqualToString:category] || session.categoryOptions != options) {
                 /* Stop the current session so we don't interrupt other application audio */
-                pause_audio_devices();
+                PauseAudioDevices();
                 [session setActive:NO error:nil];
-                session_active = NO;
+                session_active = SDL_FALSE;
 
                 if (![session setCategory:category mode:mode options:options error:&err]) {
                     NSString *desc = err.description;
                     SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
-                    return NO;
+                    return SDL_FALSE;
                 }
             }
         } else {
             if (![session.category isEqualToString:category]) {
                 /* Stop the current session so we don't interrupt other application audio */
-                pause_audio_devices();
+                PauseAudioDevices();
                 [session setActive:NO error:nil];
-                session_active = NO;
+                session_active = SDL_FALSE;
 
                 if (![session setCategory:category error:&err]) {
                     NSString *desc = err.description;
                     SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
-                    return NO;
+                    return SDL_FALSE;
                 }
             }
         }
@@ -455,19 +457,19 @@ static BOOL update_audio_session(SDL_AudioDevice *device, SDL_bool open, SDL_boo
             if (![session setActive:YES error:&err]) {
                 if ([err code] == AVAudioSessionErrorCodeResourceNotAvailable &&
                     category == AVAudioSessionCategoryPlayAndRecord) {
-                    return update_audio_session(device, open, SDL_FALSE);
+                    return UpdateAudioSession(device, open, SDL_FALSE);
                 }
 
                 NSString *desc = err.description;
                 SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
-                return NO;
+                return SDL_FALSE;
             }
-            session_active = YES;
-            resume_audio_devices();
+            session_active = SDL_TRUE;
+            ResumeAudioDevices();
         } else if (!open_playback_devices && !open_capture_devices && session_active) {
-            pause_audio_devices();
+            PauseAudioDevices();
             [session setActive:NO error:nil];
-            session_active = NO;
+            session_active = SDL_FALSE;
         }
 
         if (open) {
@@ -504,191 +506,83 @@ static BOOL update_audio_session(SDL_AudioDevice *device, SDL_bool open, SDL_boo
         }
     }
 
-    return YES;
+    return SDL_TRUE;
 }
 #endif
 
-/* The AudioQueue callback */
-static void outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
-{
-    SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData;
-
-    /* This flag is set before device->mixer_lock is destroyed during
-       shutdown, so check it before grabbing the mutex, and then check it
-       again _after_ in case we blocked waiting on the lock. */
-    if (SDL_AtomicGet(&device->shutdown)) {
-        return; /* don't do anything, since we don't even want to enqueue this buffer again. */
-    }
-
-    SDL_LockMutex(device->mixer_lock);
-
-    if (SDL_AtomicGet(&device->shutdown)) {
-        SDL_UnlockMutex(device->mixer_lock);
-        return; /* don't do anything, since we don't even want to enqueue this buffer again. */
-    }
-
-    if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
-        /* Supply silence if audio is not enabled or paused */
-        SDL_memset(inBuffer->mAudioData, device->spec.silence, inBuffer->mAudioDataBytesCapacity);
-    } else if (device->stream) {
-        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
-        Uint8 *ptr = (Uint8 *)inBuffer->mAudioData;
-
-        while (remaining > 0) {
-            if (SDL_GetAudioStreamAvailable(device->stream) == 0) {
-                /* Generate the data */
-                (*device->callbackspec.callback)(device->callbackspec.userdata,
-                                               device->hidden->buffer, device->hidden->bufferSize);
-                device->hidden->bufferOffset = 0;
-                SDL_PutAudioStreamData(device->stream, device->hidden->buffer, device->hidden->bufferSize);
-            }
-            if (SDL_GetAudioStreamAvailable(device->stream) > 0) {
-                int got;
-                UInt32 len = SDL_GetAudioStreamAvailable(device->stream);
-                if (len > remaining) {
-                    len = remaining;
-                }
-                got = SDL_GetAudioStreamData(device->stream, ptr, len);
-                SDL_assert((got < 0) || (got == len));
-                if (got != len) {
-                    SDL_memset(ptr, device->spec.silence, len);
-                }
-                ptr = ptr + len;
-                remaining -= len;
-            }
-        }
-    } else {
-        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
-        Uint8 *ptr = (Uint8 *)inBuffer->mAudioData;
-
-        while (remaining > 0) {
-            UInt32 len;
-            if (device->hidden->bufferOffset >= device->hidden->bufferSize) {
-                /* Generate the data */
-                (*device->callbackspec.callback)(device->callbackspec.userdata,
-                                               device->hidden->buffer, device->hidden->bufferSize);
-                device->hidden->bufferOffset = 0;
-            }
-
-            len = device->hidden->bufferSize - device->hidden->bufferOffset;
-            if (len > remaining) {
-                len = remaining;
-            }
-            SDL_memcpy(ptr, (char *)device->hidden->buffer + device->hidden->bufferOffset, len);
-            ptr = ptr + len;
-            remaining -= len;
-            device->hidden->bufferOffset += len;
-        }
-    }
-
-    AudioQueueEnqueueBuffer(device->hidden->audioQueue, inBuffer, 0, NULL);
 
-    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
+static void COREAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buffer_size)
+{
+    AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
+    SDL_assert(current_buffer != NULL);  // should have been called from OutputBufferReadyCallback
+    SDL_assert(buffer == (Uint8 *) current_buffer->mAudioData);
+    current_buffer->mAudioDataByteSize = current_buffer->mAudioDataBytesCapacity;
+    device->hidden->current_buffer = NULL;
+    AudioQueueEnqueueBuffer(device->hidden->audioQueue, current_buffer, 0, NULL);
+}
 
-    SDL_UnlockMutex(device->mixer_lock);
+static Uint8 *COREAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size)
+{
+    AudioQueueBufferRef current_buffer = device->hidden->current_buffer;
+    SDL_assert(current_buffer != NULL);  // should have been called from OutputBufferReadyCallback
+    SDL_assert(current_buffer->mAudioData != NULL);
+    *buffer_size = (int) current_buffer->mAudioDataBytesCapacity;
+    return (Uint8 *) current_buffer->mAudioData;
 }
 
-static void inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
-                          const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
-                          const AudioStreamPacketDescription *inPacketDescs)
+static void OutputBufferReadyCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
 {
     SDL_AudioDevice *device = (SDL_AudioDevice *)inUserData;
-
-    if (SDL_AtomicGet(&device->shutdown)) {
-        return; /* don't do anything. */
-    }
-
-    /* ignore unless we're active. */
-    if (!SDL_AtomicGet(&device->paused) && SDL_AtomicGet(&device->enabled)) {
-        const Uint8 *ptr = (const Uint8 *)inBuffer->mAudioData;
-        UInt32 remaining = inBuffer->mAudioDataByteSize;
-        while (remaining > 0) {
-            UInt32 len = device->hidden->bufferSize - device->hidden->bufferOffset;
-            if (len > remaining) {
-                len = remaining;
-            }
-
-            SDL_memcpy((char *)device->hidden->buffer + device->hidden->bufferOffset, ptr, len);
-            ptr += len;
-            remaining -= len;
-            device->hidden->bufferOffset += len;
-
-            if (device->hidden->bufferOffset >= device->hidden->bufferSize) {
-                SDL_LockMutex(device->mixer_lock);
-                (*device->callbackspec.callback)(device->callbackspec.userdata, device->hidden->buffer, device->hidden->bufferSize);
-                SDL_UnlockMutex(device->mixer_lock);
-                device->hidden->bufferOffset = 0;
-            }
-        }
-    }
-
-    AudioQueueEnqueueBuffer(device->hidden->audioQueue, inBuffer, 0, NULL);
+    SDL_assert(inBuffer != NULL);  // ...right?
+    SDL_assert(device->hidden->current_buffer == NULL);  // shouldn't have anything pending
+    device->hidden->current_buffer = inBuffer;
+    SDL_OutputAudioThreadIterate(device);
+    SDL_assert(device->hidden->current_buffer == NULL);  // PlayDevice should have enqueued and cleaned it out.
 }
 
-#ifdef MACOSX_COREAUDIO
-static const AudioObjectPropertyAddress alive_address = {
- 

(Patch may be truncated, please check the link at the top of this post.)