src/audio/macosx/SDL_coreaudio.c
author Sam Lantinga
Mon, 01 Sep 2008 16:04:20 +0000
changeset 2738 79c1bd651f04
parent 2281 6359f74f3170
child 2859 99210400e8b9
permissions -rw-r--r--
Fixed a bunch of compile warnings on Mac OS X
slouken@935
     1
/*
slouken@935
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@935
     4
slouken@935
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@935
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@935
     9
slouken@935
    10
    This library is distributed in the hope that it will be useful,
slouken@935
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@935
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@935
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@935
    18
slouken@935
    19
    Sam Lantinga
slouken@935
    20
    slouken@libsdl.org
slouken@935
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@935
    23
icculus@2049
    24
#include <CoreAudio/CoreAudio.h>
slouken@935
    25
#include <AudioUnit/AudioUnit.h>
slouken@2281
    26
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
slouken@2281
    27
#include <AudioUnit/AUNTComponent.h>
slouken@2281
    28
#endif
slouken@935
    29
slouken@935
    30
#include "SDL_audio.h"
slouken@1361
    31
#include "../SDL_audio_c.h"
slouken@1361
    32
#include "../SDL_sysaudio.h"
slouken@935
    33
#include "SDL_coreaudio.h"
slouken@935
    34
icculus@2049
    35
#define DEBUG_COREAUDIO 0
slouken@935
    36
icculus@2049
    37
typedef struct COREAUDIO_DeviceList
icculus@2049
    38
{
icculus@2049
    39
    AudioDeviceID id;
icculus@2049
    40
    const char *name;
icculus@2049
    41
} COREAUDIO_DeviceList;
slouken@935
    42
icculus@2049
    43
static COREAUDIO_DeviceList *inputDevices = NULL;
icculus@2049
    44
static int inputDeviceCount = 0;
icculus@2049
    45
static COREAUDIO_DeviceList *outputDevices = NULL;
icculus@2049
    46
static int outputDeviceCount = 0;
slouken@935
    47
slouken@1895
    48
static void
slouken@2060
    49
free_device_list(COREAUDIO_DeviceList ** devices, int *devCount)
slouken@935
    50
{
icculus@2049
    51
    if (*devices) {
icculus@2049
    52
        int i = *devCount;
icculus@2049
    53
        while (i--)
icculus@2049
    54
            SDL_free((void *) (*devices)[i].name);
icculus@2049
    55
        SDL_free(*devices);
icculus@2049
    56
        *devices = NULL;
icculus@2049
    57
    }
icculus@2049
    58
    *devCount = 0;
slouken@935
    59
}
slouken@935
    60
icculus@2049
    61
icculus@2049
    62
static void
slouken@2060
    63
build_device_list(int iscapture, COREAUDIO_DeviceList ** devices,
slouken@2060
    64
                  int *devCount)
slouken@935
    65
{
icculus@2049
    66
    Boolean outWritable = 0;
icculus@2049
    67
    OSStatus result = noErr;
icculus@2049
    68
    UInt32 size = 0;
icculus@2049
    69
    AudioDeviceID *devs = NULL;
icculus@2049
    70
    UInt32 i = 0;
icculus@2049
    71
    UInt32 max = 0;
icculus@2049
    72
icculus@2049
    73
    free_device_list(devices, devCount);
icculus@2049
    74
icculus@2049
    75
    result = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
slouken@2060
    76
                                          &size, &outWritable);
icculus@2049
    77
icculus@2049
    78
    if (result != kAudioHardwareNoError)
icculus@2049
    79
        return;
icculus@2049
    80
icculus@2049
    81
    devs = (AudioDeviceID *) alloca(size);
icculus@2049
    82
    if (devs == NULL)
icculus@2049
    83
        return;
icculus@2049
    84
slouken@2060
    85
    max = size / sizeof(AudioDeviceID);
slouken@2060
    86
    *devices = (COREAUDIO_DeviceList *) SDL_malloc(max * sizeof(**devices));
icculus@2049
    87
    if (*devices == NULL)
icculus@2049
    88
        return;
icculus@2049
    89
icculus@2049
    90
    result = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
slouken@2060
    91
                                      &size, devs);
icculus@2049
    92
    if (result != kAudioHardwareNoError)
icculus@2049
    93
        return;
icculus@2049
    94
icculus@2049
    95
    for (i = 0; i < max; i++) {
icculus@2049
    96
        CFStringRef cfstr = NULL;
icculus@2049
    97
        char *ptr = NULL;
icculus@2049
    98
        AudioDeviceID dev = devs[i];
icculus@2049
    99
        AudioBufferList *buflist = NULL;
icculus@2049
   100
        int usable = 0;
icculus@2049
   101
        CFIndex len = 0;
icculus@2049
   102
icculus@2049
   103
        result = AudioDeviceGetPropertyInfo(dev, 0, iscapture,
slouken@2060
   104
                                            kAudioDevicePropertyStreamConfiguration,
slouken@2060
   105
                                            &size, &outWritable);
icculus@2049
   106
        if (result != noErr)
icculus@2049
   107
            continue;
icculus@2049
   108
icculus@2049
   109
        buflist = (AudioBufferList *) SDL_malloc(size);
icculus@2049
   110
        if (buflist == NULL)
icculus@2049
   111
            continue;
icculus@2049
   112
icculus@2049
   113
        result = AudioDeviceGetProperty(dev, 0, iscapture,
slouken@2060
   114
                                        kAudioDevicePropertyStreamConfiguration,
slouken@2060
   115
                                        &size, buflist);
slouken@935
   116
icculus@2049
   117
        if (result == noErr) {
icculus@2049
   118
            UInt32 j;
icculus@2049
   119
            for (j = 0; j < buflist->mNumberBuffers; j++) {
icculus@2049
   120
                if (buflist->mBuffers[j].mNumberChannels > 0) {
icculus@2049
   121
                    usable = 1;
icculus@2049
   122
                    break;
icculus@2049
   123
                }
icculus@2049
   124
            }
icculus@2049
   125
        }
icculus@2049
   126
icculus@2049
   127
        SDL_free(buflist);
icculus@2049
   128
icculus@2049
   129
        if (!usable)
icculus@2049
   130
            continue;
icculus@2049
   131
slouken@2060
   132
        size = sizeof(CFStringRef);
icculus@2049
   133
        result = AudioDeviceGetProperty(dev, 0, iscapture,
slouken@2110
   134
                                        kAudioDevicePropertyDeviceNameCFString,
icculus@2049
   135
                                        &size, &cfstr);
icculus@2049
   136
icculus@2049
   137
        if (result != kAudioHardwareNoError)
icculus@2049
   138
            continue;
icculus@2049
   139
icculus@2049
   140
        len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
icculus@2049
   141
                                                kCFStringEncodingUTF8);
icculus@2049
   142
icculus@2049
   143
        ptr = (char *) SDL_malloc(len + 1);
slouken@2060
   144
        usable = ((ptr != NULL) &&
slouken@2060
   145
                  (CFStringGetCString
slouken@2060
   146
                   (cfstr, ptr, len + 1, kCFStringEncodingUTF8)));
icculus@2049
   147
icculus@2049
   148
        CFRelease(cfstr);
icculus@2049
   149
icculus@2049
   150
        if (usable) {
icculus@2049
   151
            len = strlen(ptr);
icculus@2049
   152
            /* Some devices have whitespace at the end...trim it. */
