• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
Audio_Available(void)42 static int Audio_Available(void)
43 {
44     return(1);
45 }
46 
Audio_DeleteDevice(SDL_AudioDevice * device)47 static void Audio_DeleteDevice(SDL_AudioDevice *device)
48 {
49     SDL_free(device->hidden);
50     SDL_free(device);
51 }
52 
Audio_CreateDevice(int devindex)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 */
audioCallback(void * inRefCon,AudioUnitRenderActionFlags inActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,AudioBuffer * ioData)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 */
Core_WaitAudio(_THIS)141 void Core_WaitAudio(_THIS)
142 {
143     return;
144 }
145 
Core_PlayAudio(_THIS)146 void Core_PlayAudio(_THIS)
147 {
148     return;
149 }
150 
Core_GetAudioBuf(_THIS)151 Uint8 *Core_GetAudioBuf(_THIS)
152 {
153     return(NULL);
154 }
155 
Core_CloseAudio(_THIS)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 
Core_OpenAudio(_THIS,SDL_AudioSpec * spec)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