src/audio/coreaudio/SDL_coreaudio.m
author Sam Lantinga <slouken@libsdl.org>
Thu, 24 May 2018 07:30:24 -0700
changeset 12001 84c2582329b0
parent 11945 ffd52bb02bcc
child 12503 806492103856
permissions -rw-r--r--
Fixed bug 4169 - Crash due to audio session observer race condition

Jona

The following explains why this bug was happening:
This crash was caused because the audio session was being set as active [session setActive:YES error:&err] when the audio device was actually being CLOSED. Certain cases the audio session being set to active would fail and the method would return right away. Because of the way the error was handled we never removed the SDLInterruptionListener thus leaking it. Later when an interruption was received the THIS_ object would contain a pointer to an already released device causing the crash.

The fix:
When only one device remained open and it was being closed we needed to set the audio session as NOT active and completely ignore the returned error to successfully release the SDLInterruptionListener. I think the user assumed that the open_playback_devices and open_capture_devices would equal 0 when all of them where closed but the truth is that at the end of the closing process that the open devices count is decremented.
slouken@935
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
slouken@935
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@935
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@935
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@935
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
philipp@9332
    22
philipp@9332
    23
#if SDL_AUDIO_DRIVER_COREAUDIO
philipp@9332
    24
icculus@10255
    25
/* !!! FIXME: clean out some of the macro salsa in here. */
icculus@10255
    26
slouken@935
    27
#include "SDL_audio.h"
slouken@11501
    28
#include "SDL_hints.h"
icculus@11945
    29
#include "SDL_timer.h"
slouken@1361
    30
#include "../SDL_audio_c.h"
slouken@1361
    31
#include "../SDL_sysaudio.h"
slouken@935
    32
#include "SDL_coreaudio.h"
icculus@6281
    33
#include "SDL_assert.h"
icculus@10309
    34
#include "../../thread/SDL_systhread.h"
slouken@935
    35
icculus@2049
    36
#define DEBUG_COREAUDIO 0
slouken@935
    37
icculus@5596
    38
#define CHECK_RESULT(msg) \
icculus@5596
    39
    if (result != noErr) { \
icculus@5596
    40
        SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
icculus@5596
    41
        return 0; \
icculus@5596
    42
    }
icculus@5596
    43
icculus@5596
    44
#if MACOSX_COREAUDIO
icculus@9393
    45
static const AudioObjectPropertyAddress devlist_address = {
icculus@9393
    46
    kAudioHardwarePropertyDevices,
icculus@9393
    47
    kAudioObjectPropertyScopeGlobal,
icculus@9393
    48
    kAudioObjectPropertyElementMaster
icculus@9393
    49
};
icculus@9393
    50
icculus@9393
    51
typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
icculus@9393
    52
icculus@9393
    53
typedef struct AudioDeviceList
icculus@9393
    54
{
icculus@9393
    55
    AudioDeviceID devid;
icculus@9393
    56
    SDL_bool alive;
icculus@9393
    57
    struct AudioDeviceList *next;
icculus@9393
    58
} AudioDeviceList;
icculus@9393
    59
icculus@9393
    60
static AudioDeviceList *output_devs = NULL;
icculus@9393
    61
static AudioDeviceList *capture_devs = NULL;
icculus@9393
    62
icculus@9393
    63
static SDL_bool
icculus@9393
    64
add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
icculus@9393
    65
{
icculus@9393
    66
    AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
icculus@9393
    67
    if (item == NULL) {
icculus@9393
    68
        return SDL_FALSE;
icculus@9393
    69
    }
icculus@9393
    70
    item->devid = devId;
icculus@9393
    71
    item->alive = SDL_TRUE;
icculus@9393
    72
    item->next = iscapture ? capture_devs : output_devs;
icculus@9393
    73
    if (iscapture) {
icculus@9393
    74
        capture_devs = item;
icculus@9393
    75
    } else {
icculus@9393
    76
        output_devs = item;
icculus@9393
    77
    }
icculus@9393
    78
icculus@9393
    79
    return SDL_TRUE;
icculus@9393
    80
}
slouken@935
    81
icculus@2049
    82
static void
icculus@9393
    83
addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
icculus@2049
    84
{
icculus@9393
    85
    if (add_to_internal_dev_list(iscapture, devId)) {
icculus@9394
    86
        SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
icculus@5593
    87
    }
icculus@5593
    88
}
icculus@5593
    89
icculus@5593
    90
static void
icculus@5593
    91
build_device_list(int iscapture, addDevFn addfn, void *addfndata)
icculus@2049
    92
{
icculus@2049
    93
    OSStatus result = noErr;
icculus@2049
    94
    UInt32 size = 0;
icculus@2049
    95
    AudioDeviceID *devs = NULL;
icculus@2049
    96
    UInt32 i = 0;
icculus@2049
    97
    UInt32 max = 0;
icculus@2049
    98
icculus@9393
    99
    result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
icculus@9393
   100
                                            &devlist_address, 0, NULL, &size);
icculus@2049
   101
    if (result != kAudioHardwareNoError)
icculus@2049
   102
        return;
icculus@2049
   103
icculus@2049
   104
    devs = (AudioDeviceID *) alloca(size);
icculus@2049
   105
    if (devs == NULL)
icculus@2049
   106
        return;
icculus@2049
   107
icculus@9393
   108
    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
icculus@9393
   109
                                        &devlist_address, 0, NULL, &size, devs);
icculus@2049
   110
    if (result != kAudioHardwareNoError)
icculus@2049
   111
        return;
