src/audio/coreaudio/SDL_coreaudio.c
author Sam Lantinga
Sat, 06 Oct 2012 11:23:47 -0700
changeset 6565 1f3c0df426dc
parent 6413 701a0c0d70d0
child 6628 7994e6979876
permissions -rw-r--r--
When using Xinerama, XVidMode always works on screen 0. Otherwise use the real X11 screen.
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
            /* !!! FIXME: how does iOS free this? */
icculus@5596
   344
            #if MACOSX_COREAUDIO
icculus@2055
   345
            CloseComponent(this->hidden->audioUnit);
icculus@5596
   346
            #endif
icculus@5596
   347
icculus@2055
   348
            this->hidden->audioUnitOpened = 0;
icculus@2055
   349
        }
icculus@2049
   350
        SDL_free(this->hidden->buffer);
icculus@2049
   351
        SDL_free(this->hidden);
icculus@2049
   352
        this->hidden = NULL;
slouken@935
   353
    }
icculus@2049
   354
}
slouken@935
   355
slouken@935
   356
icculus@2049
   357
static int
icculus@2049
   358
prepare_audiounit(_THIS, const char *devname, int iscapture,
slouken@2060
   359
                  const AudioStreamBasicDescription * strdesc)
slouken@935
   360
{
slouken@935
   361
    OSStatus result = noErr;
icculus@2049
   362
    AURenderCallbackStruct callback;
icculus@5596
   363
#if MACOSX_COREAUDIO
slouken@935
   364
    ComponentDescription desc;
icculus@2049
   365
    Component comp = NULL;
icculus@5596
   366
#else
icculus@5596
   367
    AudioComponentDescription desc;
icculus@5596
   368
    AudioComponent comp = NULL;
icculus@5596
   369
#endif
icculus@2049
   370
    const AudioUnitElement output_bus = 0;
icculus@2049
   371
    const AudioUnitElement input_bus = 1;
icculus@2049
   372
    const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
icculus@2049
   373
    const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
slouken@2060
   374
                                  kAudioUnitScope_Input);
icculus@2049
   375
icculus@5596
   376
#if MACOSX_COREAUDIO
icculus@2049
   377
    if (!find_device_by_name(this, devname, iscapture)) {
icculus@2049
   378
        SDL_SetError("Couldn't find requested CoreAudio device");
icculus@2049
   379
        return 0;
icculus@2049
   380
    }
icculus@5596
   381
#endif
icculus@5596
   382
    
icculus@5596
   383
    SDL_zero(desc);
icculus@2049
   384
    desc.componentType = kAudioUnitType_Output;
icculus@2049
   385
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
icculus@2049
   386
icculus@5596
   387
#if MACOSX_COREAUDIO
icculus@5596
   388
    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
icculus@2049
   389
    comp = FindNextComponent(NULL, &desc);
icculus@5596
   390
#else
icculus@6413
   391
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
icculus@5596
   392
    comp = AudioComponentFindNext(NULL, &desc);
icculus@5596
   393
#endif
icculus@5596
   394
icculus@2049
   395
    if (comp == NULL) {
icculus@2049
   396
        SDL_SetError("Couldn't find requested CoreAudio component");
icculus@2049
   397
        return 0;
icculus@2049
   398
    }
icculus@2049
   399
icculus@2049
   400
    /* Open & initialize the audio unit */
icculus@5596
   401
#if MACOSX_COREAUDIO
icculus@2049
   402
    result = OpenAComponent(comp, &this->hidden->audioUnit);
icculus@2049
   403
    CHECK_RESULT("OpenAComponent");
icculus@5596
   404
#else
icculus@5596
   405
    /*
icculus@5596
   406
       AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
icculus@5596
   407
       We can't use OpenAComponent on iPhone because it is not present
icculus@5596
   408
     */
icculus@5596
   409
    result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
icculus@5596
   410
    CHECK_RESULT("AudioComponentInstanceNew");
icculus@5596
   411
#endif
icculus@2049
   412
icculus@2055
   413
    this->hidden->audioUnitOpened = 1;
icculus@2055
   414
icculus@5596
   415
#if MACOSX_COREAUDIO
icculus@2049
   416
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   417
                                  kAudioOutputUnitProperty_CurrentDevice,
icculus@2049
   418
                                  kAudioUnitScope_Global, 0,
icculus@2049
   419
                                  &this->hidden->deviceID,
slouken@2060
   420
                                  sizeof(AudioDeviceID));
slouken@2060
   421
    CHECK_RESULT
slouken@2060
   422
        ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
icculus@5596
   423
#endif
icculus@2049
   424
icculus@2049
   425
    /* Set the data format of the audio unit. */
icculus@2049
   426
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   427
                                  kAudioUnitProperty_StreamFormat,
slouken@2060
   428
                                  scope, bus, strdesc, sizeof(*strdesc));
icculus@2049
   429
    CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
icculus@2049
   430
icculus@2049
   431
    /* Set the audio callback */
slouken@6352
   432
    SDL_memset(&callback, 0, sizeof(AURenderCallbackStruct));
icculus@2049
   433
    callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
icculus@2049
   434
    callback.inputProcRefCon = this;
icculus@2049
   435
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   436
                                  kAudioUnitProperty_SetRenderCallback,
slouken@2060
   437
                                  scope, bus, &callback, sizeof(callback));
slouken@2060
   438
    CHECK_RESULT
icculus@3617
   439
        ("AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)");
