Mac and Linux SDL2 binary snapshots
Edward Rudd
2018-08-19 561f0d614098a95527367cc3f911e476f35643d6
source/src/audio/coreaudio/SDL_coreaudio.m
@@ -1,6 +1,6 @@
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  Copyright (C) 1997-2018 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
@@ -25,6 +25,7 @@
/* !!! FIXME: clean out some of the macro salsa in here. */
#include "SDL_audio.h"
#include "SDL_hints.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_coreaudio.h"
@@ -285,9 +286,9 @@
static void interruption_end(_THIS)
{
    if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
    && this->hidden->interrupted) {
    && this->hidden->interrupted
    && AudioQueueStart(this->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
        this->hidden->interrupted = SDL_FALSE;
        AudioQueueStart(this->hidden->audioQueue, NULL);
    }
}
@@ -325,7 +326,8 @@
    @autoreleasepool {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        NSString *category;
        /* Set category to ambient by default so that other music continues playing. */
        NSString *category = AVAudioSessionCategoryAmbient;
        NSError *err = nil;
        if (open_playback_devices && open_capture_devices) {
@@ -333,10 +335,17 @@
        } else if (open_capture_devices) {
            category = AVAudioSessionCategoryRecord;
        } else {
            /* Set category to ambient so that other music continues playing.
             You can change this at runtime in your own code if you need different
             behavior. If this is common, we can add an SDL hint for this. */
            category = AVAudioSessionCategoryAmbient;
            const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
            if (hint) {
                if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
                    category = AVAudioSessionCategoryAmbient;
                } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
                    category = AVAudioSessionCategorySoloAmbient;
                } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
                           SDL_strcasecmp(hint, "playback") == 0) {
                    category = AVAudioSessionCategoryPlayback;
                }
            }
        }
        if (![session setCategory:category error:&err]) {
@@ -400,8 +409,12 @@
outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
{
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
    if (SDL_AtomicGet(&this->hidden->shutdown)) {
        return;  /* don't do anything. */
    }
    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
        /* Supply silence if audio is enabled and not paused */
        /* Supply silence if audio is not enabled or paused */
        SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
    } else {
        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
@@ -412,7 +425,7 @@
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
                /* Generate the data */
                SDL_LockMutex(this->mixer_lock);
                (*this->spec.callback)(this->spec.userdata,
                (*this->callbackspec.callback)(this->callbackspec.userdata,
                            this->hidden->buffer, this->hidden->bufferSize);
                SDL_UnlockMutex(this->mixer_lock);
                this->hidden->bufferOffset = 0;
@@ -430,9 +443,7 @@
        }
    }
    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
    }
    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
}
@@ -443,7 +454,13 @@
              const AudioStreamPacketDescription *inPacketDescs )
{
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
    if (SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {  /* ignore unless we're active. */
    if (SDL_AtomicGet(&this->shutdown)) {
        return;  /* don't do anything. */
    }
    /* 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) {
@@ -459,16 +476,14 @@
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
                SDL_LockMutex(this->mixer_lock);
                (*this->spec.callback)(this->spec.userdata, this->hidden->buffer, this->hidden->bufferSize);
                (*this->callbackspec.callback)(this->callbackspec.userdata, this->hidden->buffer, this->hidden->bufferSize);
                SDL_UnlockMutex(this->mixer_lock);
                this->hidden->bufferOffset = 0;
            }
        }
    }
    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
    }
    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
}
@@ -514,7 +529,6 @@
COREAUDIO_CloseDevice(_THIS)
{
    const SDL_bool iscapture = this->iscapture;
    int i;
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
@@ -527,24 +541,24 @@
    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->audioQueue) {
        for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
            if (this->hidden->audioBuffer[i]) {
                AudioQueueFreeBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i]);
            }
        }
        AudioQueueDispose(this->hidden->audioQueue, 1);
    }
    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);
@@ -663,7 +677,31 @@
        return 0;
    }
    for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
    /* Make sure we can feed the device a minimum amount of time */
    double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
#if defined(__IPHONEOS__)
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
        /* Older iOS hardware, use 40 ms as a minimum time */
        MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
    }
#endif
    const double msecs = (this->spec.samples / ((double) this->spec.freq)) * 1000.0;
    int numAudioBuffers = 2;
    if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {  /* use more buffers if we have a VERY small sample set. */
        numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
    }
    this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
    if (this->hidden->audioBuffer == NULL) {
        SDL_OutOfMemory();
        return 0;
    }
#if DEBUG_COREAUDIO
    printf("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
#endif
    for (i = 0; i < numAudioBuffers; i++) {
        result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
        CHECK_RESULT("AudioQueueAllocateBuffer");
        SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
@@ -696,10 +734,7 @@
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
    }
    if (this->iscapture) {  /* just stop immediately for capture devices. */
        AudioQueueStop(this->hidden->audioQueue, 1);
    } else {  /* Drain off any pending playback. */
        AudioQueueStop(this->hidden->audioQueue, 0);
    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);
    }
@@ -734,6 +769,13 @@
    if (!update_audio_session(this, SDL_TRUE)) {
        return -1;
    }
    /* Stop CoreAudio from doing expensive audio rate conversion */
    @autoreleasepool {
        AVAudioSession* session = [AVAudioSession sharedInstance];
        [session setPreferredSampleRate:this->spec.freq error:nil];
        this->spec.freq = (int)session.sampleRate;
    }
#endif
    /* Setup a AudioStreamBasicDescription with the requested format */