icculus@2049
   112
icculus@5593
   113
    max = size / sizeof (AudioDeviceID);
icculus@2049
   114
    for (i = 0; i < max; i++) {
icculus@2049
   115
        CFStringRef cfstr = NULL;
icculus@2049
   116
        char *ptr = NULL;
icculus@2049
   117
        AudioDeviceID dev = devs[i];
icculus@2049
   118
        AudioBufferList *buflist = NULL;
icculus@2049
   119
        int usable = 0;
icculus@2049
   120
        CFIndex len = 0;
icculus@9393
   121
        const AudioObjectPropertyAddress addr = {
icculus@9393
   122
            kAudioDevicePropertyStreamConfiguration,
icculus@9393
   123
            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@9393
   124
            kAudioObjectPropertyElementMaster
icculus@9393
   125
        };
icculus@2049
   126
icculus@9393
   127
        const AudioObjectPropertyAddress nameaddr = {
icculus@9393
   128
            kAudioObjectPropertyName,
icculus@9393
   129
            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@9393
   130
            kAudioObjectPropertyElementMaster
icculus@9393
   131
        };
icculus@5643
   132
icculus@5643
   133
        result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
icculus@2049
   134
        if (result != noErr)
icculus@2049
   135
            continue;
icculus@2049
   136
icculus@2049
   137
        buflist = (AudioBufferList *) SDL_malloc(size);
icculus@2049
   138
        if (buflist == NULL)
icculus@2049
   139
            continue;
icculus@2049
   140
icculus@5643
   141
        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
icculus@5643
   142
                                            &size, buflist);
icculus@2049
   143
icculus@2049
   144
        if (result == noErr) {
icculus@2049
   145
            UInt32 j;
icculus@2049
   146
            for (j = 0; j < buflist->mNumberBuffers; j++) {
icculus@2049
   147
                if (buflist->mBuffers[j].mNumberChannels > 0) {
icculus@2049
   148
                    usable = 1;
icculus@2049
   149
                    break;
icculus@2049
   150
                }
icculus@2049
   151
            }
icculus@2049
   152
        }
icculus@2049
   153
icculus@2049
   154
        SDL_free(buflist);
icculus@2049
   155
icculus@2049
   156
        if (!usable)
icculus@2049
   157
            continue;
icculus@2049
   158
icculus@9393
   159
icculus@5643
   160
        size = sizeof (CFStringRef);
icculus@9393
   161
        result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
icculus@2049
   162
        if (result != kAudioHardwareNoError)
icculus@2049
   163
            continue;
icculus@2049
   164
icculus@2049
   165
        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
icculus@2049
   166
                                                kCFStringEncodingUTF8);
icculus@2049
   167
icculus@2049
   168
        ptr = (char *) SDL_malloc(len + 1);
slouken@2060
   169
        usable = ((ptr != NULL) &&
slouken@2060
   170
                  (CFStringGetCString
slouken@2060
   171
                   (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
icculus@2049
   172
icculus@2049
   173
        CFRelease(cfstr);
icculus@2049
   174
icculus@2049
   175
        if (usable) {
icculus@2049
   176
            len = strlen(ptr);
icculus@2049
   177
            /* Some devices have whitespace at the end...trim it. */
slouken@2060
   178
            while ((len > 0) && (ptr[len - 1] == ' ')) {
icculus@2049
   179
                len--;
icculus@2049
   180
            }
icculus@2049
   181
            usable = (len > 0);
icculus@2049
   182
        }
icculus@2049
   183
icculus@5593
   184
        if (usable) {
icculus@2049
   185
            ptr[len] = '\0';
icculus@2049
   186
slouken@2060
   187
#if DEBUG_COREAUDIO
icculus@2049
   188
            printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
slouken@2060
   189
                   ((iscapture) ? "capture" : "output"),
icculus@10232
   190
                   (int) i, ptr, (int) dev);
slouken@2060
   191
#endif
icculus@9393
   192
            addfn(ptr, iscapture, dev, addfndata);
icculus@2049
   193
        }
icculus@5593
   194
        SDL_free(ptr);  /* addfn() would have copied the string. */
icculus@2049
   195
    }
icculus@2049
   196
}
icculus@2049
   197
icculus@5593
   198
static void
icculus@9393
   199
free_audio_device_list(AudioDeviceList **list)
icculus@9393
   200
{
icculus@9393
   201
    AudioDeviceList *item = *list;
icculus@9393
   202
    while (item) {
icculus@9393
   203
        AudioDeviceList *next = item->next;
icculus@9393
   204
        SDL_free(item);
icculus@9393
   205
        item = next;
icculus@9393
   206
    }
icculus@9393
   207
    *list = NULL;
icculus@9393
   208
}
icculus@9393
   209
icculus@9393
   210
static void
icculus@9394
   211
COREAUDIO_DetectDevices(void)
icculus@2049
   212
{
icculus@9394
   213
    build_device_list(SDL_TRUE, addToDevList, NULL);
icculus@9394
   214
    build_device_list(SDL_FALSE, addToDevList, NULL);
slouken@935
   215
}
slouken@935
   216
icculus@9393
   217
static void
icculus@9393
   218
build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
icculus@9393
   219
{
icculus@9393
   220
    AudioDeviceList **list = (AudioDeviceList **) data;
icculus@9393
   221
    AudioDeviceList *item;
icculus@9393
   222
    for (item = *list; item != NULL; item = item->next) {
icculus@9393
   223
        if (item->devid == devId) {
icculus@9393
   224
            item->alive = SDL_TRUE;
icculus@9393
   225
            return;
icculus@9393
   226
        }
icculus@9393
   227
    }
icculus@9393
   228
icculus@9393
   229
    add_to_internal_dev_list(iscapture, devId);  /* new device, add it. */
icculus@9394
   230
    SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
icculus@9393
   231
}
icculus@9393
   232