icculus@2049
   440
icculus@2049
   441
    /* Calculate the final parameters for this audio specification */
icculus@2049
   442
    SDL_CalculateAudioSpec(&this->spec);
icculus@2049
   443
icculus@2049
   444
    /* Allocate a sample buffer */
icculus@2049
   445
    this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
icculus@2049
   446
    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
icculus@2049
   447
icculus@2049
   448
    result = AudioUnitInitialize(this->hidden->audioUnit);
icculus@2049
   449
    CHECK_RESULT("AudioUnitInitialize");
icculus@2049
   450
icculus@2049
   451
    /* Finally, start processing of the audio unit */
icculus@2049
   452
    result = AudioOutputUnitStart(this->hidden->audioUnit);
icculus@2049
   453
    CHECK_RESULT("AudioOutputUnitStart");
icculus@2049
   454
icculus@2049
   455
    /* We're running! */
icculus@2049
   456
    return 1;
icculus@2049
   457
}
icculus@2049
   458
icculus@2049
   459
icculus@2049
   460
static int
icculus@2049
   461
COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
icculus@2049
   462
{
icculus@2016
   463
    AudioStreamBasicDescription strdesc;
icculus@2049
   464
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2003
   465
    int valid_datatype = 0;
slouken@935
   466
icculus@2049
   467
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   468
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   469
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   470
    if (this->hidden == NULL) {
icculus@2049
   471
        SDL_OutOfMemory();
icculus@2049
   472
        return (0);
icculus@2049
   473
    }
icculus@2049
   474
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@2049
   475
slouken@935
   476
    /* Setup a AudioStreamBasicDescription with the requested format */
icculus@2049
   477
    SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
icculus@2016
   478
    strdesc.mFormatID = kAudioFormatLinearPCM;
icculus@2016
   479
    strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
icculus@2049
   480
    strdesc.mChannelsPerFrame = this->spec.channels;
icculus@2049
   481
    strdesc.mSampleRate = this->spec.freq;
icculus@2016
   482
    strdesc.mFramesPerPacket = 1;
slouken@1895
   483
icculus@2003
   484
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   485
        this->spec.format = test_format;
icculus@2003
   486
        /* Just a list of valid SDL formats, so people don't pass junk here. */
icculus@2003
   487
        switch (test_format) {
slouken@2043
   488
        case AUDIO_U8:
slouken@2043
   489
        case AUDIO_S8:
slouken@2043
   490
        case AUDIO_U16LSB:
slouken@2043
   491
        case AUDIO_S16LSB:
slouken@2043
   492
        case AUDIO_U16MSB:
slouken@2043
   493
        case AUDIO_S16MSB:
slouken@2043
   494
        case AUDIO_S32LSB:
slouken@2043
   495
        case AUDIO_S32MSB:
slouken@2043
   496
        case AUDIO_F32LSB:
slouken@2043
   497
        case AUDIO_F32MSB:
slouken@2043
   498
            valid_datatype = 1;
icculus@2049
   499
            strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
icculus@2049
   500
            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
slouken@2043
   501
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
slouken@935
   502
icculus@2049
   503
            if (SDL_AUDIO_ISFLOAT(this->spec.format))
slouken@2043
   504
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
icculus@2049
   505
            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
slouken@2043
   506
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
slouken@2043
   507
            break;
icculus@2003
   508
        }
icculus@2003
   509
    }
icculus@2003
   510
slouken@2043
   511
    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
icculus@2055
   512
        COREAUDIO_CloseDevice(this);
icculus@2003
   513
        SDL_SetError("Unsupported audio format");
icculus@2049
   514
        return 0;
icculus@2003
   515
    }
icculus@2003
   516
icculus@2016
   517
    strdesc.mBytesPerFrame =
icculus@2016
   518
        strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
icculus@2016
   519
    strdesc.mBytesPerPacket =
icculus@2016
   520
        strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
slouken@935
   521
icculus@2049
   522
    if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
icculus@2057
   523
        COREAUDIO_CloseDevice(this);
slouken@2060
   524
        return 0;               /* prepare_audiounit() will call SDL_SetError()... */
slouken@935
   525
    }
slouken@1895
   526
slouken@2060
   527
    return 1;                   /* good to go. */
slouken@1895
   528
}
slouken@935
   529
icculus@2049
   530
static int
slouken@2060
   531
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   532
{
icculus@2049
   533
    /* Set the function pointers */
icculus@2049
   534
    impl->OpenDevice = COREAUDIO_OpenDevice;
icculus@2049
   535
    impl->CloseDevice = COREAUDIO_CloseDevice;
icculus@5596
   536
icculus@5596
   537
#if MACOSX_COREAUDIO
icculus@5596
   538
    impl->DetectDevices = COREAUDIO_DetectDevices;
icculus@5596
   539
#else
icculus@5596
   540
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@5596
   541
#endif
icculus@5596
   542
    
icculus@2049
   543
    impl->ProvidesOwnCallbackThread = 1;
icculus@2049
   544
icculus@3699
   545
    return 1;   /* this audio target is available. */
icculus@2049
   546
}
icculus@2049
   547
icculus@2049
   548
AudioBootStrap COREAUDIO_bootstrap = {
icculus@5596
   549
    "coreaudio", "CoreAudio", COREAUDIO_Init, 0
icculus@2049
   550
};
icculus@2049
   551
slouken@1895
   552
/* vi: set ts=4 sw=4 expandtab: */