Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 <CoreAudio/CoreAudio.h>
     25 #include <CoreServices/CoreServices.h>
     26 #include <AudioUnit/AudioUnit.h>
     27 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050
     28 #include <AudioUnit/AUNTComponent.h>
     29 #endif
     30 
     31 #include "SDL_audio.h"
     32 #include "../SDL_audio_c.h"
     33 #include "../SDL_sysaudio.h"
     34 #include "SDL_coreaudio.h"
     35 
     36 
     37 /* Audio driver functions */
     38 
     39 static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec);
     40 static void Core_WaitAudio(_THIS);
     41 static void Core_PlayAudio(_THIS);
     42 static Uint8 *Core_GetAudioBuf(_THIS);
     43 static void Core_CloseAudio(_THIS);
     44 
     45 /* Audio driver bootstrap functions */
     46 
     47 static int Audio_Available(void)
     48 {
     49     return(1);
     50 }
     51 
     52 static void Audio_DeleteDevice(SDL_AudioDevice *device)
     53 {
     54     SDL_free(device->hidden);
     55     SDL_free(device);
     56 }
     57 
     58 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
     59 {
     60     SDL_AudioDevice *this;
     61 
     62     /* Initialize all variables that we clean on shutdown */
     63     this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
     64     if ( this ) {
     65         SDL_memset(this, 0, (sizeof *this));
     66         this->hidden = (struct SDL_PrivateAudioData *)
     67                 SDL_malloc((sizeof *this->hidden));
     68     }
     69     if ( (this == NULL) || (this->hidden == NULL) ) {
     70         SDL_OutOfMemory();
     71         if ( this ) {
     72             SDL_free(this);
     73         }
     74         return(0);
     75     }
     76     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
     77 
     78     /* Set the function pointers */
     79     this->OpenAudio = Core_OpenAudio;
     80     this->WaitAudio = Core_WaitAudio;
     81     this->PlayAudio = Core_PlayAudio;
     82     this->GetAudioBuf = Core_GetAudioBuf;
     83     this->CloseAudio = Core_CloseAudio;
     84 
     85     this->free = Audio_DeleteDevice;
     86 
     87     return this;
     88 }
     89 
     90 AudioBootStrap COREAUDIO_bootstrap = {
     91     "coreaudio", "Mac OS X CoreAudio",
     92     Audio_Available, Audio_CreateDevice
     93 };
     94 
     95 /* The CoreAudio callback */
     96 static OSStatus     audioCallback (void                            *inRefCon,
     97                                    AudioUnitRenderActionFlags      *ioActionFlags,
     98                                    const AudioTimeStamp            *inTimeStamp,
     99                                    UInt32                          inBusNumber,
    100                                    UInt32                          inNumberFrames,
    101                                    AudioBufferList                 *ioData)
    102 {
    103     SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon;
    104     UInt32 remaining, len;
    105     AudioBuffer *abuf;
    106     void *ptr;
    107     UInt32 i;
    108 
    109     /* Only do anything if audio is enabled and not paused */
    110     if ( ! this->enabled || this->paused ) {
    111         for (i = 0; i < ioData->mNumberBuffers; i++) {
    112             abuf = &ioData->mBuffers[i];
    113             SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
    114         }
    115         return 0;
    116     }
    117 
    118     /* No SDL conversion should be needed here, ever, since we accept
    119        any input format in OpenAudio, and leave the conversion to CoreAudio.
    120      */
    121     /*
    122     assert(!this->convert.needed);
    123     assert(this->spec.channels == ioData->mNumberChannels);
    124      */
    125 
    126     for (i = 0; i < ioData->mNumberBuffers; i++) {
    127         abuf = &ioData->mBuffers[i];
    128         remaining = abuf->mDataByteSize;
    129         ptr = abuf->mData;
    130         while (remaining > 0) {
    131             if (bufferOffset >= bufferSize) {
    132                 /* Generate the data */
    133                 SDL_memset(buffer, this->spec.silence, bufferSize);
    134                 SDL_mutexP(this->mixer_lock);
    135                 (*this->spec.callback)(this->spec.userdata,
    136                             buffer, bufferSize);
    137                 SDL_mutexV(this->mixer_lock);
    138                 bufferOffset = 0;
    139             }
    140 
    141             len = bufferSize - bufferOffset;
    142             if (len > remaining)
    143                 len = remaining;
    144             SDL_memcpy(ptr, (char *)buffer + bufferOffset, len);
    145             ptr = (char *)ptr + len;
    146             remaining -= len;
    147             bufferOffset += len;
    148         }
    149     }
    150 
    151     return 0;
    152 }
    153 
    154 /* Dummy functions -- we don't use thread-based audio */
    155 void Core_WaitAudio(_THIS)
    156 {
    157     return;
    158 }
    159 
    160 void Core_PlayAudio(_THIS)
    161 {
    162     return;
    163 }
    164 
    165 Uint8 *Core_GetAudioBuf(_THIS)
    166 {
    167     return(NULL);
    168 }
    169 
    170 void Core_CloseAudio(_THIS)
    171 {
    172     OSStatus result;
    173     struct AURenderCallbackStruct callback;
    174 
    175     /* stop processing the audio unit */
    176     result = AudioOutputUnitStop (outputAudioUnit);
    177     if (result != noErr) {
    178         SDL_SetError("Core_CloseAudio: AudioOutputUnitStop");
    179         return;
    180     }
    181 
    182     /* Remove the input callback */
    183     callback.inputProc = 0;
    184     callback.inputProcRefCon = 0;
    185     result = AudioUnitSetProperty (outputAudioUnit,
    186                         kAudioUnitProperty_SetRenderCallback,
    187                         kAudioUnitScope_Input,
    188                         0,
    189                         &callback,
    190                         sizeof(callback));
    191     if (result != noErr) {
    192         SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
    193         return;
    194     }
    195 
    196     result = CloseComponent(outputAudioUnit);
    197     if (result != noErr) {
    198         SDL_SetError("Core_CloseAudio: CloseComponent");
    199         return;
    200     }
    201 
    202     SDL_free(buffer);
    203 }
    204 
    205 #define CHECK_RESULT(msg) \
    206     if (result != noErr) { \
    207         SDL_SetError("Failed to start CoreAudio: " msg); \
    208         return -1; \
    209     }
    210 
    211 
    212 int Core_OpenAudio(_THIS, SDL_AudioSpec *spec)
    213 {
    214     OSStatus result = noErr;
    215     Component comp;
    216     ComponentDescription desc;
    217     struct AURenderCallbackStruct callback;
    218     AudioStreamBasicDescription requestedDesc;
    219 
    220     /* Setup a AudioStreamBasicDescription with the requested format */
    221     requestedDesc.mFormatID = kAudioFormatLinearPCM;
    222     requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
    223     requestedDesc.mChannelsPerFrame = spec->channels;
    224     requestedDesc.mSampleRate = spec->freq;
    225 
    226     requestedDesc.mBitsPerChannel = spec->format & 0xFF;
    227     if (spec->format & 0x8000)
    228         requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
    229     if (spec->format & 0x1000)
    230         requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
    231 
    232     requestedDesc.mFramesPerPacket = 1;
    233     requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
    234     requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
    235 
    236 
    237     /* Locate the default output audio unit */
    238     desc.componentType = kAudioUnitType_Output;
    239     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
    240     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    241     desc.componentFlags = 0;
    242     desc.componentFlagsMask = 0;
    243 
    244     comp = FindNextComponent (NULL, &desc);
    245     if (comp == NULL) {
    246         SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL");
    247         return -1;
    248     }
    249 
    250     /* Open & initialize the default output audio unit */
    251     result = OpenAComponent (comp, &outputAudioUnit);
    252     CHECK_RESULT("OpenAComponent")
    253 
    254     result = AudioUnitInitialize (outputAudioUnit);
    255     CHECK_RESULT("AudioUnitInitialize")
    256 
    257     /* Set the input format of the audio unit. */
    258     result = AudioUnitSetProperty (outputAudioUnit,
    259                                kAudioUnitProperty_StreamFormat,
    260                                kAudioUnitScope_Input,
    261                                0,
    262                                &requestedDesc,
    263                                sizeof (requestedDesc));
    264     CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)")
    265 
    266     /* Set the audio callback */
    267     callback.inputProc = audioCallback;
    268     callback.inputProcRefCon = this;
    269     result = AudioUnitSetProperty (outputAudioUnit,
    270                         kAudioUnitProperty_SetRenderCallback,
    271                         kAudioUnitScope_Input,
    272                         0,
    273                         &callback,
    274                         sizeof(callback));
    275     CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)")
    276 
    277     /* Calculate the final parameters for this audio specification */
    278     SDL_CalculateAudioSpec(spec);
    279 
    280     /* Allocate a sample buffer */
    281     bufferOffset = bufferSize = this->spec.size;
    282     buffer = SDL_malloc(bufferSize);
    283 
    284     /* Finally, start processing of the audio unit */
    285     result = AudioOutputUnitStart (outputAudioUnit);
    286     CHECK_RESULT("AudioOutputUnitStart")
    287 
    288 
    289     /* We're running! */
    290     return(1);
    291 }
    292