icculus@9394
   233
static void
icculus@9393
   234
reprocess_device_list(const int iscapture, AudioDeviceList **list)
icculus@9393
   235
{
icculus@9393
   236
    AudioDeviceList *item;
icculus@9393
   237
    AudioDeviceList *prev = NULL;
icculus@9393
   238
    for (item = *list; item != NULL; item = item->next) {
icculus@9393
   239
        item->alive = SDL_FALSE;
icculus@9393
   240
    }
icculus@9393
   241
icculus@9393
   242
    build_device_list(iscapture, build_device_change_list, list);
icculus@9393
   243
icculus@9393
   244
    /* free items in the list that aren't still alive. */
icculus@9393
   245
    item = *list;
icculus@9393
   246
    while (item != NULL) {
icculus@9393
   247
        AudioDeviceList *next = item->next;
icculus@9393
   248
        if (item->alive) {
icculus@9393
   249
            prev = item;
icculus@9393
   250
        } else {
icculus@9399
   251
            SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
icculus@9393
   252
            if (prev) {
icculus@9393
   253
                prev->next = item->next;
icculus@9393
   254
            } else {
icculus@9393
   255
                *list = item->next;
icculus@9393
   256
            }
icculus@9393
   257
            SDL_free(item);
icculus@9393
   258
        }
icculus@9393
   259
        item = next;
icculus@9393
   260
    }
icculus@9393
   261
}
icculus@9393
   262
icculus@9393
   263
/* this is called when the system's list of available audio devices changes. */
icculus@9393
   264
static OSStatus
icculus@9393
   265
device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
icculus@9393
   266
{
icculus@9394
   267
    reprocess_device_list(SDL_TRUE, &capture_devs);
icculus@9394
   268
    reprocess_device_list(SDL_FALSE, &output_devs);
icculus@9393
   269
    return 0;
icculus@9393
   270
}
icculus@5596
   271
#endif
icculus@5596
   272
icculus@10245
   273
icculus@10245
   274
static int open_playback_devices = 0;
icculus@10245
   275
static int open_capture_devices = 0;
icculus@10245
   276
slime73@10346
   277
#if !MACOSX_COREAUDIO
slime73@10357
   278
slime73@10357
   279
static void interruption_begin(_THIS)
slime73@10357
   280
{
slime73@10357
   281
    if (this != NULL && this->hidden->audioQueue != NULL) {
slime73@10357
   282
        this->hidden->interrupted = SDL_TRUE;
slime73@10357
   283
        AudioQueuePause(this->hidden->audioQueue);
slime73@10357
   284
    }
slime73@10357
   285
}
slime73@10357
   286
slime73@10357
   287
static void interruption_end(_THIS)
slime73@10357
   288
{
slime73@10357
   289
    if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
slime73@10987
   290
    && this->hidden->interrupted
slime73@10987
   291
    && AudioQueueStart(this->hidden->audioQueue, NULL) == AVAudioSessionErrorCodeNone) {
slime73@10357
   292
        this->hidden->interrupted = SDL_FALSE;
slime73@10357
   293
    }
slime73@10357
   294
}
slime73@10357
   295
slime73@10357
   296
@interface SDLInterruptionListener : NSObject
slime73@10357
   297
slime73@10357
   298
@property (nonatomic, assign) SDL_AudioDevice *device;
slime73@10357
   299
slime73@10357
   300
@end
slime73@10357
   301
slime73@10357
   302
@implementation SDLInterruptionListener
slime73@10357
   303
slime73@10357
   304
- (void)audioSessionInterruption:(NSNotification *)note
slime73@10357
   305
{
slime73@10357
   306
    @synchronized (self) {
slime73@10357
   307
        NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
slime73@10357
   308
        if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
slime73@10357
   309
            interruption_begin(self.device);
slime73@10357
   310
        } else {
slime73@10357
   311
            interruption_end(self.device);
slime73@10357
   312
        }
slime73@10357
   313
    }
slime73@10357
   314
}
slime73@10357
   315
slime73@10357
   316
- (void)applicationBecameActive:(NSNotification *)note
slime73@10357
   317
{
slime73@10357
   318
    @synchronized (self) {
slime73@10357
   319
        interruption_end(self.device);
slime73@10357
   320
    }
slime73@10357
   321
}
slime73@10357
   322
slime73@10357
   323
@end
slime73@10357
   324
slime73@10357
   325
