Mercurial > sdl-ios-xcode
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: */ |