Mercurial > sdl-ios-xcode
comparison src/audio/iphoneos/SDL_coreaudio_iphone.c @ 2765:f55c87ae336b
Final merge of Google Summer of Code 2008 work...
Bring SDL to iPhone and iPod Touch
by Holmes Futrell, mentored by Sam Lantinga
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 04 Oct 2008 06:46:59 +0000 |
parents | |
children | 99210400e8b9 |
comparison
equal
deleted
inserted
replaced
2764:4868c0df2e83 | 2765:f55c87ae336b |
---|---|
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", | |
337 COREAUDIO_Init, 0 | |
338 }; | |
339 | |
340 /* vi: set ts=4 sw=4 expandtab: */ |