slouken@2060
   153
            while ((len > 0) && (ptr[len - 1] == ' ')) {
icculus@2049
   154
                len--;
icculus@2049
   155
            }
icculus@2049
   156
            usable = (len > 0);
icculus@2049
   157
        }
icculus@2049
   158
icculus@2049
   159
        if (!usable) {
icculus@2049
   160
            SDL_free(ptr);
icculus@2049
   161
        } else {
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@2049
   169
icculus@2049
   170
            (*devices)[*devCount].id = dev;
icculus@2049
   171
            (*devices)[*devCount].name = ptr;
icculus@2049
   172
            (*devCount)++;
icculus@2049
   173
        }
slouken@935
   174
    }
slouken@935
   175
}
slouken@935
   176
icculus@2049
   177
static inline void
icculus@2049
   178
build_device_lists(void)
icculus@2049
   179
{
icculus@2049
   180
    build_device_list(0, &outputDevices, &outputDeviceCount);
icculus@2049
   181
    build_device_list(1, &inputDevices, &inputDeviceCount);
icculus@2049
   182
}
icculus@2049
   183
icculus@2049
   184
icculus@2049
   185
static inline void
icculus@2049
   186
free_device_lists(void)
icculus@2049
   187
{
icculus@2049
   188
    free_device_list(&outputDevices, &outputDeviceCount);
icculus@2049
   189
    free_device_list(&inputDevices, &inputDeviceCount);
icculus@2049
   190
}
icculus@2049
   191
