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