static BOOL update_audio_session(_THIS, SDL_bool open)
icculus@10245
   326
{
slime73@10346
   327
    @autoreleasepool {
slime73@10346
   328
        AVAudioSession *session = [AVAudioSession sharedInstance];
slime73@10357
   329
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slouken@11523
   330
        /* Set category to ambient by default so that other music continues playing. */
slouken@11501
   331
        NSString *category = AVAudioSessionCategoryAmbient;
slime73@10346
   332
        NSError *err = nil;
slime73@10346
   333
slime73@10346
   334
        if (open_playback_devices && open_capture_devices) {
slime73@10346
   335
            category = AVAudioSessionCategoryPlayAndRecord;
slime73@10346
   336
        } else if (open_capture_devices) {
slime73@10346
   337
            category = AVAudioSessionCategoryRecord;
slime73@10346
   338
        } else {
slouken@11501
   339
            const char *hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
slouken@11501
   340
            if (hint) {
slouken@11501
   341
                if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0) {
slouken@11501
   342
                    category = AVAudioSessionCategoryAmbient;
slouken@11501
   343
                } else if (SDL_strcasecmp(hint, "AVAudioSessionCategorySoloAmbient") == 0) {
slouken@11501
   344
                    category = AVAudioSessionCategorySoloAmbient;
slouken@11501
   345
                } else if (SDL_strcasecmp(hint, "AVAudioSessionCategoryPlayback") == 0 ||
slouken@11501
   346
                           SDL_strcasecmp(hint, "playback") == 0) {
slouken@11501
   347
                    category = AVAudioSessionCategoryPlayback;
slouken@11501
   348
                }
slouken@11501
   349
            }
slime73@10346
   350
        }
slime73@10346
   351
slime73@10357
   352
        if (![session setCategory:category error:&err]) {
slime73@10357
   353
            NSString *desc = err.description;
slime73@10357
   354
            SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
slime73@10357
   355
            return NO;
slime73@10357
   356
        }
slime73@10357
   357
slouken@12001
   358
        if (open && (open_playback_devices + open_capture_devices) == 1) {
slime73@10346
   359
            if (![session setActive:YES error:&err]) {
slime73@10346
   360
                NSString *desc = err.description;
slime73@10346
   361
                SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
slime73@10346
   362
                return NO;
slime73@10346
   363
            }
slime73@10346
   364
        } else if (!open_playback_devices && !open_capture_devices) {
slime73@10346
   365
            [session setActive:NO error:nil];
slime73@10346
   366
        }
slime73@10346
   367
slime73@10357
   368
        if (open) {
slime73@10357
   369
            SDLInterruptionListener *listener = [SDLInterruptionListener new];
slime73@10357
   370
            listener.device = this;
slime73@10357
   371
slime73@10357
   372
            [center addObserver:listener
slime73@10357
   373
                       selector:@selector(audioSessionInterruption:)
slime73@10357
   374
                           name:AVAudioSessionInterruptionNotification
slime73@10357
   375
                         object:session];
slime73@10357
   376
slime73@10357
   377
            /* An interruption end notification is not guaranteed to be sent if
slime73@10357
   378
             we were previously interrupted... resuming if needed when the app
slime73@10357
   379
             becomes active seems to be the way to go. */
slime73@10357
   380
            [center addObserver:listener
slime73@10357
   381
                       selector:@selector(applicationBecameActive:)
slime73@10357
   382
                           name:UIApplicationDidBecomeActiveNotification
slime73@10357
   383
                         object:session];
slime73@10357
   384
slime73@10357
   385
            [center addObserver:listener
slime73@10357
   386
                       selector:@selector(applicationBecameActive:)
slime73@10357
   387
                           name:UIApplicationWillEnterForegroundNotification
slime73@10357
   388
                         object:session];
slime73@10357
   389
slime73@10357
   390
            this->hidden->interruption_listener = CFBridgingRetain(listener);
slime73@10357
   391
        } else {
slime73@10357
   392
            if (this->hidden->interruption_listener != NULL) {
slime73@10357
   393
                SDLInterruptionListener *listener = nil;
slime73@10357
   394
                listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
slouken@12001
   395
                [center removeObserver:listener];
slime73@10357
   396
                @synchronized (listener) {
slime73@10357
   397
                    listener.device = NULL;
slime73@10357
   398
                }
slime73@10357
   399
            }
slime73@10346
   400
        }
icculus@10245
   401
    }
slime73@10346
   402
slime73@10346
   403
    return YES;
slime73@10346
   404
}
icculus@10245
   405
#endif
icculus@10245
   406
icculus@10245
   407
icculus@10309
   408
/* The AudioQueue callback */
icculus@10309
   409
static void
icculus@10309
   410
outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
slouken@935
   411
{
icculus@10309
   412
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
icculus@11945
   413
    SDL_assert(inBuffer->mAudioDataBytesCapacity == this->hidden->bufferSize);
icculus@11945
   414
    SDL_memcpy(inBuffer->mAudioData, this->hidden->buffer, this->hidden->bufferSize);
icculus@11945
   415
    SDL_memset(this->hidden->buffer, '\0', this->hidden->bufferSize);  /* zero out in case we have to fill again without new data. */
icculus@11945
   416
    inBuffer->mAudioDataByteSize = this->hidden->bufferSize;
icculus@11945
   417
    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
icculus@11945
   418
    this->hidden->refill = SDL_TRUE;
icculus@11945
   419
}
icculus@11945
   420
icculus@11945
   421
static Uint8 *
icculus@11945
   422
COREAUDIO_GetDeviceBuf(_THIS)
icculus@11945
   423
{
icculus@11945
   424
    return this->hidden->buffer;
icculus@11945
   425
}
icculus@11945
   426
icculus@11945
   427
static void
icculus@11945
   428
COREAUDIO_WaitDevice(_THIS)
icculus@11945
   429
{
icculus@11945
   430
    while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
icculus@11945
   431
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
icculus@11026
   432
    }
icculus@11945
   433
    this->hidden->refill = SDL_FALSE;
slouken@935
   434
}
slouken@935
   435
icculus@10309
   436
static void
icculus@10309
   437
inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
icculus@10309
   438
              const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
icculus@10309
   439
              const AudioStreamPacketDescription *inPacketDescs )
