src/audio/coreaudio/SDL_coreaudio.c
author Sam Lantinga
Fri, 02 Nov 2012 09:28:40 -0700
changeset 6634 b14b66ed5718
parent 6628 7994e6979876
child 6885 700f1b25f77f
permissions -rw-r--r--
Fixed bug 1632 - iOS CoreAudio doesn't close

C.W. Betts 2012-10-28 19:42:01 PDT

I noticed when looking through the CoreAudio code of SDL 2.0 that there was a
fix me wondering how iOS closed the audio system. While working on my own audio
code on PlayerPRO, I discovered that Carbon's component code was replaced in
the audio subsystem with Audio Component Services.
slouken@935
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6138
     3
  Copyright (C) 1997-2012 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
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@935
    22
#include "SDL_audio.h"
slouken@1361
    23
#include "../SDL_audio_c.h"
slouken@1361
    24
#include "../SDL_sysaudio.h"
slouken@935
    25
#include "SDL_coreaudio.h"
icculus@6281
    26
#include "SDL_assert.h"
slouken@935
    27
icculus@2049
    28
#define DEBUG_COREAUDIO 0
slouken@935
    29
icculus@5596
    30
static void COREAUDIO_CloseDevice(_THIS);
icculus@5596
    31
icculus@5596
    32
#define CHECK_RESULT(msg) \
icculus@5596
    33
    if (result != noErr) { \
icculus@5596
    34
        COREAUDIO_CloseDevice(this); \
icculus@5596
    35
        SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
icculus@5596
    36
        return 0; \
icculus@5596
    37
    }
icculus@5596
    38
icculus@5596
    39
#if MACOSX_COREAUDIO
icculus@5593
    40
typedef void (*addDevFn)(const char *name, AudioDeviceID devId, void *data);
slouken@935
    41
slouken@1895
    42
static void
icculus@5593
    43
addToDevList(const char *name, AudioDeviceID devId, void *data)
slouken@935
    44
{
icculus@5593
    45
    SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
icculus@5593
    46
    addfn(name);
slouken@935
    47
}
slouken@935
    48
icculus@5593
    49
typedef struct
icculus@5593
    50
{
icculus@5593
    51
    const char *findname;
icculus@5593
    52
    AudioDeviceID devId;
icculus@5593
    53
    int found;
icculus@5593
    54
} FindDevIdData;
icculus@2049
    55
icculus@2049
    56
static void
icculus@5593
    57
findDevId(const char *name, AudioDeviceID devId, void *_data)
icculus@5593
    58
{
icculus@5593
    59
    FindDevIdData *data = (FindDevIdData *) _data;
icculus@5593
    60
    if (!data->found) {
icculus@5593
    61
        if (SDL_strcmp(name, data->findname) == 0) {
icculus@5593
    62
            data->found = 1;
icculus@5593
    63
            data->devId = devId;
icculus@5593
    64
        }
icculus@5593
    65
    }
icculus@5593
    66
}
icculus@5593
    67
icculus@5593
    68
static void
icculus@5593
    69
build_device_list(int iscapture, addDevFn addfn, void *addfndata)
slouken@935
    70
{
icculus@2049
    71
    OSStatus result = noErr;
icculus@2049
    72
    UInt32 size = 0;
icculus@2049
    73
    AudioDeviceID *devs = NULL;
icculus@2049
    74
    UInt32 i = 0;
icculus@2049
    75
    UInt32 max = 0;
icculus@2049
    76
icculus@5643
    77
    AudioObjectPropertyAddress addr = {
icculus@5643
    78
        kAudioHardwarePropertyDevices,
icculus@5643
    79
        kAudioObjectPropertyScopeGlobal,
icculus@5643
    80
        kAudioObjectPropertyElementMaster
icculus@5643
    81
    };
icculus@2049
    82
icculus@5643
    83
    result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
icculus@5643
    84
                                            0, NULL, &size);
icculus@2049
    85
    if (result != kAudioHardwareNoError)
icculus@2049
    86
        return;
icculus@2049
    87
