diff src/audio/macosx/SDL_coreaudio.c @ 935:f8d5ddc7aef1

Audio improvements from Max Horn, including a new CoreAudio driver for MacOSX
author Sam Lantinga <slouken@libsdl.org>
date Sat, 21 Aug 2004 02:06:30 +0000
parents
children 84f930aebaeb
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audio/macosx/SDL_coreaudio.c	Sat Aug 21 02:06:30 2004 +0000
@@ -0,0 +1,287 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997-2004 Sam Lantinga
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifdef SAVE_RCSID
+static char rcsid =
+ "@(#) $Id$";
+#endif
+
+#include <AudioUnit/AudioUnit.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "SDL_endian.h"
+#include "SDL_audio.h"
+#include "SDL_audio_c.h"
+#include "SDL_audiomem.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)
+{
+    free(device->hidden);
+    free(device);
+}
+
+static SDL_AudioDevice *Audio_CreateDevice(int devindex)
+{
+    SDL_AudioDevice *this;
+
+    /* Initialize all variables that we clean on shutdown */
+    this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
+    if ( this ) {
+        memset(this, 0, (sizeof *this));
+        this->hidden = (struct SDL_PrivateAudioData *)
+                malloc((sizeof *this->hidden));
+    }
+    if ( (this == NULL) || (this->hidden == NULL) ) {
+        SDL_OutOfMemory();
+        if ( this ) {
+            free(this);
+        }
+        return(0);
+    }
+    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 ) {
+        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 */
+            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;
+        memcpy(ptr, buffer + bufferOffset, len);
+        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;
+    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;
+    }
+    
+    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;
+    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 = malloc(bufferSize);
+    assert(buffer);
+    
+    /* Finally, start processing of the audio unit */
+    result = AudioOutputUnitStart (outputAudioUnit);
+    CHECK_RESULT("AudioOutputUnitStart")    
+    
+
+    /* We're running! */
+    return(1);
+}