src/audio/coreaudio/SDL_coreaudio.m
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 10906 66a188fbf8bf
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
slouken@935
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 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@1361
    28
#include "../SDL_audio_c.h"
slouken@1361
    29
#include "../SDL_sysaudio.h"
slouken@935
    30
#include "SDL_coreaudio.h"
icculus@6281
    31
#include "SDL_assert.h"
icculus@10309
    32
#include "../../thread/SDL_systhread.h"
slouken@935
    33
icculus@2049
    34
#define DEBUG_COREAUDIO 0
slouken@935
    35
icculus@5596
    36
#define CHECK_RESULT(msg) \
icculus@5596
    37
    if (result != noErr) { \
icculus@5596
    38
        SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
icculus@5596
    39
        return 0; \
icculus@5596
    40
    }
icculus@5596
    41
icculus@5596
    42
#if MACOSX_COREAUDIO
icculus@9393
    43
static const AudioObjectPropertyAddress devlist_address = {
icculus@9393
    44
    kAudioHardwarePropertyDevices,
icculus@9393
    45
    kAudioObjectPropertyScopeGlobal,
icculus@9393
    46
    kAudioObjectPropertyElementMaster
icculus@9393
    47
};
icculus@9393
    48
icculus@9393
    49
typedef void (*addDevFn)(const char *name, const int iscapture, AudioDeviceID devId, void *data);
icculus@9393
    50
icculus@9393
    51
typedef struct AudioDeviceList
icculus@9393
    52
{
icculus@9393
    53
    AudioDeviceID devid;
icculus@9393
    54
    SDL_bool alive;
icculus@9393
    55
    struct AudioDeviceList *next;
icculus@9393
    56
} AudioDeviceList;
icculus@9393
    57
icculus@9393
    58
static AudioDeviceList *output_devs = NULL;
icculus@9393
    59
static AudioDeviceList *capture_devs = NULL;
icculus@9393
    60
icculus@9393
    61
static SDL_bool
icculus@9393
    62
add_to_internal_dev_list(const int iscapture, AudioDeviceID devId)
icculus@9393
    63
{
icculus@9393
    64
    AudioDeviceList *item = (AudioDeviceList *) SDL_malloc(sizeof (AudioDeviceList));
icculus@9393
    65
    if (item == NULL) {
icculus@9393
    66
        return SDL_FALSE;
icculus@9393
    67
    }
icculus@9393
    68
    item->devid = devId;
icculus@9393
    69
    item->alive = SDL_TRUE;
icculus@9393
    70
    item->next = iscapture ? capture_devs : output_devs;
icculus@9393
    71
    if (iscapture) {
icculus@9393
    72
        capture_devs = item;
icculus@9393
    73
    } else {
icculus@9393
    74
        output_devs = item;
icculus@9393
    75
    }
icculus@9393
    76
icculus@9393
    77
    return SDL_TRUE;
icculus@9393
    78
}
slouken@935
    79
slouken@1895
    80
static void
icculus@9393
    81
addToDevList(const char *name, const int iscapture, AudioDeviceID devId, void *data)
slouken@935
    82
{
icculus@9393
    83
    if (add_to_internal_dev_list(iscapture, devId)) {
icculus@9394
    84
        SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
icculus@5593
    85
    }
icculus@5593
    86
}
icculus@5593
    87
icculus@5593
    88
static void
icculus@5593
    89
build_device_list(int iscapture, addDevFn addfn, void *addfndata)
slouken@935
    90
{
icculus@2049
    91
    OSStatus result = noErr;
icculus@2049
    92
    UInt32 size = 0;
icculus@2049
    93
    AudioDeviceID *devs = NULL;
icculus@2049
    94
    UInt32 i = 0;
icculus@2049
    95
    UInt32 max = 0;
icculus@2049
    96
icculus@9393
    97
    result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
icculus@9393
    98
                                            &devlist_address, 0, NULL, &size);
icculus@2049
    99
    if (result != kAudioHardwareNoError)
icculus@2049
   100
        return;
icculus@2049
   101
icculus@2049
   102
    devs = (AudioDeviceID *) alloca(size);
icculus@2049
   103
    if (devs == NULL)
icculus@2049
   104
        return;
icculus@2049
   105
icculus@9393
   106
    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
icculus@9393
   107
                                        &devlist_address, 0, NULL, &size, devs);
icculus@2049
   108
    if (result != kAudioHardwareNoError)
icculus@2049
   109
        return;
icculus@2049
   110
icculus@5593
   111
    max = size / sizeof (AudioDeviceID);