icculus@2049
    88
    devs = (AudioDeviceID *) alloca(size);
icculus@2049
    89
    if (devs == NULL)
icculus@2049
    90
        return;
icculus@2049
    91
icculus@5643
    92
    result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
icculus@5643
    93
                                        0, NULL, &size, devs);
icculus@2049
    94
    if (result != kAudioHardwareNoError)
icculus@2049
    95
        return;
icculus@2049
    96
icculus@5593
    97
    max = size / sizeof (AudioDeviceID);
icculus@2049
    98
    for (i = 0; i < max; i++) {
icculus@2049
    99
        CFStringRef cfstr = NULL;
icculus@2049
   100
        char *ptr = NULL;
icculus@2049
   101
        AudioDeviceID dev = devs[i];
icculus@2049
   102
        AudioBufferList *buflist = NULL;
icculus@2049
   103
        int usable = 0;
icculus@2049
   104
        CFIndex len = 0;
icculus@2049
   105
icculus@5643
   106
        addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
icculus@5643
   107
                        kAudioDevicePropertyScopeOutput;
icculus@5643
   108
        addr.mSelector = kAudioDevicePropertyStreamConfiguration;
icculus@5643
   109
icculus@5643
   110
        result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
icculus@2049
   111
        if (result != noErr)
icculus@2049
   112
            continue;
icculus@2049
   113
icculus@2049
   114
        buflist = (AudioBufferList *) SDL_malloc(size);
icculus@2049
   115
        if (buflist == NULL)
icculus@2049
   116
            continue;
icculus@2049
   117
icculus@5643
   118
        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
icculus@5643
   119
                                            &size, buflist);
slouken@935
   120
icculus@2049
   121
        if (result == noErr) {
icculus@2049
   122
            UInt32 j;
icculus@2049
   123
            for (j = 0; j < buflist->mNumberBuffers; j++) {
icculus@2049
   124
                if (buflist->mBuffers[j].mNumberChannels > 0) {
icculus@2049
   125
                    usable = 1;
icculus@2049
   126
                    break;
icculus@2049
   127
                }
icculus@2049
   128
            }
icculus@2049
   129
        }
icculus@2049
   130
icculus@2049
   131
        SDL_free(buflist);
icculus@2049
   132
icculus@2049
   133
        if (!usable)
icculus@2049
   134
            continue;
icculus@2049
   135
icculus@5643
   136
        addr.mSelector = kAudioObjectPropertyName;
icculus@5643
   137
        size = sizeof (CFStringRef);
icculus@5643
   138
        result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &size, &cfstr);
icculus@2049
   139
        if (result != kAudioHardwareNoError)
icculus@2049
   140
            continue;
icculus@2049
   141
icculus@2049
   142
        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
icculus@2049
   143
                                                kCFStringEncodingUTF8);
icculus@2049
   144
icculus@2049
   145
        ptr = (char *) SDL_malloc(len + 1);
slouken@2060
   146
        usable = ((ptr != NULL) &&
slouken@2060
   147
                  (CFStringGetCString
slouken@2060
   148
                   (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
icculus@2049
   149
icculus@2049
   150
        CFRelease(cfstr);
icculus@2049
   151
icculus@2049
   152
        if (usable) {
icculus@2049
   153
            len = strlen(ptr);
icculus@2049
   154
            /* Some devices have whitespace at the end...trim it. */
slouken@2060
   155
            while ((len > 0) && (ptr[len - 1] == ' ')) {
icculus@2049
   156
                len--;
icculus@2049
   157
            }
icculus@2049
   158
            usable = (len > 0);
icculus@2049
   159
        }
icculus@2049
   160
icculus@5593
   161
        if (usable) {
icculus@2049
   162
            ptr[len] = '\0';
icculus@2049
   163
slouken@2060
   164
#if DEBUG_COREAUDIO
icculus@2049
   165
            printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n",
slouken@2060
   166
                   ((iscapture) ? "capture" : "output"),
slouken@2060
   167
                   (int) *devCount, ptr, (int) dev);
slouken@2060
   168
#endif
icculus@5593
   169
            addfn(ptr, dev, addfndata);
icculus@2049
   170
        }
icculus@5593
   171
        SDL_free(ptr);  /* addfn() would have copied the string. */
slouken@935
   172
    }
slouken@935
   173
}
slouken@935
   174