slouken@935
   440
{
icculus@10309
   441
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
icculus@11945
   442
    if (SDL_AtomicGet(&this->enabled)) {
icculus@11945
   443
        SDL_AudioStream *stream = this->hidden->capturestream;
icculus@11945
   444
        if (SDL_AudioStreamPut(stream, inBuffer->mAudioData, inBuffer->mAudioDataByteSize) == -1) {
icculus@11945
   445
            /* yikes, out of memory or something. I guess drop the buffer. Our WASAPI target kills the device in this case, though */
icculus@11945
   446
        }
icculus@11945
   447
        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
icculus@11945
   448
        this->hidden->refill = SDL_TRUE;
icculus@11945
   449
    }
icculus@11945
   450
}
icculus@11026
   451
icculus@11945
   452
static int
icculus@11945
   453
COREAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
icculus@11945
   454
{
icculus@11945
   455
    SDL_AudioStream *stream = this->hidden->capturestream;
icculus@11945
   456
    while (SDL_AtomicGet(&this->enabled)) {
icculus@11945
   457
        const int avail = SDL_AudioStreamAvailable(stream);
icculus@11945
   458
        if (avail > 0) {
icculus@11945
   459
            const int cpy = SDL_min(buflen, avail);
icculus@11945
   460
            SDL_AudioStreamGet(stream, buffer, cpy);
icculus@11945
   461
            return cpy;
icculus@11945
   462
        }
icculus@11945
   463
icculus@11945
   464
        /* wait for more data, try again. */
icculus@11945
   465
        while (SDL_AtomicGet(&this->enabled) && !this->hidden->refill) {
icculus@11945
   466
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
icculus@11945
   467
        }
icculus@11945
   468
        this->hidden->refill = SDL_FALSE;
icculus@11026
   469
    }
icculus@11026
   470
icculus@11945
   471
    return 0;  /* not enabled, giving up. */
icculus@11945
   472
}
icculus@10232
   473
icculus@11945
   474
static void
icculus@11945
   475
COREAUDIO_FlushCapture(_THIS)
icculus@11945
   476
{
icculus@11945
   477
    while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 1) == kCFRunLoopRunHandledSource) {
icculus@11945
   478
        /* spin. */
icculus@10232
   479
    }
icculus@11945
   480
    this->hidden->refill = SDL_FALSE;
icculus@11945
   481
    SDL_AudioStreamClear(this->hidden->capturestream);
slouken@935
   482
}
slouken@935
   483
icculus@2049
   484
icculus@9393
   485
#if MACOSX_COREAUDIO
icculus@9393
   486
static const AudioObjectPropertyAddress alive_address =
icculus@9393
   487
{
icculus@9393
   488
    kAudioDevicePropertyDeviceIsAlive,
icculus@9393
   489
    kAudioObjectPropertyScopeGlobal,
icculus@9393
   490
    kAudioObjectPropertyElementMaster
icculus@9393
   491
};
icculus@9393
   492
icculus@9393
   493
static OSStatus
icculus@9393
   494
device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
icculus@9393
   495
{
icculus@9393
   496
    SDL_AudioDevice *this = (SDL_AudioDevice *) data;
icculus@9393
   497
    SDL_bool dead = SDL_FALSE;
icculus@9393
   498
    UInt32 isAlive = 1;
icculus@9393
   499
    UInt32 size = sizeof (isAlive);
icculus@9393
   500
    OSStatus error;
icculus@9393
   501
icculus@10238
   502
    if (!SDL_AtomicGet(&this->enabled)) {
icculus@9393
   503
        return 0;  /* already known to be dead. */
icculus@9393
   504
    }
icculus@9393
   505
icculus@9393
   506
    error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
icculus@9393
   507
                                       0, NULL, &size, &isAlive);
icculus@9393
   508
icculus@9393
   509
    if (error == kAudioHardwareBadDeviceError) {
icculus@9393
   510
        dead = SDL_TRUE;  /* device was unplugged. */
icculus@9393
   511
    } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
icculus@9393
   512
        dead = SDL_TRUE;  /* device died in some other way. */
icculus@9393
   513
    }
icculus@9393
   514
icculus@9393
   515
    if (dead) {
icculus@9394
   516
        SDL_OpenedAudioDeviceDisconnected(this);
icculus@9393
   517
    }
icculus@9393
   518
icculus@9393
   519
    return 0;
icculus@9393
   520
}
icculus@9393
   521
#endif
icculus@9393
   522
icculus@2049
   523
static void
icculus@2049
   524
COREAUDIO_CloseDevice(_THIS)
slouken@935
   525
{
icculus@10309
   526
    const SDL_bool iscapture = this->iscapture;
icculus@10306
   527
icculus@10309
   528
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
icculus@10309
   529
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
icculus@10309
   530
#if MACOSX_COREAUDIO
icculus@10309
   531
    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
icculus@10309
   532
    AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
icculus@10309
   533
#endif
icculus@10306
   534
slime73@10357
   535
#if !MACOSX_COREAUDIO
slime73@10357
   536
    update_audio_session(this, SDL_FALSE);
slime73@10357
   537
#endif
slime73@10357
   538
icculus@11620
   539
    if (this->hidden->audioQueue) {
icculus@11620
   540
        AudioQueueDispose(this->hidden->audioQueue, 1);
icculus@11620
   541
    }
icculus@11620
   542
icculus@11945
   543
    if (this->hidden->capturestream) {
icculus@11945
   544
        SDL_FreeAudioStream(this->hidden->capturestream);
icculus@10309
   545
    }
icculus@10309
   546
icculus@11026
   547
    /* AudioQueueDispose() frees the actual buffer objects. */
icculus@11026
   548
    SDL_free(this->hidden->audioBuffer);
icculus@10255
   549
    SDL_free(this->hidden->buffer);
icculus@10255
   550
    SDL_free(this->hidden);
icculus@10245
   551
icculus@10255
   552
    if (iscapture) {
icculus@10255
   553
        open_capture_devices--;
icculus@10255
   554
    } else {
icculus@10255
   555
        open_playback_devices--;
icculus@2049
   556
    }
slouken@935
   557
}
slouken@935
   558