icculus@2049
   112
    for (i = 0; i < max; i++) {
icculus@2049
   113
        CFStringRef cfstr = NULL;
icculus@2049
   114
        char *ptr = NULL;
icculus@2049
   115
        AudioDeviceID dev = devs[i];
icculus@2049
   116
        AudioBufferList *buflist = NULL;
icculus@2049
   117
        int usable = 0;
icculus@2049
   118
        CFIndex len = 0;
icculus@9393
   119
        const AudioObjectPropertyAddress addr = {
icculus@9393
   120
            kAudioDevicePropertyStreamConfiguration,
icculus@9393
   121
            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@9393
   122
            kAudioObjectPropertyElementMaster
icculus@9393
   123
        };
icculus@2049
   124
icculus@9393
   125
        const AudioObjectPropertyAddress nameaddr = {
icculus@9393
   126
            kAudioObjectPropertyName,
icculus@9393
   127
            iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@9393
   128
            kAudioObjectPropertyElementMaster
icculus@9393
   129
        };
icculus@5643
   130
icculus@5643
   131
        result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
icculus@2049
   132
        if (result != noErr)
icculus@2049
   133
            continue;
icculus@2049
   134
icculus@2049
   135
        buflist = (AudioBufferList *) SDL_malloc(size);
icculus@2049
   136
        if (buflist == NULL)
icculus@2049
   137
            continue;
icculus@2049
   138
icculus@5643
   139
        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
icculus@5643
   140
                                            &size, buflist);
slouken@935
   141
icculus@2049
   142
        if (result == noErr) {
icculus@2049
   143
            UInt32 j;
icculus@2049
   144
            for (j = 0; j < buflist->mNumberBuffers; j++) {
icculus@2049
   145
                if (buflist->mBuffers[j].mNumberChannels > 0) {
icculus@2049
   146
                    usable = 1;
icculus@2049
   147
                    break;
icculus@2049
   148
                }
icculus@2049
   149
            }
icculus@2049
   150
        }
icculus@2049
   151
icculus@2049
   152
        SDL_free(buflist);
icculus@2049
   153
icculus@2049
   154
        if (!usable)
icculus@2049
   155
            continue;
icculus@2049
   156
icculus@9393
   157
icculus@5643
   158
        size = sizeof (CFStringRef);
icculus@9393
   159
        result = AudioObjectGetPropertyData(dev, &nameaddr, 0, NULL, &size, &cfstr);
icculus@2049
   160
        if (result != kAudioHardwareNoError)
icculus@2049
   161
            continue;
icculus@2049
   162
icculus@2049
   163
        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
icculus@2049
   164
                                                kCFStringEncodingUTF8);
icculus@2049
   165
icculus@2049
   166
        ptr = (char *) SDL_malloc(len + 1);
slouken@2060
   167
        usable = ((ptr != NULL) &&
slouken@2060
   168
                  (CFStringGetCString
slouken@2060
   169
                   (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
icculus@2049
   170
icculus@2049
   171
        CFRelease(cfstr);
icculus@2049
   172
icculus@2049
   173
        if (usable) {
icculus@2049
   174
            len = strlen(ptr);
icculus@2049
   175
            /* Some devices have whitespace at the end...trim it. */
slouken@2060
   176
            while ((len > 0) && (ptr[len - 1] == ' ')) {
icculus@2049
   177
                len--;
icculus@2049
   178
            }
icculus@2049
   179
            usable = (len > 0);
icculus@2049
   180
        }
icculus@2049
   181
icculus@5593
   182
        if (usable) {
icculus@2049
   183
            ptr[len] = '\0';
icculus@2049
   184
slouken@2060
   185
#if DEBUG_COREAUDIO
icculus@2049
   186
            printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
slouken@2060
   187
                   ((iscapture) ? "capture" : "output"),
icculus@10232
   188
                   (int) i, ptr, (int) dev);
slouken@2060
   189
#endif
icculus@9393
   190
            addfn(ptr, iscapture, dev, addfndata);
icculus@2049
   191
        }
icculus@5593
   192
        SDL_free(ptr);  /* addfn() would have copied the string. */
slouken@935
   193
    }
slouken@935
   194
}
slouken@935
   195
icculus@5593
   196
static void
icculus@9393
   197
free_audio_device_list(AudioDeviceList **list)
icculus@9393
   198
{
icculus@9393
   199
    AudioDeviceList *item = *list;
icculus@9393
   200
    while (item) {
icculus@9393
   201
        AudioDeviceList *next = item->next;
icculus@9393
   202
        SDL_free(item);
icculus@9393
   203
        item = next;
icculus@9393
   204
    }
icculus@9393
   205
    *list = NULL;
icculus@9393
   206
}
icculus@9393
   207
icculus@9393
   208
static void
icculus@9394
   209
COREAUDIO_DetectDevices(void)
icculus@2049
   210
{
icculus@9394
   211
    build_device_list(SDL_TRUE, addToDevList, NULL);
icculus@9394
   212
    build_device_list(SDL_FALSE, addToDevList, NULL);
icculus@2049
   213
}
icculus@2049
   214
icculus@9393
   215
static void
icculus@9393
   216
build_device_change_list(const char *name, const int iscapture, AudioDeviceID devId, void *data)
icculus@9393
   217
{
icculus@9393
   218
    AudioDeviceList **list = (AudioDeviceList **) data;
icculus@9393
   219
    AudioDeviceList *item;
icculus@9393
   220
    for (item = *list; item != NULL; item = item->next) {
icculus@9393
   221
        if (item->devid == devId) {
icculus@9393
   222
            item->alive = SDL_TRUE;
icculus@9393
   223
            return;
icculus@9393
   224
        }
icculus@9393
   225
    }
icculus@9393
   226
icculus@9393
   227
    add_to_internal_dev_list(iscapture, devId);  /* new device, add it. */
icculus@9394
   228
    SDL_AddAudioDevice(iscapture, name, (void *) ((size_t) devId));
icculus@9393
   229
}
icculus@9393
   230