icculus@5593
   175
static void
icculus@5593
   176
COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
icculus@2049
   177
{
icculus@5593
   178
    build_device_list(iscapture, addToDevList, addfn);
icculus@2049
   179
}
icculus@2049
   180
icculus@5596
   181
static int
icculus@5596
   182
find_device_by_name(_THIS, const char *devname, int iscapture)
icculus@5596
   183
{
icculus@5596
   184
    AudioDeviceID devid = 0;
icculus@5596
   185
    OSStatus result = noErr;
icculus@5596
   186
    UInt32 size = 0;
icculus@5596
   187
    UInt32 alive = 0;
icculus@5596
   188
    pid_t pid = 0;
icculus@5596
   189
icculus@5643
   190
    AudioObjectPropertyAddress addr = {
icculus@5643
   191
        0,
icculus@5643
   192
        kAudioObjectPropertyScopeGlobal,
icculus@5643
   193
        kAudioObjectPropertyElementMaster
icculus@5643
   194
    };
icculus@5643
   195
icculus@5596
   196
    if (devname == NULL) {
icculus@5643
   197
        size = sizeof (AudioDeviceID);
icculus@5643
   198
        addr.mSelector =
icculus@5596
   199
            ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
icculus@5643
   200
            kAudioHardwarePropertyDefaultOutputDevice);
icculus@5643
   201
        result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
icculus@5643
   202
                                            0, NULL, &size, &devid);
icculus@5596
   203
        CHECK_RESULT("AudioHardwareGetProperty (default device)");
icculus@5596
   204
    } else {
icculus@5596
   205
        FindDevIdData data;
icculus@5596
   206
        SDL_zero(data);
icculus@5596
   207
        data.findname = devname;
icculus@5596
   208
        build_device_list(iscapture, findDevId, &data);
icculus@5596
   209
        if (!data.found) {
icculus@5596
   210
            SDL_SetError("CoreAudio: No such audio device.");
icculus@5596
   211
            return 0;
icculus@5596
   212
        }
icculus@5596
   213
        devid = data.devId;
icculus@5596
   214
    }
icculus@5596
   215
icculus@5643
   216
    addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
icculus@5643
   217
    addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
icculus@5643
   218
                    kAudioDevicePropertyScopeOutput;
icculus@5643
   219
icculus@5643
   220
    size = sizeof (alive);
icculus@5643
   221
    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
icculus@5596
   222
    CHECK_RESULT
icculus@5596
   223
        ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
icculus@5596
   224
icculus@5596
   225
    if (!alive) {
icculus@5596
   226
        SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
icculus@5596
   227
        return 0;
icculus@5596
   228
    }
icculus@5596
   229
icculus@5643
   230
    addr.mSelector = kAudioDevicePropertyHogMode;
icculus@5643
   231
    size = sizeof (pid);
icculus@5643
   232
    result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &pid);
icculus@5596
   233
icculus@5596
   234
    /* some devices don't support this property, so errors are fine here. */
icculus@5596
   235
    if ((result == noErr) && (pid != -1)) {
icculus@5596
   236
        SDL_SetError("CoreAudio: requested device is being hogged.");
icculus@5596
   237
        return 0;
icculus@5596
   238
    }
icculus@5596
   239
icculus@5596
   240
    this->hidden->deviceID = devid;
icculus@5596
   241
    return 1;
icculus@5596
   242
}
icculus@5596
   243
#endif
icculus@5596
   244
slouken@935
   245
/* The CoreAudio callback */
slouken@1895
   246
static OSStatus
icculus@2049
   247
outputCallback(void *inRefCon,
slouken@2060
   248
               AudioUnitRenderActionFlags * ioActionFlags,
slouken@2060
   249
               const AudioTimeStamp * inTimeStamp,
slouken@2060
   250
               UInt32 inBusNumber, UInt32 inNumberFrames,
icculus@3617
   251
               AudioBufferList * ioData)
