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: */