icculus@9394
   559
#if MACOSX_COREAUDIO
icculus@2049
   560
static int
icculus@9394
   561
prepare_device(_THIS, void *handle, int iscapture)
icculus@9394
   562
{
icculus@9394
   563
    AudioDeviceID devid = (AudioDeviceID) ((size_t) handle);
icculus@9394
   564
    OSStatus result = noErr;
icculus@9394
   565
    UInt32 size = 0;
icculus@9394
   566
    UInt32 alive = 0;
icculus@9394
   567
    pid_t pid = 0;
icculus@9394
   568
icculus@9394
   569
    AudioObjectPropertyAddress addr = {
icculus@9394
   570
        0,
icculus@9394
   571
        kAudioObjectPropertyScopeGlobal,
icculus@9394
   572
        kAudioObjectPropertyElementMaster
icculus@9394
   573
    };
icculus@9394
   574
icculus@9394
   575
    if (handle == NULL) {
icculus@9394
   576
        size = sizeof (AudioDeviceID);
icculus@9394
   577
        addr.mSelector =
icculus@9394
   578
            ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
icculus@9394
   579
            kAudioHardwarePropertyDefaultOutputDevice);
icculus@9394
   580
        result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
icculus@9394
   581
                                            0, NULL, &size, &devid);
icculus@9394
   582
        CHECK_RESULT("AudioHardwareGetProperty (default device)");
icculus@9394
   583
    }
icculus@9394
   584
icculus@9394
   585
    addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
icculus@9394
   586
    addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
icculus@9394
   587
                    kAudioDevicePropertyScopeOutput;
icculus@9394
   588
icculus@9394
   589
    size = sizeof (alive);
icculus@9394
   590
    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
icculus@9394
   591
    CHECK_RESULT
icculus@9394
   592
        ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
icculus@9394
   593
icculus@9394
   594
    if (!alive) {
icculus@9394
   595
        SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
icculus@9394
   596
        return 0;
icculus@9394
   597
    }
icculus@9394
   598
icculus@9394
   599
    addr.mSelector = kAudioDevicePropertyHogMode;
icculus@9394
   600
    size = sizeof (pid);
icculus@9394
   601
    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
icculus@9394
   602
icculus@9394
   603
    /* some devices don't support this property, so errors are fine here. */
icculus@9394
   604
    if ((result == noErr) && (pid != -1)) {
icculus@9394
   605
        SDL_SetError("CoreAudio: requested device is being hogged.");
icculus@9394
   606
        return 0;
icculus@9394
   607
    }
icculus@9394
   608
icculus@9394
   609
    this->hidden->deviceID = devid;
icculus@9394
   610
    return 1;
icculus@9394
   611
}
icculus@9394
   612
#endif
icculus@9394
   613
icculus@11945
   614
icculus@11945
   615
/* this all happens in the audio thread, since it needs a separate runloop. */
icculus@9394
   616
static int
icculus@10309
   617
prepare_audioqueue(_THIS)
slouken@935
   618
{
icculus@10309
   619
    const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
icculus@10309
   620
    const int iscapture = this->iscapture;
icculus@10309
   621
    OSStatus result;
icculus@10309
   622
    int i;
icculus@2049
   623
icculus@10309
   624
    SDL_assert(CFRunLoopGetCurrent() != NULL);
slouken@7191
   625
icculus@10309
   626
    if (iscapture) {
icculus@10309
   627
        result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
icculus@10309
   628
        CHECK_RESULT("AudioQueueNewInput");
icculus@10309
   629
    } else {
icculus@10309
   630
        result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
icculus@10309
   631
        CHECK_RESULT("AudioQueueNewOutput");
icculus@10232
   632
    }
icculus@10232
   633
icculus@10245
   634
#if MACOSX_COREAUDIO
icculus@10309
   635
{
icculus@10309
   636
    const AudioObjectPropertyAddress prop = {
icculus@10309
   637
        kAudioDevicePropertyDeviceUID,
icculus@10309
   638
        iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@10309
   639
        kAudioObjectPropertyElementMaster
icculus@10309
   640
    };
icculus@10309
   641
    CFStringRef devuid;
icculus@10309
   642
    UInt32 devuidsize = sizeof (devuid);
icculus@10309
   643
    result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
icculus@10309
   644
    CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
icculus@10309
   645
    result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
icculus@10309
   646
    CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
icculus@10309
   647
icculus@10309
   648
    /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
icculus@10309
   649
    /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
icculus@10309
   650
    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
icculus@10309
   651
    AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
icculus@10309
   652
}
icculus@5596
   653
#endif
icculus@2049
   654
slouken@11523
   655
    /* Make sure we can feed the device a minimum amount of time */
slouken@11525
   656
    double MINIMUM_AUDIO_BUFFER_TIME_MS = 15.0;
slouken@11525
   657
#if defined(__IPHONEOS__)
slouken@11523
   658
    if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
slouken@11525
   659
        /* Older iOS hardware, use 40 ms as a minimum time */
slouken@11523
   660
        MINIMUM_AUDIO_BUFFER_TIME_MS = 40.0;
slouken@11523
   661
    }
