From 03f8528315fa46c95991a34f3325d7b33ae5538c Mon Sep 17 00:00:00 2001
From: Edward Rudd <urkle@outoforder.cc>
Date: Sat, 02 May 2020 21:48:36 +0000
Subject: [PATCH] Update source to SDL2 2.0.12

---
 source/src/audio/coreaudio/SDL_coreaudio.m |  363 ++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 243 insertions(+), 120 deletions(-)

diff --git a/source/src/audio/coreaudio/SDL_coreaudio.m b/source/src/audio/coreaudio/SDL_coreaudio.m
index 59242f9..68aae35 100644
--- a/source/src/audio/coreaudio/SDL_coreaudio.m
+++ b/source/src/audio/coreaudio/SDL_coreaudio.m
@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -26,7 +26,6 @@
 
 #include "SDL_audio.h"
 #include "SDL_hints.h"
-#include "SDL_timer.h"
 #include "../SDL_audio_c.h"
 #include "../SDL_sysaudio.h"
 #include "SDL_coreaudio.h"
@@ -327,12 +326,18 @@
     @autoreleasepool {
         AVAudioSession *session = [AVAudioSession sharedInstance];
         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
         /* Set category to ambient by default so that other music continues playing. */
         NSString *category = AVAudioSessionCategoryAmbient;
+        NSString *mode = AVAudioSessionModeDefault;
+        NSUInteger options = 0;
         NSError *err = nil;
 
         if (open_playback_devices && open_capture_devices) {
             category = AVAudioSessionCategoryPlayAndRecord;
+#if !TARGET_OS_TV
+            options = AVAudioSessionCategoryOptionDefaultToSpeaker;
+#endif
         } else if (open_capture_devices) {
             category = AVAudioSessionCategoryRecord;
         } else {
@@ -349,10 +354,18 @@
             }
         }
 
-        if (![session setCategory:category error:&err]) {
-            NSString *desc = err.description;
-            SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
-            return NO;
+        if ([session respondsToSelector:@selector(setCategory:mode:options:error:)]) {
+            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;
+            }
+        } else {
+            if (![session setCategory:category error:&err]) {
+                NSString *desc = err.description;
+                SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
+                return NO;
+            }
         }
 
         if (open && (open_playback_devices + open_capture_devices) == 1) {
@@ -377,15 +390,16 @@
             /* An interruption end notification is not guaranteed to be sent if
              we were previously interrupted... resuming if needed when the app
              becomes active seems to be the way to go. */
+            // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.  johna
             [center addObserver:listener
                        selector:@selector(applicationBecameActive:)
                            name:UIApplicationDidBecomeActiveNotification
-                         object:session];
+                         object:nil];
 
             [center addObserver:listener
                        selector:@selector(applicationBecameActive:)
                            name:UIApplicationWillEnterForegroundNotification
-                         object:session];
+                         object:nil];
 
             this->hidden->interruption_listener = CFBridgingRetain(listener);
         } else {
@@ -410,27 +424,71 @@
 outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
 {
     SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
-    SDL_assert(inBuffer->mAudioDataBytesCapacity == this->hidden->bufferSize);
-    SDL_memcpy(inBuffer->mAudioData, this->hidden->buffer, this->hidden->bufferSize);
-    SDL_memset(this->hidden->buffer, '\0', this->hidden->bufferSize);  /* zero out in case we have to fill again without new data. */
-    inBuffer->mAudioDataByteSize = this->hidden->bufferSize;
-    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
-    this->hidden->refill = SDL_TRUE;
-}
-
-static Uint8 *
-COREAUDIO_GetDeviceBuf(_THIS)
-{
-    return this->hidden->buffer;
-}
-
-static void
-COREAUDIO_WaitDevice(_THIS)
-{
-    while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
-        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
+    if (SDL_AtomicGet(&this->hidden->shutdown)) {
+        return;  /* don't do anything. */
     }
-    this->hidden->refill = SDL_FALSE;
+
+    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
+        /* Supply silence if audio is not enabled or paused */
+        SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
+    } else if (this->stream ) {
+        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
+        Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
+
+        while (remaining > 0) {
+            if ( SDL_AudioStreamAvailable(this->stream) == 0 ) {
+                /* Generate the data */
+                SDL_LockMutex(this->mixer_lock);
+                (*this->callbackspec.callback)(this->callbackspec.userdata,
+                                               this->hidden->buffer, this->hidden->bufferSize);
+                SDL_UnlockMutex(this->mixer_lock);
+                this->hidden->bufferOffset = 0;
+                SDL_AudioStreamPut(this->stream, this->hidden->buffer, this->hidden->bufferSize);
+            }
+            if ( SDL_AudioStreamAvailable(this->stream) > 0 ) {
+                int got;
+                UInt32 len = SDL_AudioStreamAvailable(this->stream);
+                if ( len > remaining )
+                    len = remaining;
+                got = SDL_AudioStreamGet(this->stream, ptr, len);
+                SDL_assert((got < 0) || (got == len));
+                if (got != len) {
+                    SDL_memset(ptr, this->spec.silence, len);
+                }
+                ptr = ptr + len;
+                remaining -= len;
+            }
+        }
+    } else {
+        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
+        Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
+
+        while (remaining > 0) {
+            UInt32 len;
+            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
+                /* Generate the data */
+                SDL_LockMutex(this->mixer_lock);
+                (*this->callbackspec.callback)(this->callbackspec.userdata,
+                            this->hidden->buffer, this->hidden->bufferSize);
+                SDL_UnlockMutex(this->mixer_lock);
+                this->hidden->bufferOffset = 0;
+            }
+
+            len = this->hidden->bufferSize - this->hidden->bufferOffset;
+            if (len > remaining) {
+                len = remaining;
+            }
+            SDL_memcpy(ptr, (char *)this->hidden->buffer +
+                       this->hidden->bufferOffset, len);
+            ptr = ptr + len;
+            remaining -= len;
+            this->hidden->bufferOffset += len;
+        }
+    }
+
+    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
+
+    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
 }
 
 static void