icculus@2049
   192
icculus@2049
   193
static int
slouken@2060
   194
find_device_id(const char *devname, int iscapture, AudioDeviceID * id)
icculus@2049
   195
{
icculus@2049
   196
    int i = ((iscapture) ? inputDeviceCount : outputDeviceCount);
icculus@2049
   197
    COREAUDIO_DeviceList *devs = ((iscapture) ? inputDevices : outputDevices);
icculus@2049
   198
    while (i--) {
icculus@2049
   199
        if (SDL_strcmp(devname, devs->name) == 0) {
icculus@2049
   200
            *id = devs->id;
icculus@2049
   201
            return 1;
icculus@2049
   202
        }
icculus@2049
   203
        devs++;
icculus@2049
   204
    }
icculus@2049
   205
icculus@2049
   206
    return 0;
icculus@2049
   207
}
icculus@2049
   208
icculus@2049
   209
icculus@2049
   210
static int
icculus@2049
   211
COREAUDIO_DetectDevices(int iscapture)
icculus@2049
   212
{
icculus@2049
   213
    if (iscapture) {
icculus@2049
   214
        build_device_list(1, &inputDevices, &inputDeviceCount);
icculus@2049
   215
        return inputDeviceCount;
icculus@2049
   216
    } else {
icculus@2049
   217
        build_device_list(0, &outputDevices, &outputDeviceCount);
icculus@2049
   218
        return outputDeviceCount;
icculus@2049
   219
    }
icculus@2049
   220
slouken@2060
   221
    return 0;                   /* shouldn't ever hit this. */
icculus@2049
   222
}
icculus@2049
   223
icculus@2049
   224
icculus@2049
   225
static const char *
icculus@2049
   226
COREAUDIO_GetDeviceName(int index, int iscapture)
icculus@2049
   227
{
icculus@2049
   228
    if ((iscapture) && (index < inputDeviceCount)) {
icculus@2049
   229
        return inputDevices[index].name;
icculus@2049
   230
    } else if ((!iscapture) && (index < outputDeviceCount)) {
icculus@2049
   231
        return outputDevices[index].name;
icculus@2049
   232
    }
icculus@2049
   233
icculus@2049
   234
    SDL_SetError("No such device");
icculus@2049
   235
    return NULL;
icculus@2049
   236
}
icculus@2049
   237
icculus@2049
   238
icculus@2049
   239
static void
icculus@2049
   240
COREAUDIO_Deinitialize(void)
icculus@2049
   241
{
icculus@2049
   242
    free_device_lists();
icculus@2049
   243
}
icculus@2049
   244
slouken@935
   245
slouken@935
   246
/* The CoreAudio callback */
slouken@1895
   247
static OSStatus
icculus@2049
   248
outputCallback(void *inRefCon,
slouken@2060
   249
               AudioUnitRenderActionFlags * ioActionFlags,
slouken@2060
   250
               const AudioTimeStamp * inTimeStamp,
slouken@2060
   251
               UInt32 inBusNumber, UInt32 inNumberFrames,
slouken@2060
   252
               AudioBufferList * ioDataList)
