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