icculus@9394
   231
static void
icculus@9393
   232
reprocess_device_list(const int iscapture, AudioDeviceList **list)
icculus@9393
   233
{
icculus@9393
   234
    AudioDeviceList *item;
icculus@9393
   235
    AudioDeviceList *prev = NULL;
icculus@9393
   236
    for (item = *list; item != NULL; item = item->next) {
icculus@9393
   237
        item->alive = SDL_FALSE;
icculus@9393
   238
    }
icculus@9393
   239
icculus@9393
   240
    build_device_list(iscapture, build_device_change_list, list);
icculus@9393
   241
icculus@9393
   242
    /* free items in the list that aren't still alive. */
icculus@9393
   243
    item = *list;
icculus@9393
   244
    while (item != NULL) {
icculus@9393
   245
        AudioDeviceList *next = item->next;
icculus@9393
   246
        if (item->alive) {
icculus@9393
   247
            prev = item;
icculus@9393
   248
        } else {
icculus@9399
   249
            SDL_RemoveAudioDevice(iscapture, (void *) ((size_t) item->devid));
icculus@9393
   250
            if (prev) {
icculus@9393
   251
                prev->next = item->next;
icculus@9393
   252
            } else {
icculus@9393
   253
                *list = item->next;
icculus@9393
   254
            }
icculus@9393
   255
            SDL_free(item);
icculus@9393
   256
        }
icculus@9393
   257
        item = next;
icculus@9393
   258
    }
icculus@9393
   259
}
icculus@9393
   260
icculus@9393
   261
/* this is called when the system's list of available audio devices changes. */
icculus@9393
   262
static OSStatus
icculus@9393
   263
device_list_changed(AudioObjectID systemObj, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
icculus@9393
   264
{
icculus@9394
   265
    reprocess_device_list(SDL_TRUE, &capture_devs);
icculus@9394
   266
    reprocess_device_list(SDL_FALSE, &output_devs);
icculus@9393
   267
    return 0;
icculus@9393
   268
}
icculus@5596
   269
#endif
icculus@5596
   270
icculus@10245
   271
icculus@10245
   272
static int open_playback_devices = 0;
icculus@10245
   273
static int open_capture_devices = 0;
icculus@10245
   274
slime73@10346
   275
#if !MACOSX_COREAUDIO
slime73@10357
   276
slime73@10357
   277
static void interruption_begin(_THIS)
slime73@10357
   278
{
slime73@10357
   279
    if (this != NULL && this->hidden->audioQueue != NULL) {
slime73@10357
   280
        this->hidden->interrupted = SDL_TRUE;
slime73@10357
   281
        AudioQueuePause(this->hidden->audioQueue);
slime73@10357
   282
    }
slime73@10357
   283
}
slime73@10357
   284
slime73@10357
   285
static void interruption_end(_THIS)
slime73@10357
   286
{
slime73@10357
   287
    if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
slime73@10357
   288
    && this->hidden->interrupted) {
slime73@10357
   289
        this->hidden->interrupted = SDL_FALSE;
slime73@10357
   290
        AudioQueueStart(this->hidden->audioQueue, NULL);
slime73@10357
   291
    }
slime73@10357
   292
}
slime73@10357
   293
slime73@10357
   294
@interface SDLInterruptionListener : NSObject
slime73@10357
   295
slime73@10357
   296
@property (nonatomic, assign) SDL_AudioDevice *device;
slime73@10357
   297
slime73@10357
   298
@end
slime73@10357
   299
slime73@10357
   300
@implementation SDLInterruptionListener
slime73@10357
   301
slime73@10357
   302
- (void)audioSessionInterruption:(NSNotification *)note
slime73@10357
   303
{
slime73@10357
   304
    @synchronized (self) {
slime73@10357
   305
        NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
slime73@10357
   306
        if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
slime73@10357
   307
            interruption_begin(self.device);
slime73@10357
   308
        } else {
slime73@10357
   309
            interruption_end(self.device);
slime73@10357
   310
        }
slime73@10357
   311
    }
slime73@10357
   312
}
slime73@10357
   313
slime73@10357
   314
- (void)applicationBecameActive:(NSNotification *)note
slime73@10357
   315
{
slime73@10357
   316
    @synchronized (self) {
slime73@10357
   317
        interruption_end(self.device);
slime73@10357
   318
    }
slime73@10357
   319
}
slime73@10357
   320
slime73@10357
   321
@end
slime73@10357
   322
slime73@10357
   323
