src/audio/macosx/SDL_coreaudio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1487 dc6b59e925a2
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API. WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid. The code is now run through a consistent indent format: indent -i4 -nut -nsc -br -ce The headers are being converted to automatically generate doxygen documentation.

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#include <AudioUnit/AudioUnit.h>

#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_coreaudio.h"


/* Audio driver functions */

static int Core_OpenAudio (_THIS, SDL_AudioSpec * spec);
static void Core_WaitAudio (_THIS);
static void Core_PlayAudio (_THIS);
static Uint8 *Core_GetAudioBuf (_THIS);
static void Core_CloseAudio (_THIS);

/* Audio driver bootstrap functions */

static int
Audio_Available (void)
{
    return (1);
}

static void
Audio_DeleteDevice (SDL_AudioDevice * device)
{
    SDL_free (device->hidden);
    SDL_free (device);
}

static SDL_AudioDevice *
Audio_CreateDevice (int devindex)
{
    SDL_AudioDevice *this;

    /* Initialize all variables that we clean on shutdown */
    this = (SDL_AudioDevice *) SDL_malloc (sizeof (SDL_AudioDevice));
    if (this) {
        SDL_memset (this, 0, (sizeof *this));
        this->hidden = (struct SDL_PrivateAudioData *)
            SDL_malloc ((sizeof *this->hidden));
    }
    if ((this == NULL) || (this->hidden == NULL)) {
        SDL_OutOfMemory ();
        if (this) {
            SDL_free (this);
        }
        return (0);
    }
    SDL_memset (this->hidden, 0, (sizeof *this->hidden));

    /* Set the function pointers */
    this->OpenAudio = Core_OpenAudio;
    this->WaitAudio = Core_WaitAudio;
    this->PlayAudio = Core_PlayAudio;
    this->GetAudioBuf = Core_GetAudioBuf;
    this->CloseAudio = Core_CloseAudio;

    this->free = Audio_DeleteDevice;

    return this;
}

AudioBootStrap COREAUDIO_bootstrap = {
    "coreaudio", "Mac OS X CoreAudio",
    Audio_Available, Audio_CreateDevice
};

/* The CoreAudio callback */
static OSStatus
audioCallback (void *inRefCon,
               AudioUnitRenderActionFlags inActionFlags,
               const AudioTimeStamp * inTimeStamp,
               UInt32 inBusNumber, AudioBuffer * ioData)
{
    SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
    UInt32 remaining, len;
    void *ptr;

    /* Only do anything if audio is enabled and not paused */
    if (!this->enabled || this->paused) {
        SDL_memset (ioData->mData, this->spec.silence, ioData->mDataByteSize);
        return 0;
    }

    /* No SDL conversion should be needed here, ever, since we accept
       any input format in OpenAudio, and leave the conversion to CoreAudio.
     */
    /*
       assert(!this->convert.needed);
       assert(this->spec.channels == ioData->mNumberChannels);
     */

    remaining = ioData->mDataByteSize;
    ptr = ioData->mData;
    while (remaining > 0) {
        if (bufferOffset >= bufferSize) {
            /* Generate the data */
            SDL_memset (buffer, this->spec.silence, bufferSize);
            SDL_mutexP (this->mixer_lock);
            (*this->spec.callback) (this->spec.userdata, buffer, bufferSize);
            SDL_mutexV (this->mixer_lock);
            bufferOffset = 0;
        }

        len = bufferSize - bufferOffset;
        if (len > remaining)
            len = remaining;
        SDL_memcpy (ptr, (char *) buffer + bufferOffset, len);
        ptr = (char *) ptr + len;
        remaining -= len;
        bufferOffset += len;
    }

    return 0;
}

/* Dummy functions -- we don't use thread-based audio */
void
Core_WaitAudio (_THIS)
{
    return;
}

void
Core_PlayAudio (_THIS)
{
    return;
}

Uint8 *
Core_GetAudioBuf (_THIS)
{
    return (NULL);
}