slouken@935
   252
{
slouken@1895
   253
    SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
icculus@3617
   254
    AudioBuffer *abuf;
slouken@935
   255
    UInt32 remaining, len;
slouken@935
   256
    void *ptr;
icculus@3617
   257
    UInt32 i;
slouken@935
   258
slouken@935
   259
    /* Only do anything if audio is enabled and not paused */
slouken@1895
   260
    if (!this->enabled || this->paused) {
icculus@3617
   261
        for (i = 0; i < ioData->mNumberBuffers; i++) {
icculus@3617
   262
            abuf = &ioData->mBuffers[i];
icculus@3617
   263
            SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
icculus@3617
   264
        }
slouken@935
   265
        return 0;
slouken@935
   266
    }
slouken@1895
   267
slouken@935
   268
    /* No SDL conversion should be needed here, ever, since we accept
slouken@935
   269
       any input format in OpenAudio, and leave the conversion to CoreAudio.
slouken@935
   270
     */
slouken@1338
   271
    /*
icculus@6281
   272
       SDL_assert(!this->convert.needed);
icculus@6281
   273
       SDL_assert(this->spec.channels == ioData->mNumberChannels);
slouken@1338
   274
     */
slouken@1895
   275
icculus@3617
   276
    for (i = 0; i < ioData->mNumberBuffers; i++) {
icculus@3617
   277
        abuf = &ioData->mBuffers[i];
icculus@3617
   278
        remaining = abuf->mDataByteSize;
icculus@3617
   279
        ptr = abuf->mData;
icculus@3617
   280
        while (remaining > 0) {
icculus@3636
   281
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
icculus@3617
   282
                /* Generate the data */
icculus@3617
   283
                SDL_mutexP(this->mixer_lock);
icculus@3617
   284
                (*this->spec.callback)(this->spec.userdata,
icculus@3636
   285
                            this->hidden->buffer, this->hidden->bufferSize);
icculus@3617
   286
                SDL_mutexV(this->mixer_lock);
icculus@3636
   287
                this->hidden->bufferOffset = 0;
icculus@3617
   288
            }
icculus@3617
   289
icculus@3636
   290
            len = this->hidden->bufferSize - this->hidden->bufferOffset;
icculus@3617
   291
            if (len > remaining)
icculus@3617
   292
                len = remaining;
icculus@3636
   293
            SDL_memcpy(ptr, (char *)this->hidden->buffer +
icculus@3636
   294
                       this->hidden->bufferOffset, len);
icculus@3617
   295
            ptr = (char *)ptr + len;
icculus@3617
   296
            remaining -= len;
icculus@3636
   297
            this->hidden->bufferOffset += len;
slouken@935
   298
        }
slouken@935
   299
    }
slouken@1895
   300
slouken@935
   301
    return 0;
slouken@935
   302
}
slouken@935
   303
icculus@2049
   304
static OSStatus
icculus@2049
   305
inputCallback(void *inRefCon,
slouken@2060
   306
              AudioUnitRenderActionFlags * ioActionFlags,
icculus@2049
   307
              const AudioTimeStamp * inTimeStamp,
icculus@2049
   308
              UInt32 inBusNumber, UInt32 inNumberFrames,
slouken@2060
   309
              AudioBufferList * ioData)
slouken@935
   310
{
icculus@2049
   311
    //err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer);
icculus@2049
   312
    // !!! FIXME: write me!
icculus@2049
   313
    return noErr;
slouken@935
   314
}
slouken@935
   315
icculus@2049
   316
icculus@2049
   317
static void
icculus@2049
   318