slouken@935
   253
{
slouken@1895
   254
    SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
icculus@2049
   255
    AudioBuffer *ioData = &ioDataList->mBuffers[0];
slouken@935
   256
    UInt32 remaining, len;
slouken@935
   257
    void *ptr;
slouken@935
   258
icculus@2049
   259
    /* Is there ever more than one buffer, and what do you do with it? */
icculus@2049
   260
    if (ioDataList->mNumberBuffers != 1) {
icculus@2049
   261
        return noErr;
icculus@2049
   262
    }
icculus@2049
   263
slouken@935
   264
    /* Only do anything if audio is enabled and not paused */
slouken@1895
   265
    if (!this->enabled || this->paused) {
slouken@1336
   266
        SDL_memset(ioData->mData, this->spec.silence, ioData->mDataByteSize);
slouken@935
   267
        return 0;
slouken@935
   268
    }
slouken@1895
   269
slouken@935
   270
    /* No SDL conversion should be needed here, ever, since we accept
slouken@935
   271
       any input format in OpenAudio, and leave the conversion to CoreAudio.
slouken@935
   272
     */
slouken@1338
   273
    /*
slouken@1895
   274
       assert(!this->convert.needed);
slouken@1895
   275
       assert(this->spec.channels == ioData->mNumberChannels);
slouken@1338
   276
     */
slouken@1895
   277
slouken@935
   278
    remaining = ioData->mDataByteSize;
slouken@935
   279
    ptr = ioData->mData;
slouken@935
   280
    while (remaining > 0) {
icculus@2049
   281
        if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
slouken@935
   282
            /* Generate the data */
icculus@2049
   283
            SDL_memset(this->hidden->buffer, this->spec.silence,
icculus@2049
   284
                       this->hidden->bufferSize);
slouken@935
   285
            SDL_mutexP(this->mixer_lock);
icculus@2049
   286
            (*this->spec.callback) (this->spec.userdata, this->hidden->buffer,
icculus@2049
   287
                                    this->hidden->bufferSize);
slouken@935
   288
            SDL_mutexV(this->mixer_lock);
icculus@2049
   289
            this->hidden->bufferOffset = 0;
slouken@935
   290
        }
slouken@1895
   291
icculus@2049
   292
        len = this->hidden->bufferSize - this->hidden->bufferOffset;
slouken@935
   293
        if (len > remaining)
slouken@935
   294
            len = remaining;
icculus@2049
   295
        SDL_memcpy(ptr,
slouken@2060
   296
                   (char *) this->hidden->buffer + this->hidden->bufferOffset,
slouken@2060
   297
                   len);
slouken@1895
   298
        ptr = (char *) ptr + len;
slouken@935
   299
        remaining -= len;
icculus@2049
   300
        this->hidden->bufferOffset += len;
slouken@935
   301
    }
slouken@1895
   302
slouken@935
   303
    return 0;
slouken@935
   304
}
slouken@935
   305
icculus@2049
   306
static OSStatus
icculus@2049
   307
inputCallback(void *inRefCon,
slouken@2060
   308
              AudioUnitRenderActionFlags * ioActionFlags,
icculus@2049
   309
              const AudioTimeStamp * inTimeStamp,
icculus@2049
   310
              UInt32 inBusNumber, UInt32 inNumberFrames,
slouken@2060
   311
              AudioBufferList * ioData)
slouken@935
   312
{
icculus@2049
   313
    //err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer);
icculus@2049
   314
    // !!! FIXME: write me!
icculus@2049
   315
    return noErr;
slouken@935
   316
}
slouken@935
   317
icculus@2049
   318
icculus@2049
   319
static void
icculus@2049
   320
COREAUDIO_CloseDevice(_THIS)
slouken@935
   321
{
icculus@2049
   322
    if (this->hidden != NULL) {
icculus@2055
   323
        if (this->hidden->audioUnitOpened) {
icculus@2055
   324
            OSStatus result = noErr;
icculus@2055
   325
            AURenderCallbackStruct callback;
icculus@2055
   326
            const AudioUnitElement output_bus = 0;
icculus@2055
   327
            const AudioUnitElement input_bus = 1;
icculus@2055
   328
            const int iscapture = this->iscapture;
slouken@2060
   329
            const AudioUnitElement bus =
slouken@2060
   330
                ((iscapture) ? input_bus : output_bus);
slouken@2060
   331
            const AudioUnitScope scope =
slouken@2060
   332
                ((iscapture) ? kAudioUnitScope_Output :
slouken@2060
   333
                 kAudioUnitScope_Input);
slouken@935
   334
icculus@2055
   335
            /* stop processing the audio unit */
icculus@2055
   336
            result = AudioOutputUnitStop(this->hidden->audioUnit);
icculus@2049
   337
icculus@2055
   338
            /* Remove the input callback */
slouken@2060
   339
            SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
icculus@2055
   340
            result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2055
   341
                                          kAudioUnitProperty_SetRenderCallback,
icculus@2055
   342
                                          scope, bus, &callback,
slouken@2060
   343
                                          sizeof(callback));
icculus@2049
   344
icculus@2055
   345
            CloseComponent(this->hidden->audioUnit);
icculus@2055
   346
            this->hidden->audioUnitOpened = 0;
icculus@2055
   347
        }
icculus@2049
   348
        SDL_free(this->hidden->buffer);
icculus@2049
   349
        SDL_free(this->hidden);
icculus@2049
   350
        this->hidden = NULL;
slouken@935
   351
    }
