Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 #include <AudioUnit/AudioUnit.h>
     25 
     26 #include "SDL_audio.h"
     27 #include "../SDL_audio_c.h"
     28 #include "../SDL_sysaudio.h"
     29 #include "SDL_coreaudio.h"
     30 
     31 
     32 /* Audio driver functions */
     33 
     34 static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec);
     35 static void Core_WaitAudio(_THIS);
     36 static void Core_PlayAudio(_THIS);
     37 static Uint8 *Core_GetAudioBuf(_THIS);
     38 static void Core_CloseAudio(_THIS);
     39 
     40 /* Audio driver bootstrap functions */
     41 
     42 static int Audio_Available(void)
     43 {
     44     return(1);
     45 }
     46 
     47 static void Audio_DeleteDevice(SDL_AudioDevice *device)
     48 {
     49     SDL_free(device->hidden);
     50     SDL_free(device);
     51 }
     52 
     53 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
     54 {
     55     SDL_AudioDevice *this;
     56 
     57     /* Initialize all variables that we clean on shutdown */
     58     this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
     59     if ( this ) {
     60         SDL_memset(this, 0, (sizeof *this));
     61         this->hidden = (struct SDL_PrivateAudioData *)
     62                 SDL_malloc((sizeof *this->hidden));
     63     }
     64     if ( (this == NULL) || (this->hidden == NULL) ) {
     65         SDL_OutOfMemory();
     66         if ( this ) {
     67             SDL_free(this);
     68         }
     69         return(0);
     70     }
     71     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
     72 
     73     /* Set the function pointers */
     74     this->OpenAudio = Core_OpenAudio;
     75     this->WaitAudio = Core_WaitAudio;
     76     this->PlayAudio = Core_PlayAudio;
     77     this->GetAudioBuf = Core_GetAudioBuf;
     78     this->CloseAudio = Core_CloseAudio;
     79 
     80     this->free = Audio_DeleteDevice;
     81 
     82     return this;
     83 }
     84 
     85 AudioBootStrap COREAUDIO_bootstrap = {
     86     "coreaudio", "Mac OS X CoreAudio",
     87     Audio_Available, Audio_CreateDevice
     88 };
     89 
     90 /* The CoreAudio callback */
     91 static OSStatus     audioCallback (void                             *inRefCon,
     92                                     AudioUnitRenderActionFlags      inActionFlags,
     93                                     const AudioTimeStamp            *inTimeStamp,
     94                                     UInt32                          inBusNumber,
     95                                     AudioBuffer                     *ioData)
     96 {
     97     SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon;
     98     UInt32 remaining, len;
     99     void *ptr;
    100 
    101     /* Only do anything if audio is enabled and not paused */
    102     if ( ! this->enabled || this->paused ) {
    103         SDL_memset(ioData->mData, this->spec.silence, ioData->mDataByteSize);
    104         return 0;
    105     }
    106 
    107     /* No SDL conversion should be needed here, ever, since we accept
    108        any input format in OpenAudio, and leave the conversion to CoreAudio.
    109      */
    110     /*
    111     assert(!this->convert.needed);
    112     assert(this->spec.channels == ioData->mNumberChannels);
    113      */
    114 
    115     remaining = ioData->mDataByteSize;
    116     ptr = ioData->mData;
    117     while (remaining > 0) {
    118         if (bufferOffset >= bufferSize) {
    119             /* Generate the data */
    120             SDL_memset(buffer, this->spec.silence, bufferSize);
    121             SDL_mutexP(this->mixer_lock);
    122             (*this->spec.callback)(this->spec.userdata,
    123                         buffer, bufferSize);
    124             SDL_mutexV(this->mixer_lock);
    125             bufferOffset = 0;
    126         }
    127 
    128         len = bufferSize - bufferOffset;
    129         if (len > remaining)
    130             len = remaining;
    131         SDL_memcpy(ptr, (char *)buffer + bufferOffset, len);
    132         ptr = (char *)ptr + len;
    133         remaining -= len;
    134         bufferOffset += len;
    135     }
    136 
    137     return 0;
    138 }
    139 
    140 /* Dummy functions -- we don't use thread-based audio */
    141 void Core_WaitAudio(_THIS)
    142 {
    143     return;
    144 }
    145 
    146 void Core_PlayAudio(_THIS)
    147 {
    148     return;
    149 }
    150 
    151 Uint8 *Core_GetAudioBuf(_THIS)
    152 {
    153     return(NULL);
    154 }
    155 
    156 void Core_CloseAudio(_THIS)
    157 {
    158     OSStatus result;
    159     struct AudioUnitInputCallback callback;
    160 
    161     /* stop processing the audio unit */
    162     result = AudioOutputUnitStop (outputAudioUnit);
    163     if (result != noErr) {
    164         SDL_SetError("Core_CloseAudio: AudioOutputUnitStop");
    165         return;
    166     }
    167 
    168     /* Remove the input callback */
    169     callback.inputProc = 0;
    170     callback.inputProcRefCon = 0;
    171     result = AudioUnitSetProperty (outputAudioUnit,
    172                         kAudioUnitProperty_SetInputCallback,
    173                         kAudioUnitScope_Input,
    174                         0,
    175                         &callback,
    176                         sizeof(callback));
    177     if (result != noErr) {
    178         SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
    179         return;
    180     }
    181 
    182     result = CloseComponent(outputAudioUnit);
    183     if (result != noErr) {
    184         SDL_SetError("Core_CloseAudio: CloseComponent");
    185         return;
    186     }
    187 
    188     SDL_free(buffer);
    189 }
    190 
    191 #define CHECK_RESULT(msg) \
    192     if (result != noErr) { \
    193         SDL_SetError("Failed to start CoreAudio: " msg); \
    194         return -1; \
    195     }
    196 
    197 
    198 int Core_OpenAudio(_THIS, SDL_AudioSpec *spec)
    199 {
    200     OSStatus result = noErr;
    201     Component comp;
    202     ComponentDescription desc;
    203     struct AudioUnitInputCallback callback;
    204     AudioStreamBasicDescription requestedDesc;
    205 
    206     /* Setup a AudioStreamBasicDescription with the requested format */
    207     requestedDesc.mFormatID = kAudioFormatLinearPCM;
    208     requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
    209     requestedDesc.mChannelsPerFrame = spec->channels;
    210     requestedDesc.mSampleRate = spec->freq;
    211 
    212     requestedDesc.mBitsPerChannel = spec->format & 0xFF;
    213     if (spec->format & 0x8000)
    214         requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
    215     if (spec->format & 0x1000)
    216         requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
    217 
    218     requestedDesc.mFramesPerPacket = 1;
    219     requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
    220     requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
    221 
    222 
    223     /* Locate the default output audio unit */
    224     desc.componentType = kAudioUnitComponentType;
    225     desc.componentSubType = kAudioUnitSubType_Output;
    226     desc.componentManufacturer = kAudioUnitID_DefaultOutput;
    227     desc.componentFlags = 0;
    228     desc.componentFlagsMask = 0;
    229 
    230     comp = FindNextComponent (NULL, &desc);
    231     if (comp == NULL) {
    232         SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL");
    233         return -1;
    234     }
    235 
    236     /* Open & initialize the default output audio unit */
    237     result = OpenAComponent (comp, &outputAudioUnit);
    238     CHECK_RESULT("OpenAComponent")
    239 
    240     result = AudioUnitInitialize (outputAudioUnit);
    241     CHECK_RESULT("AudioUnitInitialize")
    242 
    243     /* Set the input format of the audio unit. */
    244     result = AudioUnitSetProperty (outputAudioUnit,
    245                                kAudioUnitProperty_StreamFormat,
    246                                kAudioUnitScope_Input,
    247                                0,
    248                                &requestedDesc,
    249                                sizeof (requestedDesc));
    250     CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)")
    251 
    252     /* Set the audio callback */
    253     callback.inputProc = audioCallback;
    254     callback.inputProcRefCon = this;
    255     result = AudioUnitSetProperty (outputAudioUnit,
    256                         kAudioUnitProperty_SetInputCallback,
    257                         kAudioUnitScope_Input,
    258                         0,
    259                         &callback,
    260                         sizeof(callback));
    261     CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)")
    262 
    263     /* Calculate the final parameters for this audio specification */
    264     SDL_CalculateAudioSpec(spec);
    265 
    266     /* Allocate a sample buffer */
    267     bufferOffset = bufferSize = this->spec.size;
    268     buffer = SDL_malloc(bufferSize);
    269 
    270     /* Finally, start processing of the audio unit */
    271     result = AudioOutputUnitStart (outputAudioUnit);
    272     CHECK_RESULT("AudioOutputUnitStart")
    273 
    274 
    275     /* We're running! */
    276     return(1);
    277 }
    278