@@ -439,46 +497,36 @@
               const AudioStreamPacketDescription *inPacketDescs )
 {
     SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
-    if (SDL_AtomicGet(&this->enabled)) {
-        SDL_AudioStream *stream = this->hidden->capturestream;
-        if (SDL_AudioStreamPut(stream, inBuffer->mAudioData, inBuffer->mAudioDataByteSize) == -1) {
-            /* yikes, out of memory or something. I guess drop the buffer. Our WASAPI target kills the device in this case, though */
-        }
-        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
-        this->hidden->refill = SDL_TRUE;
-    }
-}
 
-static int
-COREAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
-{
-    SDL_AudioStream *stream = this->hidden->capturestream;
-    while (SDL_AtomicGet(&this->enabled)) {
-        const int avail = SDL_AudioStreamAvailable(stream);
-        if (avail > 0) {
-            const int cpy = SDL_min(buflen, avail);
-            SDL_AudioStreamGet(stream, buffer, cpy);
-            return cpy;
-        }
-
-        /* wait for more data, try again. */
-        while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
-            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
-        }
-        this->hidden->refill = SDL_FALSE;
+    if (SDL_AtomicGet(&this->shutdown)) {
+        return;  /* don't do anything. */
     }
 
-    return 0;  /* not enabled, giving up. */
-}
+    /* ignore unless we're active. */
+    if (!SDL_AtomicGet(&this->paused) && SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {
+        const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
+        UInt32 remaining = inBuffer->mAudioDataByteSize;
+        while (remaining > 0) {
+            UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
+            if (len > remaining) {
+                len = remaining;
+            }
 
-static void
-COREAUDIO_FlushCapture(_THIS)
-{
-    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1) == kCFRunLoopRunHandledSource) {
-        /* spin. */
+            SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
+            ptr += len;
+            remaining -= len;
+            this->hidden->bufferOffset += len;
+
+            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
+                SDL_LockMutex(this->mixer_lock);
+                (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
+                SDL_UnlockMutex(this->mixer_lock);
+                this->hidden->bufferOffset = 0;
+            }
+        }
     }
-    this->hidden->refill = SDL_FALSE;
-    SDL_AudioStreamClear(this->hidden->capturestream);
+
+    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
 }
 
 
@@ -532,28 +580,37 @@
     AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
 #endif
 