static BOOL update_audio_session(_THIS, SDL_bool open)
icculus@10245
   324
{
slime73@10346
   325
    @autoreleasepool {
slime73@10346
   326
        AVAudioSession *session = [AVAudioSession sharedInstance];
slime73@10357
   327
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@10346
   328
        NSString *category;
slime73@10346
   329
        NSError *err = nil;
slime73@10346
   330
slime73@10346
   331
        if (open_playback_devices && open_capture_devices) {
slime73@10346
   332
            category = AVAudioSessionCategoryPlayAndRecord;
slime73@10346
   333
        } else if (open_capture_devices) {
slime73@10346
   334
            category = AVAudioSessionCategoryRecord;
slime73@10346
   335
        } else {
slime73@10346
   336
            /* Set category to ambient so that other music continues playing.
slime73@10346
   337
             You can change this at runtime in your own code if you need different
slime73@10346
   338
             behavior. If this is common, we can add an SDL hint for this. */
slime73@10346
   339
            category = AVAudioSessionCategoryAmbient;
slime73@10346
   340
        }
slime73@10346
   341
slime73@10357
   342
        if (![session setCategory:category error:&err]) {
slime73@10357
   343
            NSString *desc = err.description;
slime73@10357
   344
            SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
slime73@10357
   345
            return NO;
slime73@10357
   346
        }
slime73@10357
   347
slime73@10346
   348
        if (open_playback_devices + open_capture_devices == 1) {
slime73@10346
   349
            if (![session setActive:YES error:&err]) {
slime73@10346
   350
                NSString *desc = err.description;
slime73@10346
   351
                SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
slime73@10346
   352
                return NO;
slime73@10346
   353
            }
slime73@10346
   354
        } else if (!open_playback_devices && !open_capture_devices) {
slime73@10346
   355
            [session setActive:NO error:nil];
slime73@10346
   356
        }
slime73@10346
   357
slime73@10357
   358
        if (open) {
slime73@10357
   359
            SDLInterruptionListener *listener = [SDLInterruptionListener new];
slime73@10357
   360
            listener.device = this;
slime73@10357
   361
slime73@10357
   362
            [center addObserver:listener
slime73@10357
   363
                       selector:@selector(audioSessionInterruption:)
slime73@10357
   364
                           name:AVAudioSessionInterruptionNotification
slime73@10357
   365
                         object:session];
slime73@10357
   366
slime73@10357
   367
            /* An interruption end notification is not guaranteed to be sent if
slime73@10357
   368
             we were previously interrupted... resuming if needed when the app
slime73@10357
   369
             becomes active seems to be the way to go. */
slime73@10357
   370
            [center addObserver:listener
slime73@10357
   371
                       selector:@selector(applicationBecameActive:)
slime73@10357
   372
                           name:UIApplicationDidBecomeActiveNotification
slime73@10357
   373
                         object:session];
slime73@10357
   374
slime73@10357
   375
            [center addObserver:listener
slime73@10357
   376
                       selector:@selector(applicationBecameActive:)
slime73@10357
   377
                           name:UIApplicationWillEnterForegroundNotification
slime73@10357
   378
                         object:session];
slime73@10357
   379
slime73@10357
   380
            this->hidden->interruption_listener = CFBridgingRetain(listener);
slime73@10357
   381
        } else {
slime73@10357
   382
            if (this->hidden->interruption_listener != NULL) {
slime73@10357
   383
                SDLInterruptionListener *listener = nil;
slime73@10357
   384
                listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
slime73@10357
   385
                @synchronized (listener) {
slime73@10357
   386
                    listener.device = NULL;
slime73@10357
   387
                }
slime73@10357
   388
                [center removeObserver:listener];
slime73@10357
   389
            }
slime73@10346
   390
        }
icculus@10245
   391
    }
slime73@10346
   392
slime73@10346
   393
    return YES;
slime73@10346
   394
}
icculus@10245
   395
#endif
icculus@10245
   396
icculus@10245
   397
icculus@10309
   398
/* The AudioQueue callback */
icculus@10309
   399
static void
icculus@10309
   400
outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
slouken@935
   401
{
icculus@10309
   402
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
icculus@10238
   403
    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
icculus@10309
   404
        /* Supply silence if audio is enabled and not paused */
icculus@10309
   405
        SDL_memset(inBuffer->mAudioData, this->spec.silence, inBuffer->mAudioDataBytesCapacity);
icculus@10309
   406
    } else {
icculus@10309
   407
        UInt32 remaining = inBuffer->mAudioDataBytesCapacity;
icculus@10309
   408
        Uint8 *ptr = (Uint8 *) inBuffer->mAudioData;
slouken@1895
   409
icculus@3617
   410
        while (remaining > 0) {
icculus@10309
   411
            UInt32 len;
icculus@3636
   412
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
icculus@3617
   413
                /* Generate the data */
slouken@6977
   414
                SDL_LockMutex(this->mixer_lock);
icculus@3617
   415
                (*this->spec.callback)(this->spec.userdata,
icculus@3636
   416
                            this->hidden->buffer, this->hidden->bufferSize);
slouken@6977
   417
                SDL_UnlockMutex(this->mixer_lock);
icculus@3636
   418
                this->hidden->bufferOffset = 0;
icculus@3617
   419
            }
icculus@3617
   420
icculus@3636
   421
            len = this->hidden->bufferSize - this->hidden->bufferOffset;
icculus@10309
   422
            if (len > remaining) {
icculus@3617
   423
                len = remaining;
icculus@10309
   424
            }
icculus@3636
   425
            SDL_memcpy(ptr, (char *)this->hidden->buffer +
icculus@3636
   426
                       this->hidden->bufferOffset, len);
icculus@10309
   427
            ptr = ptr + len;
icculus@3617
   428
            remaining -= len;
icculus@3636
   429
            this->hidden->bufferOffset += len;
slouken@935
   430
        }
