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