COREAUDIO_CloseDevice(_THIS)
slouken@935
   319
{
icculus@2049
   320
    if (this->hidden != NULL) {
icculus@2055
   321
        if (this->hidden->audioUnitOpened) {
icculus@2055
   322
            OSStatus result = noErr;
icculus@2055
   323
            AURenderCallbackStruct callback;
icculus@2055
   324
            const AudioUnitElement output_bus = 0;
icculus@2055
   325
            const AudioUnitElement input_bus = 1;
icculus@2055
   326
            const int iscapture = this->iscapture;
slouken@2060
   327
            const AudioUnitElement bus =
slouken@2060
   328
                ((iscapture) ? input_bus : output_bus);
slouken@2060
   329
            const AudioUnitScope scope =
slouken@2060
   330
                ((iscapture) ? kAudioUnitScope_Output :
slouken@2060
   331
                 kAudioUnitScope_Input);
slouken@935
   332
icculus@2055
   333
            /* stop processing the audio unit */
icculus@2055
   334
            result = AudioOutputUnitStop(this->hidden->audioUnit);
icculus@2049
   335
icculus@2055
   336
            /* Remove the input callback */
slouken@6352
   337
            SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
icculus@2055
   338
            result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2055
   339
                                          kAudioUnitProperty_SetRenderCallback,
icculus@2055
   340
                                          scope, bus, &callback,
slouken@2060
   341
                                          sizeof(callback));
icculus@2049
   342
icculus@5596
   343
            #if MACOSX_COREAUDIO
icculus@2055
   344
            CloseComponent(this->hidden->audioUnit);
slouken@6634
   345
            #else
slouken@6634
   346
            AudioComponentInstanceDispose(this->hidden->audioUnit);
icculus@5596
   347
            #endif
icculus@5596
   348
icculus@2055
   349
            this->hidden->audioUnitOpened = 0;
icculus@2055
   350
        }
icculus@2049
   351
        SDL_free(this->hidden->buffer);
icculus@2049
   352
        SDL_free(this->hidden);
icculus@2049
   353
        this->hidden = NULL;
slouken@935
   354
    }
icculus@2049
   355
}
slouken@935
   356
slouken@935
   357
icculus@2049
   358
static int
icculus@2049
   359
prepare_audiounit(_THIS, const char *devname, int iscapture,
slouken@2060
   360
                  const AudioStreamBasicDescription * strdesc)
slouken@935
   361
{
slouken@935
   362
    OSStatus result = noErr;
icculus@2049
   363
    AURenderCallbackStruct callback;
icculus@5596
   364
#if MACOSX_COREAUDIO
slouken@935
   365
    ComponentDescription desc;
icculus@2049
   366
    Component comp = NULL;
icculus@5596
   367
#else
icculus@5596
   368
    AudioComponentDescription desc;
icculus@5596
   369
    AudioComponent comp = NULL;
icculus@5596
   370
#endif
icculus@2049
   371
    const AudioUnitElement output_bus = 0;
icculus@2049
   372
    const AudioUnitElement input_bus = 1;
icculus@2049
   373
    const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
icculus@2049
   374
    const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
slouken@2060
   375
                                  kAudioUnitScope_Input);
icculus@2049
   376
icculus@5596
   377
#if MACOSX_COREAUDIO
icculus@2049
   378
    if (!find_device_by_name(this, devname, iscapture)) {
icculus@2049
   379
        SDL_SetError("Couldn't find requested CoreAudio device");
icculus@2049
   380
        return 0;
icculus@2049
   381
    }
icculus@5596
   382
#endif
icculus@5596
   383
    
icculus@5596
   384
    SDL_zero(desc);
icculus@2049
   385
    desc.componentType = kAudioUnitType_Output;
icculus@2049
   386
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
icculus@2049
   387
icculus@5596
   388
#if MACOSX_COREAUDIO
icculus@5596
   389
    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
icculus@2049
   390
    comp = FindNextComponent(NULL, &desc);
icculus@5596
   391
#else
icculus@6413
   392
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
icculus@5596
   393
    comp = AudioComponentFindNext(NULL, &desc);
icculus@5596
   394
#endif
icculus@5596
   395
icculus@2049
   396
    if (comp == NULL) {
icculus@2049
   397
        SDL_SetError("Couldn't find requested CoreAudio component");
icculus@2049
   398
        return 0;
icculus@2049
   399
    }
icculus@2049
   400