slouken@935
   431
    }
slouken@1895
   432
icculus@10309
   433
    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
icculus@10309
   434
        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
icculus@10232
   435
    }
icculus@10232
   436
icculus@10309
   437
    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
icculus@10309
   438
}
icculus@10232
   439
icculus@10309
   440
static void
icculus@10309
   441
inputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
icculus@10309
   442
              const AudioTimeStamp *inStartTime, UInt32 inNumberPacketDescriptions,
icculus@10309
   443
              const AudioStreamPacketDescription *inPacketDescs )
icculus@10309
   444
{
icculus@10309
   445
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
icculus@10309
   446
    if (SDL_AtomicGet(&this->enabled) && !SDL_AtomicGet(&this->paused)) {  /* ignore unless we're active. */
icculus@10309
   447
        const Uint8 *ptr = (const Uint8 *) inBuffer->mAudioData;
icculus@10309
   448
        UInt32 remaining = inBuffer->mAudioDataByteSize;
icculus@10232
   449
        while (remaining > 0) {
icculus@10232
   450
            UInt32 len = this->hidden->bufferSize - this->hidden->bufferOffset;
icculus@10309
   451
            if (len > remaining) {
icculus@10232
   452
                len = remaining;
icculus@10309
   453
            }
icculus@10232
   454
icculus@10232
   455
            SDL_memcpy((char *)this->hidden->buffer + this->hidden->bufferOffset, ptr, len);
icculus@10232
   456
            ptr += len;
icculus@10232
   457
            remaining -= len;
icculus@10232
   458
            this->hidden->bufferOffset += len;
icculus@10232
   459
icculus@10232
   460
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
icculus@10232
   461
                SDL_LockMutex(this->mixer_lock);
icculus@10309
   462
                (*this->spec.callback)(this->spec.userdata, this->hidden->buffer, this->hidden->bufferSize);
icculus@10232
   463
                SDL_UnlockMutex(this->mixer_lock);
icculus@10232
   464
                this->hidden->bufferOffset = 0;
icculus@10232
   465
            }
icculus@10232
   466
        }
icculus@10232
   467
    }
icculus@10232
   468
icculus@10309
   469
    if (!SDL_AtomicGet(&this->hidden->shutdown)) {
icculus@10309
   470
        AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
icculus@10309
   471
    }
slouken@935
   472
}
slouken@935
   473
icculus@2049
   474
icculus@9393
   475
#if MACOSX_COREAUDIO
icculus@9393
   476
static const AudioObjectPropertyAddress alive_address =
icculus@9393
   477
{
icculus@9393
   478
    kAudioDevicePropertyDeviceIsAlive,
icculus@9393
   479
    kAudioObjectPropertyScopeGlobal,
icculus@9393
   480
    kAudioObjectPropertyElementMaster
icculus@9393
   481
};
icculus@9393
   482
icculus@9393
   483
static OSStatus
icculus@9393
   484
device_unplugged(AudioObjectID devid, UInt32 num_addr, const AudioObjectPropertyAddress *addrs, void *data)
icculus@9393
   485
{
icculus@9393
   486
    SDL_AudioDevice *this = (SDL_AudioDevice *) data;
icculus@9393
   487
    SDL_bool dead = SDL_FALSE;
icculus@9393
   488
    UInt32 isAlive = 1;
icculus@9393
   489
    UInt32 size = sizeof (isAlive);
icculus@9393
   490
    OSStatus error;
icculus@9393
   491
icculus@10238
   492
    if (!SDL_AtomicGet(&this->enabled)) {
icculus@9393
   493
        return 0;  /* already known to be dead. */
icculus@9393
   494
    }
icculus@9393
   495
icculus@9393
   496
    error = AudioObjectGetPropertyData(this->hidden->deviceID, &alive_address,
icculus@9393
   497
                                       0, NULL, &size, &isAlive);
icculus@9393
   498
icculus@9393
   499
    if (error == kAudioHardwareBadDeviceError) {
icculus@9393
   500
        dead = SDL_TRUE;  /* device was unplugged. */
icculus@9393
   501
    } else if ((error == kAudioHardwareNoError) && (!isAlive)) {
icculus@9393
   502
        dead = SDL_TRUE;  /* device died in some other way. */
icculus@9393
   503
    }
icculus@9393
   504
icculus@9393
   505
    if (dead) {
icculus@9394
   506
        SDL_OpenedAudioDeviceDisconnected(this);
icculus@9393
   507
    }
icculus@9393
   508
icculus@9393
   509
    return 0;
icculus@9393
   510
}
icculus@9393
   511
#endif
icculus@9393
   512
icculus@2049
   513
static void
icculus@2049
   514