icculus@2049
   352
}
slouken@935
   353
slouken@935
   354
slouken@935
   355
#define CHECK_RESULT(msg) \
slouken@935
   356
    if (result != noErr) { \
icculus@2049
   357
        COREAUDIO_CloseDevice(this); \
icculus@2017
   358
        SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
icculus@2049
   359
        return 0; \
icculus@2049
   360
    }
icculus@2049
   361
icculus@2049
   362
static int
icculus@2049
   363
find_device_by_name(_THIS, const char *devname, int iscapture)
icculus@2049
   364
{
icculus@2049
   365
    AudioDeviceID devid = 0;
icculus@2049
   366
    OSStatus result = noErr;
icculus@2049
   367
    UInt32 size = 0;
icculus@2049
   368
    UInt32 alive = 0;
icculus@2049
   369
    pid_t pid = 0;
icculus@2049
   370
icculus@2049
   371
    if (devname == NULL) {
slouken@2060
   372
        size = sizeof(AudioDeviceID);
icculus@2049
   373
        const AudioHardwarePropertyID propid =
slouken@2060
   374
            ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
slouken@2060
   375
             kAudioHardwarePropertyDefaultOutputDevice);
icculus@2049
   376
icculus@2049
   377
        result = AudioHardwareGetProperty(propid, &size, &devid);
icculus@2049
   378
        CHECK_RESULT("AudioHardwareGetProperty (default device)");
icculus@2049
   379
    } else {
icculus@2049
   380
        if (!find_device_id(devname, iscapture, &devid)) {
icculus@2049
   381
            SDL_SetError("CoreAudio: No such audio device.");
icculus@2049
   382
            return 0;
icculus@2049
   383
        }
slouken@935
   384
    }
slouken@935
   385
slouken@2060
   386
    size = sizeof(alive);
icculus@2049
   387
    result = AudioDeviceGetProperty(devid, 0, iscapture,
icculus@2049
   388
                                    kAudioDevicePropertyDeviceIsAlive,
icculus@2049
   389
                                    &size, &alive);
slouken@2060
   390
    CHECK_RESULT
slouken@2060
   391
        ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
slouken@935
   392
icculus@2049
   393
    if (!alive) {
icculus@2049
   394
        SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
icculus@2049
   395
        return 0;
icculus@2049
   396
    }
icculus@2049
   397
slouken@2060
   398
    size = sizeof(pid);
icculus@2049
   399
    result = AudioDeviceGetProperty(devid, 0, iscapture,
icculus@2049
   400
                                    kAudioDevicePropertyHogMode, &size, &pid);
icculus@2049
   401
icculus@2049
   402
    /* some devices don't support this property, so errors are fine here. */
icculus@2049
   403
    if ((result == noErr) && (pid != -1)) {
icculus@2049
   404
        SDL_SetError("CoreAudio: requested device is being hogged.");
icculus@2049
   405
        return 0;
icculus@2049
   406
    }
icculus@2049
   407
icculus@2049
   408
    this->hidden->deviceID = devid;
icculus@2049
   409
    return 1;
icculus@2049
   410
}
icculus@2049
   411
icculus@2049
   412
icculus@2049
   413
static int
icculus@2049
   414
prepare_audiounit(_THIS, const char *devname, int iscapture,
slouken@2060
   415
                  const AudioStreamBasicDescription * strdesc)
slouken@935
   416
{
slouken@935
   417
    OSStatus result = noErr;
icculus@2049
   418
    AURenderCallbackStruct callback;
slouken@935
   419
    ComponentDescription desc;
icculus@2049
   420
    Component comp = NULL;
icculus@2049
   421
    UInt32 enableIO = 0;
icculus@2049
   422
    const AudioUnitElement output_bus = 0;
icculus@2049
   423
    const AudioUnitElement input_bus = 1;
icculus@2049
   424
    const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
icculus@2049
   425
    const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
slouken@2060
   426
                                  kAudioUnitScope_Input);
icculus@2049
   427
icculus@2049
   428
    if (!find_device_by_name(this, devname, iscapture)) {
icculus@2049
   429
        SDL_SetError("Couldn't find requested CoreAudio device");
icculus@2049
   430
        return 0;
icculus@2049
   431
    }
icculus@2049
   432
icculus@2049
   433
    SDL_memset(&desc, '\0', sizeof(ComponentDescription));
icculus@2049
   434
    desc.componentType = kAudioUnitType_Output;
