src/audio/coreaudio/SDL_coreaudio.m
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 10906 66a188fbf8bf
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

In the normal case we'll load all the keymaps from the kernel, but this reduces the size of the SDL library for the fallback case when we can't get to the tty.
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: */