COREAUDIO_CloseDevice(_THIS)
slouken@935
   515
{
icculus@10309
   516
    const SDL_bool iscapture = this->iscapture;
icculus@10309
   517
    int i;
icculus@10306
   518
icculus@10309
   519
/* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
icculus@10309
   520
/* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
icculus@10309
   521
#if MACOSX_COREAUDIO
icculus@10309
   522
    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
icculus@10309
   523
    AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
icculus@10309
   524
#endif
icculus@9393
   525
slime73@10357
   526
#if !MACOSX_COREAUDIO
slime73@10357
   527
    update_audio_session(this, SDL_FALSE);
slime73@10357
   528
#endif
slime73@10357
   529
icculus@10309
   530
    if (this->hidden->thread) {
icculus@10309
   531
        SDL_AtomicSet(&this->hidden->shutdown, 1);
icculus@10309
   532
        SDL_WaitThread(this->hidden->thread, NULL);
icculus@10255
   533
    }
icculus@10245
   534
icculus@10309
   535
    if (this->hidden->audioQueue) {
icculus@10309
   536
        for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
icculus@10309
   537
            if (this->hidden->audioBuffer[i]) {
icculus@10309
   538
                AudioQueueFreeBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i]);
icculus@10309
   539
            }
icculus@10309
   540
        }
icculus@10309
   541
        AudioQueueDispose(this->hidden->audioQueue, 1);
icculus@10309
   542
    }
icculus@10309
   543
icculus@10309
   544
    if (this->hidden->ready_semaphore) {
icculus@10309
   545
        SDL_DestroySemaphore(this->hidden->ready_semaphore);
icculus@10309
   546
    }
icculus@10309
   547
icculus@10309
   548
    SDL_free(this->hidden->thread_error);
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--;
slouken@935
   556
    }
icculus@2049
   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@9394
   614
static int
icculus@10309
   615
prepare_audioqueue(_THIS)
slouken@935
   616
{
icculus@10309
   617
    const AudioStreamBasicDescription *strdesc = &this->hidden->strdesc;
icculus@10309
   618
    const int iscapture = this->iscapture;
icculus@10309
   619
    OSStatus result;
icculus@10309
   620
    int i;
icculus@5596
   621
icculus@10309
   622
    SDL_assert(CFRunLoopGetCurrent() != NULL);
icculus@2055
   623
icculus@10309
   624
    if (iscapture) {
icculus@10309
   625
        result = AudioQueueNewInput(strdesc, inputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
icculus@10309
   626
        CHECK_RESULT("AudioQueueNewInput");
icculus@10309
   627
    } else {
icculus@10309
   628
        result = AudioQueueNewOutput(strdesc, outputCallback, this, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &this->hidden->audioQueue);
icculus@10309
   629
        CHECK_RESULT("AudioQueueNewOutput");
icculus@10232
   630
    }
icculus@10232
   631
icculus@10245
   632
#if MACOSX_COREAUDIO
icculus@10309
   633
{
icculus@10309
   634
    const AudioObjectPropertyAddress prop = {
icculus@10309
   635
        kAudioDevicePropertyDeviceUID,
icculus@10309
   636
        iscapture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
icculus@10309
   637
        kAudioObjectPropertyElementMaster
icculus@10309
   638
    };
icculus@10309
   639
    CFStringRef devuid;
icculus@10309
   640
    UInt32 devuidsize = sizeof (devuid);
icculus@10309
   641
    result = AudioObjectGetPropertyData(this->hidden->deviceID, &prop, 0, NULL, &devuidsize, &devuid);
icculus@10309
   642
    CHECK_RESULT("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID)");
icculus@10309
   643
    result = AudioQueueSetProperty(this->hidden->audioQueue, kAudioQueueProperty_CurrentDevice, &devuid, devuidsize);
icculus@10309
   644
    CHECK_RESULT("AudioQueueSetProperty (kAudioQueueProperty_CurrentDevice)");
icculus@2049
   645
icculus@10309
   646
    /* !!! FIXME: what does iOS do when a bluetooth audio device vanishes? Headphones unplugged? */
icculus@10309
   647
    /* !!! FIXME: (we only do a "default" device on iOS right now...can we do more?) */
icculus@10309
   648
    /* Fire a callback if the device stops being "alive" (disconnected, etc). */
icculus@10309
   649
    AudioObjectAddPropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
icculus@10309
   650
}
icculus@10309
   651
#endif
icculus@2049
   652
icculus@2049
   653
    /* Calculate the final parameters for this audio specification */
icculus@2049
   654
    SDL_CalculateAudioSpec(&this->spec);
icculus@2049
   655
icculus@2049
   656
    /* Allocate a sample buffer */
icculus@10232
   657
    this->hidden->bufferSize = this->spec.size;
icculus@10232
   658
    this->hidden->bufferOffset = iscapture ? 0 : this->hidden->bufferSize;
icculus@10232
   659
icculus@2049
   660
    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
icculus@10232
   661
    if (this->hidden->buffer == NULL) {
icculus@10232
   662
        SDL_OutOfMemory();
icculus@10232
   663
        return 0;
icculus@10232
   664
    }
icculus@2049
   665