icculus@2049
   435
    desc.componentSubType = kAudioUnitSubType_HALOutput;
icculus@2049
   436
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
icculus@2049
   437
icculus@2049
   438
    comp = FindNextComponent(NULL, &desc);
icculus@2049
   439
    if (comp == NULL) {
icculus@2049
   440
        SDL_SetError("Couldn't find requested CoreAudio component");
icculus@2049
   441
        return 0;
icculus@2049
   442
    }
icculus@2049
   443
icculus@2049
   444
    /* Open & initialize the audio unit */
icculus@2049
   445
    result = OpenAComponent(comp, &this->hidden->audioUnit);
icculus@2049
   446
    CHECK_RESULT("OpenAComponent");
icculus@2049
   447
icculus@2055
   448
    this->hidden->audioUnitOpened = 1;
icculus@2055
   449
icculus@2049
   450
    // !!! FIXME: this is wrong?
icculus@2049
   451
    enableIO = ((iscapture) ? 1 : 0);
icculus@2049
   452
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   453
                                  kAudioOutputUnitProperty_EnableIO,
icculus@2049
   454
                                  kAudioUnitScope_Input, input_bus,
slouken@2060
   455
                                  &enableIO, sizeof(enableIO));
icculus@2049
   456
    CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO input)");
icculus@2049
   457
icculus@2049
   458
    // !!! FIXME: this is wrong?
icculus@2049
   459
    enableIO = ((iscapture) ? 0 : 1);
icculus@2049
   460
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   461
                                  kAudioOutputUnitProperty_EnableIO,
icculus@2049
   462
                                  kAudioUnitScope_Output, output_bus,
slouken@2060
   463
                                  &enableIO, sizeof(enableIO));
icculus@2049
   464
    CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO output)");
icculus@2049
   465
icculus@2049
   466
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   467
                                  kAudioOutputUnitProperty_CurrentDevice,
icculus@2049
   468
                                  kAudioUnitScope_Global, 0,
icculus@2049
   469
                                  &this->hidden->deviceID,
slouken@2060
   470
                                  sizeof(AudioDeviceID));
slouken@2060
   471
    CHECK_RESULT
slouken@2060
   472
        ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
icculus@2049
   473
icculus@2049
   474
    /* Set the data format of the audio unit. */
icculus@2049
   475
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   476
                                  kAudioUnitProperty_StreamFormat,
slouken@2060
   477
                                  scope, bus, strdesc, sizeof(*strdesc));
icculus@2049
   478
    CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
icculus@2049
   479
icculus@2049
   480
    /* Set the audio callback */
slouken@2060
   481
    SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
icculus@2049
   482
    callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
icculus@2049
   483
    callback.inputProcRefCon = this;
icculus@2049
   484
    result = AudioUnitSetProperty(this->hidden->audioUnit,
icculus@2049
   485
                                  kAudioUnitProperty_SetRenderCallback,
slouken@2060
   486
                                  scope, bus, &callback, sizeof(callback));
slouken@2060
   487
    CHECK_RESULT
slouken@2060
   488
        ("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
icculus@2049
   489
icculus@2049
   490
    /* Calculate the final parameters for this audio specification */
icculus@2049
   491
    SDL_CalculateAudioSpec(&this->spec);
icculus@2049
   492
icculus@2049
   493
    /* Allocate a sample buffer */
icculus@2049
   494
    this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
icculus@2049
   495
    this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
icculus@2049
   496
icculus@2049
   497
    result = AudioUnitInitialize(this->hidden->audioUnit);
icculus@2049
   498
    CHECK_RESULT("AudioUnitInitialize");
icculus@2049
   499
icculus@2049
   500
    /* Finally, start processing of the audio unit */
icculus@2049
   501
    result = AudioOutputUnitStart(this->hidden->audioUnit);
icculus@2049
   502
    CHECK_RESULT("AudioOutputUnitStart");
icculus@2049
   503
icculus@2049
   504
    /* We're running! */
icculus@2049
   505
    return 1;
icculus@2049
   506
}
icculus@2049
   507
icculus@2049
   508
icculus@2049
   509
static int
icculus@2049
   510
COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
icculus@2049
   511
{
icculus@2016
   512
    AudioStreamBasicDescription strdesc;
icculus@2049
   513
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2003
   514
    int valid_datatype = 0;
slouken@935
   515
icculus@2049
   516
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   517
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   518
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   519
    if (this->hidden == NULL) {
icculus@2049
   520
        SDL_OutOfMemory();
icculus@2049
   521
        return (0);
icculus@2049
   522
    }
icculus@2049
   523
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
icculus@2049
   524
slouken@935
   525
    /* Setup a AudioStreamBasicDescription with the requested format */
icculus@2049
   526
    SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
icculus@2016
   527
    strdesc.mFormatID = kAudioFormatLinearPCM;
icculus@2016
   528
    strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
icculus@2049
   529
    strdesc.mChannelsPerFrame = this->spec.channels;
icculus@2049
   530
    strdesc.mSampleRate = this->spec.freq;
icculus@2016
   531
    strdesc.mFramesPerPacket = 1;
slouken@1895
   532
icculus@2003
   533
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   534
        this->spec.format = test_format;
icculus@2003
   535
        /* Just a list of valid SDL formats, so people don't pass junk here. */
icculus@2003
   536
        switch (test_format) {
slouken@2043
   537
        case AUDIO_U8:
slouken@2043
   538
        case AUDIO_S8:
slouken@2043
   539
        case AUDIO_U16LSB:
slouken@2043
   540
        case AUDIO_S16LSB:
slouken@2043
   541
        case AUDIO_U16MSB:
slouken@2043
   542
        case AUDIO_S16MSB:
slouken@2043
   543
        case AUDIO_S32LSB:
slouken@2043
   544
        case AUDIO_S32MSB:
slouken@2043
   545
        case AUDIO_F32LSB:
slouken@2043
   546
        case AUDIO_F32MSB:
slouken@2043
   547
            valid_datatype = 1;
icculus@2049
   548
            strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
icculus@2049
   549
            if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
slouken@2043
   550
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
slouken@935
   551
icculus@2049
   552
            if (SDL_AUDIO_ISFLOAT(this->spec.format))
slouken@2043
   553
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
icculus@2049
   554
            else if (SDL_AUDIO_ISSIGNED(this->spec.format))
slouken@2043
   555
                strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
slouken@2043
   556
            break;
icculus@2003
   557
        }
icculus@2003
   558
    }
icculus@2003
   559
slouken@2043
   560
    if (!valid_datatype) {      /* shouldn't happen, but just in case... */
icculus@2055
   561
        COREAUDIO_CloseDevice(this);
icculus@2003
   562
        SDL_SetError("Unsupported audio format");
icculus@2049
   563
        return 0;
icculus@2003
   564
    }
icculus@2003
   565
icculus@2016
   566
    strdesc.mBytesPerFrame =
icculus@2016
   567
        strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
icculus@2016
   568
    strdesc.mBytesPerPacket =
icculus@2016
   569
        strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
slouken@935
   570
icculus@2049
   571
    if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
icculus@2057
   572
        COREAUDIO_CloseDevice(this);
slouken@2060
   573
        return 0;               /* prepare_audiounit() will call SDL_SetError()... */
slouken@935
   574
    }
slouken@1895
   575
slouken@2060
   576
    return 1;                   /* good to go. */
slouken@1895
   577
}
slouken@935
   578
icculus@2049
   579
static int
slouken@2060
   580
COREAUDIO_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   581
{
icculus@2049
   582
    /* Set the function pointers */
icculus@2049
   583
    impl->DetectDevices = COREAUDIO_DetectDevices;
icculus@2049
   584
    impl->GetDeviceName = COREAUDIO_GetDeviceName;
icculus@2049
   585
    impl->OpenDevice = COREAUDIO_OpenDevice;
icculus@2049
   586
    impl->CloseDevice = COREAUDIO_CloseDevice;
icculus@2049
   587
    impl->Deinitialize = COREAUDIO_Deinitialize;
icculus@2049
   588
    impl->ProvidesOwnCallbackThread = 1;
icculus@2049
   589
slouken@2060
   590
    build_device_lists();       /* do an initial check for devices... */
icculus@2049
   591
icculus@2049
   592
    return 1;
icculus@2049
   593
}
icculus@2049
   594
icculus@2049
   595
AudioBootStrap COREAUDIO_bootstrap = {
icculus@2049
   596
    "coreaudio", "Mac OS X CoreAudio", COREAUDIO_Init, 0
icculus@2049
   597
};
icculus@2049
   598
slouken@1895
   599
/* vi: set ts=4 sw=4 expandtab: */