-#if !MACOSX_COREAUDIO
-    update_audio_session(this, SDL_FALSE);
-#endif
-
-    if (this->hidden->audioQueue) {
-        AudioQueueDispose(this->hidden->audioQueue, 1);
-    }
-
-    if (this->hidden->capturestream) {
-        SDL_FreeAudioStream(this->hidden->capturestream);
-    }
-
-    /* AudioQueueDispose() frees the actual buffer objects. */
-    SDL_free(this->hidden->audioBuffer);
-    SDL_free(this->hidden->buffer);
-    SDL_free(this->hidden);
-
     if (iscapture) {
         open_capture_devices--;
     } else {
         open_playback_devices--;
     }
+
+#if !MACOSX_COREAUDIO
+    update_audio_session(this, SDL_FALSE);
+#endif
+
+    /* if callback fires again, feed silence; don't call into the app. */
+    SDL_AtomicSet(&this->paused, 1);
+
+    if (this->hidden->audioQueue) {
+        AudioQueueDispose(this->hidden->audioQueue, 1);
+    }
+
+    if (this->hidden->thread) {
+        SDL_AtomicSet(&this->hidden->shutdown, 1);
+        SDL_WaitThread(this->hidden->thread, NULL);
+    }
+
+    if (this->hidden->ready_semaphore) {
+        SDL_DestroySemaphore(this->hidden->ready_semaphore);
+    }
+
+    /* AudioQueueDispose() frees the actual buffer objects. */
+    SDL_free(this->hidden->audioBuffer);
+    SDL_free(this->hidden->thread_error);
+    SDL_free(this->hidden->buffer);
+    SDL_free(this->hidden);
 }
 
 #if MACOSX_COREAUDIO
@@ -611,8 +668,6 @@
 }
 #endif
 
-
-/* this all happens in the audio thread, since it needs a separate runloop. */
 static int
 prepare_audioqueue(_THIS)
 {
@@ -652,6 +707,54 @@
 }
 #endif
 
+    /* Calculate the final parameters for this audio specification */
+    SDL_CalculateAudioSpec(&this->spec);
+
+    /* Set the channel layout for the audio queue */
+    AudioChannelLayout layout;
+    SDL_zero(layout);
+    switch (this->spec.channels) {
+    case 1:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
+        break;
+    case 2:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
+        break;
+    case 3:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4;
+        break;
+    case 4:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic;
+        break;
+    case 5:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_0_A;
+        break;
+    case 6:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
+        break;
+    case 7:
+        /* FIXME: Need to move channel[4] (BC) to channel[6] */
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A;
+        break;
+    case 8:
+        layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A;
+        break;
+    }
+    if (layout.mChannelLayoutTag != 0) {
+        result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_ChannelLayout, &layout, sizeof(layout));
+        CHECK_RESULT("AudioQueueSetProperty(kAudioQueueProperty_ChannelLayout)");
+    }
+
+    /* Allocate a sample buffer */
+    this->hidden->bufferSize = this->spec.size;
+    this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
+
+    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
+    if (this->hidden->buffer == NULL) {
+        SDL_OutOfMemory();
+        return 0;
+    }
+
     /* Make sure we can feed the device a minimum amount of time */
     double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
 #if defined(__IPHONEOS__)
@@ -666,7 +769,6 @@
         numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
     }
 
-    this->hidden->numAudioBuffers = numAudioBuffers;
     this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
     if (this->hidden->audioBuffer == NULL) {
         SDL_OutOfMemory();
@@ -693,23 +795,31 @@
     return 1;
 }
 
-static void
-COREAUDIO_ThreadInit(_THIS)
+static int
+audioqueue_thread(void *arg)
 {
+    SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
     const int rc = prepare_audioqueue(this);
     if (!rc) {
-        /* !!! FIXME: do this in RunAudio, and maybe block OpenDevice until ThreadInit finishes, too, to report an opening error */
-        SDL_OpenedAudioDeviceDisconnected(this);  /* oh well. */
+        this->hidden->thread_error = SDL_strdup(SDL_GetError());
+        SDL_SemPost(this->hidden->ready_semaphore);
+        return 0;
     }
-}
 
-static void
-COREAUDIO_PrepareToClose(_THIS)
-{
-    /* run long enough to queue some silence, so we know our actual audio
-       has been played */
-    CFRunLoopRunInMode(kCFRunLoopDefaultMode, (((this->spec.samples * 1000) / this->spec.freq) * 2) / 1000.0f, 0);
-    AudioQueueStop(this->hidden->audioQueue, 1);
+    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+
+    /* init was successful, alert parent thread and start running... */
+    SDL_SemPost(this->hidden->ready_semaphore);
+    while (!SDL_AtomicGet(&this->hidden->shutdown)) {
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
+    }
+
+    if (!this->iscapture) {  /* Drain off any pending playback. */
+        const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
+        CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
+    }
+
+    return 0;
 }
 
 static int
