comparison src/audio/iphoneos/SDL_coreaudio_iphone.c @ 2364:e321b52dee8f gsoc2008_iphone

These files contain the audio support for iPhone. They are based on the CoreAudio audio driver for Mac OS X. The principle difference is that the iPhone doesn't seem to have a concept of audio devices ... it just has special units for audio in and audio out. Also had to change some functions to versions which seem to only exist on iPhone and will apparently exist in Mac OS X 10.6(!) There is currently no audio recording support -- my iPod Touch doesn't have a microphone to test this with.
author Holmes Futrell <hfutrell@umail.ucsb.edu>
date Fri, 18 Jul 2008 17:53:54 +0000
parents
children
comparison
equal deleted inserted replaced
2363:49b243db2e04 2364:e321b52dee8f
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_iphone.h"
30
31 #define DEBUG_COREAUDIO 0
32
33 static void
34 COREAUDIO_Deinitialize(void)
35 {
36 }
37
38 /* The CoreAudio callback */
39 static OSStatus
40 outputCallback(void *inRefCon,
41 AudioUnitRenderActionFlags * ioActionFlags,
42 const AudioTimeStamp * inTimeStamp,
43 UInt32 inBusNumber, UInt32 inNumberFrames,
44 AudioBufferList * ioDataList)
45 {
46 SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
47 AudioBuffer *ioData = &ioDataList->mBuffers[0];
48 UInt32 remaining, len;
49 void *ptr;
50
51 /* Is there ever more than one buffer, and what do you do with it? */
52 if (ioDataList->mNumberBuffers != 1) {
53 return noErr;
54 }
55
56 /* Only do anything if audio is enabled and not paused */
57 if (!this->enabled || this->paused) {
58 SDL_memset(ioData->mData, this->spec.silence, ioData->mDataByteSize);
59 return 0;
60 }
61
62 /* No SDL conversion should be needed here, ever, since we accept
63 any input format in OpenAudio, and leave the conversion to CoreAudio.
64 */
65 /*
66 assert(!this->convert.needed);
67 assert(this->spec.channels == ioData->mNumberChannels);
68 */
69
70 remaining = ioData->mDataByteSize;
71 ptr = ioData->mData;
72 while (remaining > 0) {
73 if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
74 /* Generate the data */
75 SDL_memset(this->hidden->buffer, this->spec.silence,
76 this->hidden->bufferSize);
77 SDL_mutexP(this->mixer_lock);
78 (*this->spec.callback) (this->spec.userdata, this->hidden->buffer,
79 this->hidden->bufferSize);
80 SDL_mutexV(this->mixer_lock);
81 this->hidden->bufferOffset = 0;
82 }
83
84 len = this->hidden->bufferSize - this->hidden->bufferOffset;
85 if (len > remaining)
86 len = remaining;
87 SDL_memcpy(ptr,
88 (char *) this->hidden->buffer + this->hidden->bufferOffset,
89 len);
90 ptr = (char *) ptr + len;
91 remaining -= len;
92 this->hidden->bufferOffset += len;
93 }
94
95 return 0;
96 }
97
98 static OSStatus
99 inputCallback(void *inRefCon,
100 AudioUnitRenderActionFlags * ioActionFlags,
101 const AudioTimeStamp * inTimeStamp,
102 UInt32 inBusNumber, UInt32 inNumberFrames,
103 AudioBufferList * ioData)
104 {
105 //err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer);
106 // !!! FIXME: write me!
107 return noErr;
108 }
109
110
111 static void
112 COREAUDIO_CloseDevice(_THIS)
113 {
114 if (this->hidden != NULL) {
115 if (this->hidden->audioUnitOpened) {
116 OSStatus result = noErr;
117 AURenderCallbackStruct callback;
118 const AudioUnitElement output_bus = 0;
119 const AudioUnitElement input_bus = 1;
120 const int iscapture = this->iscapture;
121 const AudioUnitElement bus =
122 ((iscapture) ? input_bus : output_bus);
123 const AudioUnitScope scope =
124 ((iscapture) ? kAudioUnitScope_Output :
125 kAudioUnitScope_Input);
126
127 /* stop processing the audio unit */
128 result = AudioOutputUnitStop(this->hidden->audioUnit);
129
130 /* Remove the input callback */
131 SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
132 result = AudioUnitSetProperty(this->hidden->audioUnit,
133 kAudioUnitProperty_SetRenderCallback,
134 scope, bus, &callback,
135 sizeof(callback));
136
137 //CloseComponent(this->hidden->audioUnit);
138 this->hidden->audioUnitOpened = 0;
139 }
140 SDL_free(this->hidden->buffer);
141 SDL_free(this->hidden);
142 this->hidden = NULL;
143 }
144 }
145
146
147 #define CHECK_RESULT(msg) \
148 if (result != noErr) { \
149 COREAUDIO_CloseDevice(this); \
150 SDL_SetError("CoreAudio error (%s): %d", msg, result); \
151 return 0; \
152 }
153
154 static int
155 prepare_audiounit(_THIS, const char *devname, int iscapture,
156 const AudioStreamBasicDescription * strdesc)
157 {
158 OSStatus result = noErr;
159 AURenderCallbackStruct callback;
160 AudioComponentDescription desc;
161 AudioComponent comp = NULL;
162
163 UInt32 enableIO = 0;
164 const AudioUnitElement output_bus = 0;
165 const AudioUnitElement input_bus = 1;
166 const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus);
167 const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output :
168 kAudioUnitScope_Input);
169
170 SDL_memset(&desc, '\0', sizeof(AudioComponentDescription));
171 desc.componentType = kAudioUnitType_Output;
172 desc.componentSubType = kAudioUnitSubType_RemoteIO;
173 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
174
175 comp = AudioComponentFindNext(NULL, &desc);
176 if (comp == NULL) {
177 SDL_SetError("Couldn't find requested CoreAudio component");
178 return 0;
179 }
180
181 /* Open & initialize the audio unit */
182 /*
183 AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6
184 We can't use OpenAComponent on iPhone because it is not present
185 */
186 result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit);
187 CHECK_RESULT("AudioComponentInstanceNew");
188
189 this->hidden->audioUnitOpened = 1;
190
191 // !!! FIXME: this is wrong?
192 enableIO = ((iscapture) ? 1 : 0);
193 result = AudioUnitSetProperty(this->hidden->audioUnit,
194 kAudioOutputUnitProperty_EnableIO,
195 kAudioUnitScope_Input, input_bus,
196 &enableIO, sizeof(enableIO));
197 CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO input)");
198
199 // !!! FIXME: this is wrong?
200 enableIO = ((iscapture) ? 0 : 1);
201 result = AudioUnitSetProperty(this->hidden->audioUnit,
202 kAudioOutputUnitProperty_EnableIO,
203 kAudioUnitScope_Output, output_bus,
204 &enableIO, sizeof(enableIO));
205 CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO output)");
206
207 /*result = AudioUnitSetProperty(this->hidden->audioUnit,
208 kAudioOutputUnitProperty_CurrentDevice,
209 kAudioUnitScope_Global, 0,
210 &this->hidden->deviceID,
211 sizeof(AudioDeviceID));
212
213 CHECK_RESULT("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");*/
214
215 /* Set the data format of the audio unit. */
216 result = AudioUnitSetProperty(this->hidden->audioUnit,
217 kAudioUnitProperty_StreamFormat,
218 scope, bus, strdesc, sizeof(*strdesc));
219 CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)");
220
221 /* Set the audio callback */
222 SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct));
223 callback.inputProc = ((iscapture) ? inputCallback : outputCallback);
224 callback.inputProcRefCon = this;
225 result = AudioUnitSetProperty(this->hidden->audioUnit,
226 kAudioUnitProperty_SetRenderCallback,
227 scope, bus, &callback, sizeof(callback));
228 CHECK_RESULT
229 ("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
230
231 /* Calculate the final parameters for this audio specification */
232 SDL_CalculateAudioSpec(&this->spec);
233
234 /* Allocate a sample buffer */
235 this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size;
236 this->hidden->buffer = SDL_malloc(this->hidden->bufferSize);
237
238 result = AudioUnitInitialize(this->hidden->audioUnit);
239 CHECK_RESULT("AudioUnitInitialize");
240
241 /* Finally, start processing of the audio unit */
242 result = AudioOutputUnitStart(this->hidden->audioUnit);
243 CHECK_RESULT("AudioOutputUnitStart");
244 /* We're running! */
245 return 1;
246 }
247
248 static int
249 COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
250 {
251 AudioStreamBasicDescription strdesc;
252 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
253 int valid_datatype = 0;
254
255 /* Initialize all variables that we clean on shutdown */
256 this->hidden = (struct SDL_PrivateAudioData *)
257 SDL_malloc((sizeof *this->hidden));
258 if (this->hidden == NULL) {
259 SDL_OutOfMemory();
260 return (0);
261 }
262 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
263
264 /* Setup a AudioStreamBasicDescription with the requested format */
265 SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
266 strdesc.mFormatID = kAudioFormatLinearPCM;
267 strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
268 strdesc.mChannelsPerFrame = this->spec.channels;
269 strdesc.mSampleRate = this->spec.freq;
270 strdesc.mFramesPerPacket = 1;
271
272 while ((!valid_datatype) && (test_format)) {
273 this->spec.format = test_format;
274 /* Just a list of valid SDL formats, so people don't pass junk here. */
275 switch (test_format) {
276 case AUDIO_U8:
277 case AUDIO_S8:
278 case AUDIO_U16LSB:
279 case AUDIO_S16LSB:
280 case AUDIO_U16MSB:
281 case AUDIO_S16MSB:
282 case AUDIO_S32LSB:
283 case AUDIO_S32MSB:
284 case AUDIO_F32LSB:
285 case AUDIO_F32MSB:
286 valid_datatype = 1;
287 strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format);
288 if (SDL_AUDIO_ISBIGENDIAN(this->spec.format))
289 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
290
291 if (SDL_AUDIO_ISFLOAT(this->spec.format))
292 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
293 else if (SDL_AUDIO_ISSIGNED(this->spec.format))
294 strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
295 break;
296 }
297 }
298
299 if (!valid_datatype) { /* shouldn't happen, but just in case... */
300 COREAUDIO_CloseDevice(this);
301 SDL_SetError("Unsupported audio format");
302 return 0;
303 }
304
305 strdesc.mBytesPerFrame =
306 strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
307 strdesc.mBytesPerPacket =
308 strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
309
310 if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
311 COREAUDIO_CloseDevice(this);
312 return 0; /* prepare_audiounit() will call SDL_SetError()... */
313 }
314
315 return 1; /* good to go. */
316 }
317
318 static int
319 COREAUDIO_Init(SDL_AudioDriverImpl * impl)
320 {
321 /* Set the function pointers */
322 impl->OpenDevice = COREAUDIO_OpenDevice;
323 impl->CloseDevice = COREAUDIO_CloseDevice;
324 impl->Deinitialize = COREAUDIO_Deinitialize;
325 impl->ProvidesOwnCallbackThread = 1;
326
327 /* added for iPhone */
328 impl->OnlyHasDefaultInputDevice = 1;
329 impl->OnlyHasDefaultOutputDevice = 1;
330 impl->HasCaptureSupport = 0; /* still needs to be written */
331
332 return 1;
333 }
334
335 AudioBootStrap COREAUDIOIPHONE_bootstrap = {
336 "coreaudio-iphoneos", "SDL CoreAudio (iPhone OS) audio driver", COREAUDIO_Init, 0
337 };
338
339 /* vi: set ts=4 sw=4 expandtab: */