icculus@10309
   666
    for (i = 0; i < SDL_arraysize(this->hidden->audioBuffer); i++) {
icculus@10309
   667
        result = AudioQueueAllocateBuffer(this->hidden->audioQueue, this->spec.size, &this->hidden->audioBuffer[i]);
icculus@10309
   668
        CHECK_RESULT("AudioQueueAllocateBuffer");
icculus@10309
   669
        SDL_memset(this->hidden->audioBuffer[i]->mAudioData, this->spec.silence, this->hidden->audioBuffer[i]->mAudioDataBytesCapacity);
icculus@10309
   670
        this->hidden->audioBuffer[i]->mAudioDataByteSize = this->hidden->audioBuffer[i]->mAudioDataBytesCapacity;
icculus@10309
   671
        result = AudioQueueEnqueueBuffer(this->hidden->audioQueue, this->hidden->audioBuffer[i], 0, NULL);
icculus@10309
   672
        CHECK_RESULT("AudioQueueEnqueueBuffer");
icculus@10309
   673
    }
icculus@2049
   674
icculus@10309
   675
    result = AudioQueueStart(this->hidden->audioQueue, NULL);
icculus@10309
   676
    CHECK_RESULT("AudioQueueStart");
icculus@9393
   677
icculus@2049
   678
    /* We're running! */
icculus@2049
   679
    return 1;
icculus@2049
   680
}
philipp@10360
   681
slime73@10340
   682
static int
icculus@10309
   683
audioqueue_thread(void *arg)
icculus@10309
   684
{
icculus@10309
   685
    SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
icculus@10309
   686
    const int rc = prepare_audioqueue(this);
icculus@10309
   687
    if (!rc) {
icculus@10309
   688
        this->hidden->thread_error = SDL_strdup(SDL_GetError());
icculus@10309
   689
        SDL_SemPost(this->hidden->ready_semaphore);
icculus@10309
   690
        return 0;
icculus@10309
   691
    }
icculus@10309
   692
icculus@10309
   693
    /* init was successful, alert parent thread and start running... */
icculus@10309
   694
    SDL_SemPost(this->hidden->ready_semaphore);
icculus@10309
   695
    while (!SDL_AtomicGet(&this->hidden->shutdown)) {
icculus@10309
   696
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
icculus@10309
   697
    }
icculus@10309
   698
icculus@10309
   699
    if (this->iscapture) {  /* just stop immediately for capture devices. */
icculus@10309
   700
        AudioQueueStop(this->hidden->audioQueue, 1);
icculus@10309
   701
    } else {  /* Drain off any pending playback. */
icculus@10309
   702
        AudioQueueStop(this->hidden->audioQueue, 0);
icculus@10309
   703
        const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
icculus@10309
   704
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
icculus@10309
   705
    }
icculus@10309
   706
icculus@10309
   707
    return 0;
icculus@10309
   708
}
icculus@2049
   709
icculus@2049
   710
static int
icculus@9394
   711
COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
icculus@2049
   712
{
icculus@10309
   713
    AudioStreamBasicDescription *strdesc;
icculus@2049
   714
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2003
   715
    int valid_datatype = 0;
slouken@935
   716
icculus@2049
   717
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   718
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   719
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   720
    if (this->hidden == NULL) {
icculus@7038
   721
        return SDL_OutOfMemory();
icculus@2049
   722
    }
icculus@10232
   723
    SDL_zerop(this->hidden);
icculus@2049
   724
icculus@10309
   725
    strdesc = &this->hidden->strdesc;
icculus@10309
   726
icculus@10245
   727
    if (iscapture) {
icculus@10245
   728
        open_capture_devices++;
icculus@10245
   729
    } else {
icculus@10245
   730
        open_playback_devices++;
icculus@10245
   731
    }
slime73@10346
   732
slime73@10346
   733
#if !MACOSX_COREAUDIO
slime73@10357
   734
    if (!update_audio_session(this, SDL_TRUE)) {
slime73@10346
   735
        return -1;
slime73@10346
   736
    }
slime73@10346
   737
#endif
icculus@10245
   738
slouken@935
   739
    /* Setup a AudioStreamBasicDescription with the requested format */
icculus@10309
   740
    SDL_zerop(strdesc);
icculus@10309
   741
    strdesc->mFormatID = kAudioFormatLinearPCM;
icculus@10309
   742
    strdesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
icculus@10309
   743
    strdesc->mChannelsPerFrame = this->spec.channels;
icculus@10309
   744
    strdesc->mSampleRate = this->spec.freq;
icculus@10309
   745
    strdesc->mFramesPerPacket = 1;
slouken@1895
   746
icculus@2003
   747
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   748
        this->spec.format = test_format;
icculus@2003
   749
        /* Just a list of valid SDL formats, so people don't pass junk here. */
icculus@2003
   750
        switch (test_format) {
slouken@2043
   751
        case AUDIO_U8:
slouken@2043
   752
        case AUDIO_S8:
slouken@2043
   753
        case AUDIO_U16LSB:
slouken@2043
   754
        case AUDIO_S16LSB:
slouken@2043
   755
        case AUDIO_U16MSB:
slouken@2043
   756
        case AUDIO_S16MSB:
slouken@2043
   757
        case AUDIO_S32LSB:
slouken@2043
   758
        case AUDIO_S32MSB:
slouken@2043
   759
        case AUDIO_F32LSB:
slouken@2043
   760
        case AUDIO_F32MSB:
slouken@2043
   761
            valid_datatype = 1;
icculus@10309
   762
            strdesc->mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
icculus@2049
   763
            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
icculus@10309
   764
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
slouken@935
   765
icculus@2049
   766
            if (SDL_AUDIO_ISFLOAT(this->spec.format))
icculus@10309
   767
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
icculus@2049
   768
            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
icculus@10309
   769
                strdesc->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
slouken@2043
   770
            break;
icculus@2003
   771
        }
icculus@2003
   772
    }