@@ -745,6 +855,17 @@
         AVAudioSession* session = [AVAudioSession sharedInstance];
         [session setPreferredSampleRate:this->spec.freq error:nil];
         this->spec.freq = (int)session.sampleRate;
+#if TARGET_OS_TV
+        if (iscapture) {
+            [session setPreferredInputNumberOfChannels:this->spec.channels error:nil];
+            this->spec.channels = session.preferredInputNumberOfChannels;
+        } else {
+            [session setPreferredOutputNumberOfChannels:this->spec.channels error:nil];
+            this->spec.channels = session.preferredOutputNumberOfChannels;
+        }
+#else
+		/* Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS */
+#endif /* TARGET_OS_TV */
     }
 #endif
 
@@ -758,13 +879,11 @@
 
     while ((!valid_datatype) && (test_format)) {
         this->spec.format = test_format;
-        /* Just a list of valid SDL formats, so people don't pass junk here. */
+        /* CoreAudio handles most of SDL's formats natively, but not U16, apparently. */
         switch (test_format) {
         case AUDIO_U8:
         case AUDIO_S8:
-        case AUDIO_U16LSB:
         case AUDIO_S16LSB:
-        case AUDIO_U16MSB:
         case AUDIO_S16MSB:
         case AUDIO_S32LSB:
         case AUDIO_S32MSB:
@@ -780,6 +899,10 @@
             else if (SDL_AUDIO_ISSIGNED(this->spec.format))
                 strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
             break;
+
+        default:
+            test_format = SDL_NextAudioFormat();
+            break;
         }
     }
 
@@ -787,7 +910,7 @@
         return SDL_SetError("Unsupported audio format");
     }
 
-    strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
+    strdesc->mBytesPerFrame = strdesc->mChannelsPerFrame * strdesc->mBitsPerChannel / 8;
     strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
 
 #if MACOSX_COREAUDIO
@@ -796,23 +919,28 @@
     }
 #endif
 
-    /* Calculate the final parameters for this audio specification */
-    SDL_CalculateAudioSpec(&this->spec);
-
-    if (iscapture) {
-        this->hidden->capturestream = SDL_NewAudioStream(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 */
-        }
-    } else {
-        this->hidden->bufferSize = this->spec.size;
-        this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
-        if (this->hidden->buffer == NULL) {
-            return SDL_OutOfMemory();
-        }
+    /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
+    SDL_AtomicSet(&this->hidden->shutdown, 0);
+    this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
+    if (!this->hidden->ready_semaphore) {
+        return -1;  /* oh well. */
     }
 
-    return 0;
+    this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
+    if (!this->hidden->thread) {
+        return -1;
+    }
+
+    SDL_SemWait(this->hidden->ready_semaphore);
+    SDL_DestroySemaphore(this->hidden->ready_semaphore);
+    this->hidden->ready_semaphore = NULL;
+
+    if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
+        SDL_SetError("%s", this->hidden->thread_error);
+        return -1;
+    }
+
+    return (this->hidden->thread != NULL) ? 0 : -1;
 }
 
 static void
@@ -832,12 +960,6 @@
     impl->OpenDevice = COREAUDIO_OpenDevice;
     impl->CloseDevice = COREAUDIO_CloseDevice;
     impl->Deinitialize = COREAUDIO_Deinitialize;
-    impl->ThreadInit = COREAUDIO_ThreadInit;
-    impl->WaitDevice = COREAUDIO_WaitDevice;
-    impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf;
-    impl->PrepareToClose = COREAUDIO_PrepareToClose;
-    impl->CaptureFromDevice = COREAUDIO_CaptureFromDevice;
-    impl->FlushCapture = COREAUDIO_FlushCapture;
 
 #if MACOSX_COREAUDIO
     impl->DetectDevices = COREAUDIO_DetectDevices;
@@ -847,6 +969,7 @@
     impl->OnlyHasDefaultCaptureDevice = 1;
 #endif
 
+    impl->ProvidesOwnCallbackThread = 1;
     impl->HasCaptureSupport = 1;
 
     return 1;   /* this audio target is available. */

--
Gitblit v1.9.3