slouken@11525
   662
#endif
icculus@11023
   663
    const double msecs = (this->spec.samples / ((double) this->spec.freq)) * 1000.0;
icculus@11027
   664
    int numAudioBuffers = 2;
slouken@11523
   665
    if (msecs < MINIMUM_AUDIO_BUFFER_TIME_MS) {  /* use more buffers if we have a VERY small sample set. */
slouken@11523
   666
        numAudioBuffers = ((int)SDL_ceil(MINIMUM_AUDIO_BUFFER_TIME_MS / msecs) * 2);
icculus@11022
   667
    }
icculus@11022
   668
icculus@11945
   669
    this->hidden->numAudioBuffers = numAudioBuffers;
icculus@11027
   670
    this->hidden->audioBuffer = SDL_calloc(1, sizeof (AudioQueueBufferRef) * numAudioBuffers);
icculus@11022
   671
    if (this->hidden->audioBuffer == NULL) {
icculus@11022
   672
        SDL_OutOfMemory();
icculus@11022
   673
        return 0;
icculus@11022
   674
    }
icculus@11022
   675
icculus@11022
   676
#if DEBUG_COREAUDIO
icculus@11027
   677
    printf("COREAUDIO: numAudioBuffers == %d\n", numAudioBuffers);
icculus@11022
   678
#endif
icculus@11022
   679
icculus@11027
   680
    for (i = 0; i < numAudioBuffers; i++) {
icculus@10309
   681
        result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
icculus@10309
   682
        CHECK_RESULT("AudioQueueAllocateBuffer");
icculus@10309
   683
        SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
icculus@10309
   684
        this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
icculus@10309
   685
        result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
icculus@10309
   686
        CHECK_RESULT("AudioQueueEnqueueBuffer");
icculus@10309
   687
    }
icculus@2049
   688
icculus@10309
   689
    result = AudioQueueStart(this->hidden->audioQueue, NULL);
icculus@10309
   690
    CHECK_RESULT("AudioQueueStart");
icculus@9393
   691
icculus@2049
   692
    /* We're running! */
icculus@2049
   693
    return 1;
icculus@2049
   694
}
philipp@10360
   695
icculus@11945
   696
static void
icculus@11945
   697
COREAUDIO_ThreadInit(_THIS)
icculus@10309
   698
{
icculus@10309
   699
    const int rc = prepare_audioqueue(this);
icculus@10309
   700
    if (!rc) {
icculus@11945
   701
        /* !!! FIXME: do this in RunAudio, and maybe block OpenDevice until ThreadInit finishes, too, to report an opening error */
icculus@11945
   702
        SDL_OpenedAudioDeviceDisconnected(this);  /* oh well. */
icculus@10309
   703
    }
icculus@11945
   704
}
icculus@10309
   705
icculus@11945
   706
static void
icculus@11945
   707
COREAUDIO_PrepareToClose(_THIS)
icculus@11945
   708
{
icculus@11945
   709
    /* run long enough to queue some silence, so we know our actual audio
icculus@11945
   710
       has been played */
icculus@11945
   711
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, (((this->spec.samples * 1000) / this->spec.freq) * 2) / 1000.0f, 0);
icculus@11945
   712
    AudioQueueStop(this->hidden->audioQueue, 1);
icculus@10309
   713
}
icculus@2049
   714
icculus@2049
   715
static int
icculus@9394
   716
COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
icculus@2049
   717
{
icculus@10309
   718
    AudioStreamBasicDescription *strdesc;
icculus@2049
   719
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2003
   720
    int valid_datatype = 0;
slouken@935
   721
icculus@2049
   722
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   723
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   724
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   725
    if (this->hidden == NULL) {
icculus@7038
   726
        return SDL_OutOfMemory();
icculus@2049
   727
    }
icculus@10232
   728
    SDL_zerop(this->hidden);
icculus@2049
   729
icculus@10309
   730
    strdesc = &this->hidden->strdesc;
icculus@10309
   731
icculus@10245
   732
    if (iscapture) {
icculus@10245
   733
        open_capture_devices++;
icculus@10245
   734
    } else {
icculus@10245
   735
        open_playback_devices++;
icculus@10245
   736
    }
slime73@10346
   737
slime73@10346
   738
#if !MACOSX_COREAUDIO
slime73@10357
   739
    if (!update_audio_session(this, SDL_TRUE)) {
slime73@10346
   740
        return -1;
slime73@10346
   741
    }
slouken@10906
   742
slouken@10906
   743
    /* Stop CoreAudio from doing expensive audio rate conversion */
slouken@10906
   744
    @autoreleasepool {
slouken@10906
   745
        AVAudioSession* session = [AVAudioSession sharedInstance];
slouken@10906
   746
        [session setPreferredSampleRate:this->spec.freq error:nil];
slouken@10906
   747
        this->spec.freq = (int)session.sampleRate;
slouken@10906
   748
    }
slime73@10346
   749
#endif
icculus@10245
   750
slouken@935
   751
    /* Setup a AudioStreamBasicDescription with the requested format */
icculus@10309
   752
    SDL_zerop(strdesc);
icculus@10309
   753
    strdesc->mFormatID = kAudioFormatLinearPCM;
icculus@10309
   754
    strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
icculus@10309
   755
    strdesc->mChannelsPerFrame = this->spec.channels;
icculus@10309
   756
    strdesc->mSampleRate = this->spec.freq;
icculus@10309
   757
    strdesc->mFramesPerPacket = 1;
slouken@1895
   758
icculus@2003
   759
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   760
        this->spec.format = test_format;
icculus@2003
   761
        /* Just a list of valid SDL formats, so people don't pass junk here. */
icculus@2003
   762
        switch (test_format) {
slouken@2043
   763
        case AUDIO_U8:
slouken@2043
   764
        case AUDIO_S8:
slouken@2043
   765
        case AUDIO_U16LSB:
slouken@2043
   766
        case AUDIO_S16LSB:
slouken@2043
   767
        case AUDIO_U16MSB:
slouken@2043
   768
        case AUDIO_S16MSB:
slouken@2043
   769
        case AUDIO_S32LSB:
slouken@2043
   770
        case AUDIO_S32MSB:
slouken@2043
   771
        case AUDIO_F32LSB:
slouken@2043
   772
        case AUDIO_F32MSB:
slouken@2043
   773
            valid_datatype = 1;
icculus@10309
   774
            strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
icculus@2049
   775
            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
icculus@10309
   776
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
slouken@935
   777
icculus@2049
   778
            if (SDL_AUDIO_ISFLOAT(this->spec.format))
icculus@10309
   779
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
icculus@2049
   780
            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
icculus@10309
   781
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
slouken@2043
   782
            break;
icculus@2003
   783
        }
icculus@2003
   784
    }