void
Core_CloseAudio (_THIS)
{
    OSStatus result;
    struct AudioUnitInputCallback callback;

    /* stop processing the audio unit */
    result = AudioOutputUnitStop (outputAudioUnit);
    if (result != noErr) {
        SDL_SetError ("Core_CloseAudio: AudioOutputUnitStop");
        return;
    }

    /* Remove the input callback */
    callback.inputProc = 0;
    callback.inputProcRefCon = 0;
    result = AudioUnitSetProperty (outputAudioUnit,
                                   kAudioUnitProperty_SetInputCallback,
                                   kAudioUnitScope_Input,
                                   0, &callback, sizeof (callback));
    if (result != noErr) {
        SDL_SetError
            ("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
        return;
    }

    result = CloseComponent (outputAudioUnit);
    if (result != noErr) {
        SDL_SetError ("Core_CloseAudio: CloseComponent");
        return;
    }

    SDL_free (buffer);
}

#define CHECK_RESULT(msg) \
    if (result != noErr) { \
        SDL_SetError("Failed to start CoreAudio: " msg); \
        return -1; \
    }


int
Core_OpenAudio (_THIS, SDL_AudioSpec * spec)
{
    OSStatus result = noErr;
    Component comp;
    ComponentDescription desc;
    struct AudioUnitInputCallback callback;
    AudioStreamBasicDescription requestedDesc;

    /* Setup a AudioStreamBasicDescription with the requested format */
    requestedDesc.mFormatID = kAudioFormatLinearPCM;
    requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
    requestedDesc.mChannelsPerFrame = spec->channels;
    requestedDesc.mSampleRate = spec->freq;

    requestedDesc.mBitsPerChannel = spec->format & 0xFF;
    if (spec->format & 0x8000)
        requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
    if (spec->format & 0x1000)
        requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;

    requestedDesc.mFramesPerPacket = 1;
    requestedDesc.mBytesPerFrame =
        requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
    requestedDesc.mBytesPerPacket =
        requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;


    /* Locate the default output audio unit */
    desc.componentType = kAudioUnitComponentType;
    desc.componentSubType = kAudioUnitSubType_Output;
    desc.componentManufacturer = kAudioUnitID_DefaultOutput;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    comp = FindNextComponent (NULL, &desc);
    if (comp == NULL) {
        SDL_SetError
            ("Failed to start CoreAudio: FindNextComponent returned NULL");
        return -1;
    }

    /* Open & initialize the default output audio unit */
    result = OpenAComponent (comp, &outputAudioUnit);
    CHECK_RESULT ("OpenAComponent")
        result = AudioUnitInitialize (outputAudioUnit);
    CHECK_RESULT ("AudioUnitInitialize")
        /* Set the input format of the audio unit. */
        result = AudioUnitSetProperty (outputAudioUnit,
                                       kAudioUnitProperty_StreamFormat,
                                       kAudioUnitScope_Input,
                                       0,
                                       &requestedDesc,
                                       sizeof (requestedDesc));
    CHECK_RESULT ("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)")
        /* Set the audio callback */
        callback.inputProc = audioCallback;
    callback.inputProcRefCon = this;
    result = AudioUnitSetProperty (outputAudioUnit,
                                   kAudioUnitProperty_SetInputCallback,
                                   kAudioUnitScope_Input,
                                   0, &callback, sizeof (callback));
    CHECK_RESULT
        ("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)")
        /* Calculate the final parameters for this audio specification */
        SDL_CalculateAudioSpec (spec);

    /* Allocate a sample buffer */
    bufferOffset = bufferSize = this->spec.size;
    buffer = SDL_malloc (bufferSize);

    /* Finally, start processing of the audio unit */
    result = AudioOutputUnitStart (outputAudioUnit);
    CHECK_RESULT ("AudioOutputUnitStart")
        /* We're running! */
        return (1);
}

/* vi: set ts=4 sw=4 expandtab: */