icculus@2003
   773
slouken@2043
   774
    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
icculus@7038
   775
        return SDL_SetError("Unsupported audio format");
icculus@2003
   776
    }
icculus@2003
   777
icculus@10309
   778
    strdesc->mBytesPerFrame = strdesc->mBitsPerChannel * strdesc->mChannelsPerFrame / 8;
icculus@10309
   779
    strdesc->mBytesPerPacket = strdesc->mBytesPerFrame * strdesc->mFramesPerPacket;
slouken@935
   780
icculus@10309
   781
#if MACOSX_COREAUDIO
icculus@10309
   782
    if (!prepare_device(this, handle, iscapture)) {
icculus@10309
   783
        return -1;
icculus@10309
   784
    }
icculus@10309
   785
#endif
icculus@10309
   786
icculus@10309
   787
    /* This has to init in a new thread so it can get its own CFRunLoop. :/ */
icculus@10309
   788
    SDL_AtomicSet(&this->hidden->shutdown, 0);
icculus@10309
   789
    this->hidden->ready_semaphore = SDL_CreateSemaphore(0);
icculus@10309
   790
    if (!this->hidden->ready_semaphore) {
icculus@10309
   791
        return -1;  /* oh well. */
slouken@935
   792
    }
slouken@1895
   793
icculus@10309
   794
    this->hidden->thread = SDL_CreateThreadInternal(audioqueue_thread, "AudioQueue thread", 512 * 1024, this);
icculus@10309
   795
    if (!this->hidden->thread) {
icculus@10309
   796
        return -1;
icculus@10309
   797
    }
icculus@10309
   798
icculus@10309
   799
    SDL_SemWait(this->hidden->ready_semaphore);
icculus@10309
   800
    SDL_DestroySemaphore(this->hidden->ready_semaphore);
icculus@10309
   801
    this->hidden->ready_semaphore = NULL;
icculus@10309
   802
icculus@10309
   803
    if ((this->hidden->thread != NULL) && (this->hidden->thread_error != NULL)) {
icculus@10309
   804
        SDL_SetError("%s", this->hidden->thread_error);
icculus@10309
   805
        return -1;
icculus@10309
   806
    }
icculus@10309
   807
icculus@10309
   808
    return (this->hidden->thread != NULL) ? 0 : -1;
slouken@1895
   809
}
slouken@935
   810
icculus@9393
   811
static void
icculus@9393
   812
COREAUDIO_Deinitialize(void)
icculus@9393
   813
{
icculus@9393
   814
#if MACOSX_COREAUDIO
icculus@9393
   815
    AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
icculus@9393
   816
    free_audio_device_list(&capture_devs);
icculus@9393
   817
    free_audio_device_list(&output_devs);
icculus@9393
   818
#endif
icculus@9393
   819
}
icculus@9393
   820
icculus@2049
   821
static int
slouken@2060
   822
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   823
{
icculus@2049
   824
    /* Set the function pointers */
icculus@2049
   825
    impl->OpenDevice = COREAUDIO_OpenDevice;
icculus@2049
   826
    impl->CloseDevice = COREAUDIO_CloseDevice;
icculus@9393
   827
    impl->Deinitialize = COREAUDIO_Deinitialize;
icculus@5596
   828
icculus@5596
   829
#if MACOSX_COREAUDIO
icculus@5596
   830
    impl->DetectDevices = COREAUDIO_DetectDevices;
icculus@9393
   831
    AudioObjectAddPropertyListener(kAudioObjectSystemObject, &devlist_address, device_list_changed, NULL);
icculus@5596
   832
#else
icculus@5596
   833
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@10258
   834
    impl->OnlyHasDefaultCaptureDevice = 1;
icculus@5596
   835
#endif
slouken@6628
   836
icculus@2049
   837
    impl->ProvidesOwnCallbackThread = 1;
icculus@10245
   838
    impl->HasCaptureSupport = 1;
icculus@2049
   839
icculus@3699
   840
    return 1;   /* this audio target is available. */
icculus@2049
   841
}
icculus@2049
   842
icculus@2049
   843
AudioBootStrap COREAUDIO_bootstrap = {
icculus@5596
   844
    "coreaudio", "CoreAudio", COREAUDIO_Init, 0
icculus@2049
   845
};
icculus@2049
   846
philipp@9332
   847
#endif /* SDL_AUDIO_DRIVER_COREAUDIO */
philipp@9332
   848
slouken@1895
   849
/* vi: set ts=4 sw=4 expandtab: */