icculus@2003
   785
slouken@2043
   786
    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
icculus@7038
   787
        return SDL_SetError("Unsupported audio format");
icculus@2003
   788
    }
icculus@2003
   789
icculus@10309
   790
    strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
icculus@10309
   791
    strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
slouken@935
   792
icculus@10309
   793
#if MACOSX_COREAUDIO
icculus@10309
   794
    if (!prepare_device(this, handle, iscapture)) {
icculus@10309
   795
        return -1;
icculus@10309
   796
    }
icculus@10309
   797
#endif
icculus@10309
   798
icculus@11945
   799
    /* Calculate the final parameters for this audio specification */
icculus@11945
   800
    SDL_CalculateAudioSpec(&this->spec);
icculus@11945
   801
icculus@11945
   802
    if (iscapture) {
icculus@11945
   803
        this->hidden->capturestream = SDL_NewAudioStream(this->spec.format, this->spec.channels, this->spec.freq, this->spec.format, this->spec.channels, this->spec.freq);
icculus@11945
   804
        if (!this->hidden->capturestream) {
icculus@11945
   805
            return -1;  /* already set SDL_Error */
icculus@11945
   806
        }
icculus@11945
   807
    } else {
icculus@11945
   808
        this->hidden->bufferSize = this->spec.size;
icculus@11945
   809
        this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
icculus@11945
   810
        if (this->hidden->buffer == NULL) {
icculus@11945
   811
            return SDL_OutOfMemory();
icculus@11945
   812
        }
slouken@935
   813
    }
slouken@1895
   814
icculus@11945
   815
    return 0;
slouken@1895
   816
}
slouken@935
   817
icculus@9393
   818
static void
icculus@9393
   819
COREAUDIO_Deinitialize(void)
icculus@9393
   820
{
icculus@9393
   821
#if MACOSX_COREAUDIO
icculus@9393
   822
    AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
icculus@9393
   823
    free_audio_device_list(&capture_devs);
icculus@9393
   824
    free_audio_device_list(&output_devs);
icculus@9393
   825
#endif
icculus@9393
   826
}
icculus@9393
   827
icculus@2049
   828
static int
slouken@2060
   829
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   830
{
icculus@2049
   831
    /* Set the function pointers */
icculus@2049
   832
    impl->OpenDevice = COREAUDIO_OpenDevice;
icculus@2049
   833
    impl->CloseDevice = COREAUDIO_CloseDevice;
icculus@9393
   834
    impl->Deinitialize = COREAUDIO_Deinitialize;
icculus@11945
   835
    impl->ThreadInit = COREAUDIO_ThreadInit;
icculus@11945
   836
    impl->WaitDevice = COREAUDIO_WaitDevice;
icculus@11945
   837
    impl->GetDeviceBuf = COREAUDIO_GetDeviceBuf;
icculus@11945
   838
    impl->PrepareToClose = COREAUDIO_PrepareToClose;
icculus@11945
   839
    impl->CaptureFromDevice = COREAUDIO_CaptureFromDevice;
icculus@11945
   840
    impl->FlushCapture = COREAUDIO_FlushCapture;
icculus@5596
   841
icculus@5596
   842
#if MACOSX_COREAUDIO
icculus@5596
   843
    impl->DetectDevices = COREAUDIO_DetectDevices;
icculus@9393
   844
    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
icculus@5596
   845
#else
icculus@5596
   846
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@10258
   847
    impl->OnlyHasDefaultCaptureDevice = 1;
icculus@5596
   848
#endif
slouken@6628
   849
icculus@10245
   850
    impl->HasCaptureSupport = 1;
icculus@2049
   851
icculus@3699
   852
    return 1;   /* this audio target is available. */
icculus@2049
   853
}
icculus@2049
   854
icculus@2049
   855
AudioBootStrap COREAUDIO_bootstrap = {
icculus@5596
   856
    "coreaudio", "CoreAudio", COREAUDIO_Init, 0
icculus@2049
   857
};
icculus@2049
   858
philipp@9332
   859
#endif /* SDL_AUDIO_DRIVER_COREAUDIO */
philipp@9332
   860
slouken@1895
   861
/* vi: set ts=4 sw=4 expandtab: */