icculus@2049
   401
    /* Open & initialize the audio unit */
icculus@5596
   402
#if MACOSX_COREAUDIO
icculus@2049
   403
    result = OpenAComponent(comp, &this->hidden->audioUnit);
icculus@2049
   404
    CHECK_RESULT("OpenAComponent");
icculus@5596
   405
#else
icculus@5596
   406
    /*
icculus@5596
   407
       AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
icculus@5596
   408
       We can't use OpenAComponent on iPhone because it is not present
icculus@5596
   409
     */
icculus@5596
   410
    result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
icculus@5596
   411
    CHECK_RESULT("AudioComponentInstanceNew");
icculus@5596
   412
#endif
icculus@2049
   413
icculus@2055
   414
    this->hidden->audioUnitOpened = 1;
icculus@2055
   415
icculus@5596
   416
#if MACOSX_COREAUDIO
icculus@2049
   417
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   418
                                  kAudioOutputUnitProperty_CurrentDevice,
icculus@2049
   419
                                  kAudioUnitScope_Global, 0,
icculus@2049
   420
                                  &this->hidden->deviceID,
slouken@2060
   421
                                  sizeof(AudioDeviceID));
slouken@2060
   422
    CHECK_RESULT
slouken@2060
   423
        ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
icculus@5596
   424
#endif
icculus@2049
   425
icculus@2049
   426
    /* Set the data format of the audio unit. */
icculus@2049
   427
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   428
                                  kAudioUnitProperty_StreamFormat,
slouken@2060
   429
                                  scope, bus, strdesc, sizeof(*strdesc));
icculus@2049
   430
    CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
icculus@2049
   431
icculus@2049
   432
    /* Set the audio callback */
slouken@6352
   433
    SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
icculus@2049
   434
    callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
icculus@2049
   435
    callback.inputProcRefCon = this;
icculus@2049
   436
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   437
                                  kAudioUnitProperty_SetRenderCallback,
slouken@2060
   438
                                  scope, bus, &callback, sizeof(callback));
slouken@2060
   439
    CHECK_RESULT
icculus@3617
   440
        ("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
icculus@2049
   441
icculus@2049
   442
    /* Calculate the final parameters for this audio specification */
icculus@2049
   443
    SDL_CalculateAudioSpec(&this->spec);
icculus@2049
   444
icculus@2049
   445
    /* Allocate a sample buffer */
icculus@2049
   446
    this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
icculus@2049
   447
    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
icculus@2049
   448
icculus@2049
   449
    result = AudioUnitInitialize(this->hidden->audioUnit);
icculus@2049
   450
    CHECK_RESULT("AudioUnitInitialize");
icculus@2049
   451
icculus@2049
   452
    /* Finally, start processing of the audio unit */
icculus@2049
   453
    result = AudioOutputUnitStart(this->hidden->audioUnit);
icculus@2049
   454
    CHECK_RESULT("AudioOutputUnitStart");
icculus@2049
   455
icculus@2049
   456
    /* We're running! */
icculus@2049
   457
    return 1;
icculus@2049
   458
}
icculus@2049
   459
icculus@2049
   460
icculus@2049
   461
static int
icculus@2049
   462
COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
icculus@2049
   463
{
icculus@2016
   464
    AudioStreamBasicDescription strdesc;
icculus@2049
   465
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2003
   466
    int valid_datatype = 0;
slouken@935
   467
icculus@2049
   468
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   469
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   470
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   471
    if (this->hidden == NULL) {
icculus@2049
   472
        SDL_OutOfMemory();
icculus@2049
   473
        return (0);
icculus@2049
   474
    }
icculus@2049
   475
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@2049
   476
slouken@935
   477
    /* Setup a AudioStreamBasicDescription with the requested format */
icculus@2049
   478
    SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
icculus@2016
   479
    strdesc.mFormatID = kAudioFormatLinearPCM;
icculus@2016
   480
    strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
icculus@2049
   481
    strdesc.mChannelsPerFrame = this->spec.channels;
icculus@2049
   482
    strdesc.mSampleRate = this->spec.freq;
icculus@2016
   483
    strdesc.mFramesPerPacket = 1;
slouken@1895
   484
icculus@2003
   485
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   486
        this->spec.format = test_format;
icculus@2003
   487
        /* Just a list of valid SDL formats, so people don't pass junk here. */
icculus@2003
   488
        switch (test_format) {
slouken@2043
   489
        case AUDIO_U8:
slouken@2043
   490
        case AUDIO_S8:
slouken@2043
   491
        case AUDIO_U16LSB:
slouken@2043
   492
        case AUDIO_S16LSB:
slouken@2043
   493
        case AUDIO_U16MSB:
slouken@2043
   494
        case AUDIO_S16MSB:
slouken@2043
   495
        case AUDIO_S32LSB:
slouken@2043
   496
        case AUDIO_S32MSB:
slouken@2043
   497
        case AUDIO_F32LSB:
slouken@2043
   498
        case AUDIO_F32MSB:
slouken@2043
   499
            valid_datatype = 1;
icculus@2049
   500
            strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
icculus@2049
   501
            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
slouken@2043
   502
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
slouken@935
   503
icculus@2049
   504
            if (SDL_AUDIO_ISFLOAT(this->spec.format))
slouken@2043
   505
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
icculus@2049
   506
            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
slouken@2043
   507
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
slouken@2043
   508
            break;
icculus@2003
   509
        }
icculus@2003
   510
    }
icculus@2003
   511
slouken@2043
   512
    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
icculus@2055
   513
        COREAUDIO_CloseDevice(this);
icculus@2003
   514
        SDL_SetError("Unsupported audio format");
icculus@2049
   515
        return 0;
icculus@2003
   516
    }
icculus@2003
   517
icculus@2016
   518
    strdesc.mBytesPerFrame =
icculus@2016
   519
        strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
icculus@2016
   520
    strdesc.mBytesPerPacket =
icculus@2016
   521
        strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
slouken@935
   522
icculus@2049
   523
    if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
icculus@2057
   524
        COREAUDIO_CloseDevice(this);
slouken@2060
   525
        return 0;               /* prepare_audiounit() will call SDL_SetError()... */
slouken@935
   526
    }
slouken@1895
   527
slouken@2060
   528
    return 1;                   /* good to go. */
slouken@1895
   529
}
slouken@935
   530
icculus@2049
   531
static int
slouken@2060
   532
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   533
{
icculus@2049
   534
    /* Set the function pointers */
icculus@2049
   535
    impl->OpenDevice = COREAUDIO_OpenDevice;
icculus@2049
   536
    impl->CloseDevice = COREAUDIO_CloseDevice;
icculus@5596
   537
icculus@5596
   538
#if MACOSX_COREAUDIO
icculus@5596
   539
    impl->DetectDevices = COREAUDIO_DetectDevices;
icculus@5596
   540
#else
icculus@5596
   541
    impl->OnlyHasDefaultOutputDevice = 1;
slouken@6628
   542
slouken@6628
   543
    /* Set category to ambient sound so that other music continues playing.
slouken@6628
   544
       You can change this at runtime in your own code if you need different
slouken@6628
   545
       behavior.  If this is common, we can add an SDL hint for this.
slouken@6628
   546
    */
slouken@6628
   547
    AudioSessionInitialize(NULL, NULL, NULL, nil);
slouken@6628
   548
    UInt32 category = kAudioSessionCategory_AmbientSound;
slouken@6628
   549
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
icculus@5596
   550
#endif
slouken@6628
   551
icculus@2049
   552
    impl->ProvidesOwnCallbackThread = 1;
icculus@2049
   553
icculus@3699
   554
    return 1;   /* this audio target is available. */
icculus@2049
   555
}
icculus@2049
   556
icculus@2049
   557
AudioBootStrap COREAUDIO_bootstrap = {
icculus@5596
   558
    "coreaudio", "CoreAudio", COREAUDIO_Init, 0
icculus@2049
   559
};
icculus@2049
   560
slouken@1895
   561
/* vi: set ts=4 sw=4 expandtab: */