Mercurial > SDL_sound_CoreAudio
comparison decoders/coreaudio.c @ 576:8d62447b75f2
Added new Core Audio backend.
author | Eric Wing <ewing . public |-at-| gmail . com> |
---|---|
date | Sun, 10 Oct 2010 21:30:17 -0700 |
parents | |
children | af33b35a55c1 |
comparison
equal
deleted
inserted
replaced
573:1911fc597c69 | 576:8d62447b75f2 |
---|---|
1 /* | |
2 * SDL_sound Core Audio backend | |
3 * Copyright (C) 2010 Eric Wing | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
20 #if HAVE_CONFIG_H | |
21 # include <config.h> | |
22 #endif | |
23 | |
24 #ifdef SOUND_SUPPORTS_COREAUDIO | |
25 | |
26 #include <stddef.h> /* NULL */ | |
27 #include <stdio.h> /* printf */ | |
28 #include <arpa/inet.h> /* htonl */ | |
29 #include <AudioToolbox/AudioToolbox.h> | |
30 | |
31 #include "SDL_sound.h" | |
32 | |
33 #define __SDL_SOUND_INTERNAL__ | |
34 #include "SDL_sound_internal.h" | |
35 | |
36 typedef struct CoreAudioFileContainer | |
37 { | |
38 AudioFileID* audioFileID; | |
39 ExtAudioFileRef extAudioFileRef; | |
40 AudioStreamBasicDescription* outputFormat; | |
41 } CoreAudioFileContainer; | |
42 | |
43 //http://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html | |
44 | |
45 static int CoreAudio_init(void); | |
46 static void CoreAudio_quit(void); | |
47 static int CoreAudio_open(Sound_Sample *sample, const char *ext); | |
48 static void CoreAudio_close(Sound_Sample *sample); | |
49 static Uint32 CoreAudio_read(Sound_Sample *sample); | |
50 static int CoreAudio_rewind(Sound_Sample *sample); | |
51 static int CoreAudio_seek(Sound_Sample *sample, Uint32 ms); | |
52 | |
53 static const char *extensions_coreaudio[] = | |
54 { | |
55 "aif", | |
56 "aiff", | |
57 "aifc", | |
58 "wav", | |
59 "wave", | |
60 "mp3", | |
61 "mp4", | |
62 "m4a", | |
63 "aac", | |
64 "caf", | |
65 "Sd2f", | |
66 "au", | |
67 "next", | |
68 "mp2", | |
69 "mp1", | |
70 "ac3", | |
71 "3gpp", | |
72 "3gp2", | |
73 "amrf", | |
74 NULL | |
75 }; | |
76 const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio = | |
77 { | |
78 { | |
79 extensions_coreaudio, | |
80 "Decode audio through Core Audio through", | |
81 "Eric Wing <ewing.public@playcontrol.net>", | |
82 "http://playcontrol.net" | |
83 }, | |
84 | |
85 CoreAudio_init, /* init() method */ | |
86 CoreAudio_quit, /* quit() method */ | |
87 CoreAudio_open, /* open() method */ | |
88 CoreAudio_close, /* close() method */ | |
89 CoreAudio_read, /* read() method */ | |
90 CoreAudio_rewind, /* rewind() method */ | |
91 CoreAudio_seek /* seek() method */ | |
92 }; | |
93 | |
94 | |
95 static int CoreAudio_init(void) | |
96 { | |
97 return(1); /* always succeeds. */ | |
98 } /* CoreAudio_init */ | |
99 | |
100 | |
101 static void CoreAudio_quit(void) | |
102 { | |
103 /* it's a no-op. */ | |
104 } /* CoreAudio_quit */ | |
105 | |
106 | |
107 static AudioFileTypeID CoreAudio_GetAudioTypeForExtension(const char* file_extension) | |
108 { | |
109 if( (__Sound_strcasecmp(file_extension, "aif") == 0) | |
110 || (__Sound_strcasecmp(file_extension, "aiff") == 0) | |
111 || (__Sound_strcasecmp(file_extension, "aifc") == 0) | |
112 ) | |
113 { | |
114 return kAudioFileAIFCType; | |
115 } | |
116 else if( (__Sound_strcasecmp(file_extension, "wav") == 0) | |
117 || (__Sound_strcasecmp(file_extension, "wave") == 0) | |
118 ) | |
119 { | |
120 return kAudioFileWAVEType; | |
121 } | |
122 else if( (__Sound_strcasecmp(file_extension, "mp3") == 0) | |
123 ) | |
124 { | |
125 return kAudioFileMP3Type; | |
126 } | |
127 else if( (__Sound_strcasecmp(file_extension, "mp4") == 0) | |
128 ) | |
129 { | |
130 return kAudioFileMPEG4Type; | |
131 } | |
132 else if( (__Sound_strcasecmp(file_extension, "m4a") == 0) | |
133 ) | |
134 { | |
135 return kAudioFileM4AType; | |
136 } | |
137 else if( (__Sound_strcasecmp(file_extension, "aac") == 0) | |
138 ) | |
139 { | |
140 return kAudioFileAAC_ADTSType; | |
141 } | |
142 else if( (__Sound_strcasecmp(file_extension, "aac") == 0) | |
143 ) | |
144 { | |
145 return kAudioFileAAC_ADTSType; | |
146 } | |
147 else if( (__Sound_strcasecmp(file_extension, "caf") == 0) | |
148 || (__Sound_strcasecmp(file_extension, "caff") == 0) | |
149 ) | |
150 { | |
151 return kAudioFileCAFType; | |
152 } | |
153 else if( (__Sound_strcasecmp(file_extension, "Sd2f") == 0) | |
154 ) | |
155 { | |
156 return kAudioFileSoundDesigner2Type; | |
157 } | |
158 else if( (__Sound_strcasecmp(file_extension, "au") == 0) | |
159 || (__Sound_strcasecmp(file_extension, "next") == 0) | |
160 ) | |
161 { | |
162 return kAudioFileNextType; | |
163 } | |
164 else if( (__Sound_strcasecmp(file_extension, "mp2") == 0) | |
165 ) | |
166 { | |
167 return kAudioFileMP2Type; | |
168 } | |
169 else if( (__Sound_strcasecmp(file_extension, "mp1") == 0) | |
170 ) | |
171 { | |
172 return kAudioFileMP1Type; | |
173 } | |
174 else if( (__Sound_strcasecmp(file_extension, "ac3") == 0) | |
175 ) | |
176 { | |
177 return kAudioFileAC3Type; | |
178 } | |
179 else if( (__Sound_strcasecmp(file_extension, "3gpp") == 0) | |
180 ) | |
181 { | |
182 return kAudioFile3GPType; | |
183 } | |
184 else if( (__Sound_strcasecmp(file_extension, "3gp2") == 0) | |
185 ) | |
186 { | |
187 return kAudioFile3GP2Type; | |
188 } | |
189 else if( (__Sound_strcasecmp(file_extension, "amrf") == 0) | |
190 ) | |
191 { | |
192 return kAudioFileAMRType; | |
193 } | |
194 else | |
195 { | |
196 return 0; | |
197 } | |
198 /* | |
199 kAudioFilhttp://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.htmleAIFFType = 'AIFF', | |
200 kAudioFileAIFCType = 'AIFC', | |
201 kAudioFileWAVEType = 'WAVE', | |
202 kAudioFileSoundDesigner2Type = 'Sd2f', | |
203 kAudioFileNextType = 'NeXT', | |
204 kAudioFileMP3Type = 'MPG3', | |
205 kAudioFileMP2Type = 'MPG2', | |
206 kAudioFileMP1Type = 'MPG1', | |
207 kAudioFileAC3Type = 'ac-3', | |
208 kAudioFileAAC_ADTSType = 'adts', | |
209 kAudioFileMPEG4Type = 'mp4f', | |
210 kAudioFileM4AType = 'm4af', | |
211 kAudioFileCAFType = 'caff', | |
212 kAudioFile3GPType = '3gpp', | |
213 kAudioFile3GP2Type = '3gp2', | |
214 kAudioFileAMRType = 'amrf' | |
215 | |
216 */ | |
217 } | |
218 | |
219 static const char* CoreAudio_FourCCToString(int32_t error_code) | |
220 { | |
221 static char return_string[16]; | |
222 uint32_t big_endian_code = htonl(error_code); | |
223 char* big_endian_str = (char*)&big_endian_code; | |
224 // see if it appears to be a 4-char-code | |
225 if(isprint(big_endian_str[0]) | |
226 && isprint(big_endian_str[1]) | |
227 && isprint(big_endian_str[2]) | |
228 && isprint (big_endian_str[3])) | |
229 { | |
230 return_string[0] = '\''; | |
231 return_string[1] = big_endian_str[0]; | |
232 return_string[2] = big_endian_str[1]; | |
233 return_string[3] = big_endian_str[2]; | |
234 return_string[4] = big_endian_str[3]; | |
235 return_string[5] = '\''; | |
236 return_string[6] = '\0'; | |
237 } | |
238 else if(error_code > -200000 && error_code < 200000) | |
239 { | |
240 // no, format it as an integer | |
241 snprintf(return_string, 16, "%d", error_code); | |
242 } | |
243 else | |
244 { | |
245 // no, format it as an integer but in hex | |
246 snprintf(return_string, 16, "0x%x", error_code); | |
247 } | |
248 return return_string; | |
249 } | |
250 | |
251 | |
252 | |
253 SInt64 CoreAudio_SizeCallback(void* inClientData) | |
254 { | |
255 SDL_RWops* rw_ops = (SDL_RWops*)inClientData; | |
256 SInt64 current_position = SDL_RWtell(rw_ops); | |
257 SInt64 end_position = SDL_RWseek(rw_ops, 0, SEEK_END); | |
258 SDL_RWseek(rw_ops, current_position, SEEK_SET); | |
259 // fprintf(stderr, "CoreAudio_SizeCallback:%d\n", end_position); | |
260 | |
261 return end_position; | |
262 } | |
263 | |
264 OSStatus CoreAudio_ReadCallback( | |
265 void* inClientData, | |
266 SInt64 inPosition, | |
267 UInt32 requestCount, | |
268 void* data_buffer, | |
269 UInt32* actualCount | |
270 ) | |
271 { | |
272 SDL_RWops* rw_ops = (SDL_RWops*)inClientData; | |
273 SDL_RWseek(rw_ops, inPosition, SEEK_SET); | |
274 size_t bytes_actually_read = SDL_RWread(rw_ops, data_buffer, 1, requestCount); | |
275 // Not sure how to test for a read error with SDL_RWops | |
276 // fprintf(stderr, "CoreAudio_ReadCallback:%d, %d\n", requestCount, bytes_actually_read); | |
277 | |
278 *actualCount = bytes_actually_read; | |
279 return noErr; | |
280 } | |
281 | |
282 | |
283 static int CoreAudio_open(Sound_Sample *sample, const char *ext) | |
284 { | |
285 CoreAudioFileContainer* core_audio_file_container; | |
286 AudioFileID* audio_file_id; | |
287 OSStatus error_result; | |
288 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
289 AudioStreamBasicDescription actual_format; | |
290 AudioStreamBasicDescription output_format; | |
291 Float64 estimated_duration; | |
292 UInt32 format_size; | |
293 | |
294 | |
295 core_audio_file_container = (CoreAudioFileContainer*)malloc(sizeof(CoreAudioFileContainer)); | |
296 BAIL_IF_MACRO(core_audio_file_container == NULL, ERR_OUT_OF_MEMORY, 0); | |
297 | |
298 | |
299 audio_file_id = (AudioFileID*)malloc(sizeof(AudioFileID)); | |
300 BAIL_IF_MACRO(audio_file_id == NULL, ERR_OUT_OF_MEMORY, 0); | |
301 | |
302 error_result = AudioFileOpenWithCallbacks( | |
303 internal->rw, | |
304 CoreAudio_ReadCallback, | |
305 NULL, | |
306 CoreAudio_SizeCallback, | |
307 NULL, | |
308 CoreAudio_GetAudioTypeForExtension(ext), | |
309 audio_file_id | |
310 ); | |
311 if (error_result != noErr) | |
312 { | |
313 AudioFileClose(*audio_file_id); | |
314 free(audio_file_id); | |
315 free(core_audio_file_container); | |
316 SNDDBG(("Core Audio: can't grok data. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
317 BAIL_MACRO("Core Audio: Not valid audio data.", 0); | |
318 } /* if */ | |
319 | |
320 format_size = sizeof(actual_format); | |
321 error_result = AudioFileGetProperty( | |
322 *audio_file_id, | |
323 kAudioFilePropertyDataFormat, | |
324 &format_size, | |
325 &actual_format | |
326 ); | |
327 if (error_result != noErr) | |
328 { | |
329 AudioFileClose(*audio_file_id); | |
330 free(audio_file_id); | |
331 free(core_audio_file_container); | |
332 SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
333 BAIL_MACRO("Core Audio: Not valid audio data.", 0); | |
334 } /* if */ | |
335 | |
336 format_size = sizeof(estimated_duration); | |
337 error_result = AudioFileGetProperty( | |
338 *audio_file_id, | |
339 kAudioFilePropertyEstimatedDuration, | |
340 &format_size, | |
341 &estimated_duration | |
342 ); | |
343 if (error_result != noErr) | |
344 { | |
345 AudioFileClose(*audio_file_id); | |
346 free(audio_file_id); | |
347 free(core_audio_file_container); | |
348 SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
349 BAIL_MACRO("Core Audio: Not valid audio data.", 0); | |
350 } /* if */ | |
351 | |
352 | |
353 core_audio_file_container->audioFileID = audio_file_id; | |
354 | |
355 internal->decoder_private = core_audio_file_container; | |
356 | |
357 sample->flags = SOUND_SAMPLEFLAG_CANSEEK; | |
358 sample->actual.rate = (Uint32) actual_format.mSampleRate; | |
359 sample->actual.channels = (Uint8)actual_format.mChannelsPerFrame; | |
360 internal->total_time = (Sint32)(estimated_duration * 1000.0 + 0.5); | |
361 | |
362 #if 0 | |
363 /* FIXME: Both Core Audio and SDL 1.3 support float and 32-bit formats */ | |
364 if(actual_format.mFormatFlags & kAudioFormatFlagIsBigEndian) | |
365 { | |
366 if(16 == actual_format.mBitsPerChannel) | |
367 { | |
368 if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) | |
369 { | |
370 sample->actual.format = AUDIO_S16MSB; | |
371 } | |
372 else | |
373 { | |
374 sample->actual.format = AUDIO_U16MSB; | |
375 } | |
376 } | |
377 else if(8 == actual_format.mBitsPerChannel) | |
378 { | |
379 if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) | |
380 { | |
381 sample->actual.format = AUDIO_S8; | |
382 } | |
383 else | |
384 { | |
385 sample->actual.format = AUDIO_U8; | |
386 } | |
387 } | |
388 else // might be 0 for undefined? | |
389 { | |
390 // This case seems to come up a lot for me. Maybe for file types like .m4a? | |
391 sample->actual.format = AUDIO_S16SYS; | |
392 SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel)); | |
393 | |
394 } | |
395 } | |
396 else // little endian | |
397 { | |
398 if(16 == actual_format.mBitsPerChannel) | |
399 { | |
400 if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) | |
401 { | |
402 sample->actual.format = AUDIO_S16LSB; | |
403 } | |
404 else | |
405 { | |
406 sample->actual.format = AUDIO_U16LSB; | |
407 } | |
408 } | |
409 else if(8 == actual_format.mBitsPerChannel) | |
410 { | |
411 if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) | |
412 { | |
413 sample->actual.format = AUDIO_S8; | |
414 } | |
415 else | |
416 { | |
417 sample->actual.format = AUDIO_U8; | |
418 } | |
419 } | |
420 else // might be 0 for undefined? | |
421 { | |
422 sample->actual.format = AUDIO_S16SYS; | |
423 | |
424 SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel)); | |
425 } | |
426 | |
427 } | |
428 #else | |
429 | |
430 | |
431 | |
432 /* | |
433 * I want to use Core Audio to do conversion and decoding for performance reasons. | |
434 * This is particularly important on mobile devices like iOS. | |
435 * Taking from the Ogg Vorbis decode, I pretend the "actual" format is the same | |
436 * as the desired format. | |
437 */ | |
438 sample->actual.format = (sample->desired.format == 0) ? | |
439 AUDIO_S16SYS : sample->desired.format; | |
440 #endif | |
441 | |
442 | |
443 SNDDBG(("CoreAudio: channels == (%d).\n", sample->actual.channels)); | |
444 SNDDBG(("CoreAudio: sampling rate == (%d).\n",sample->actual.rate)); | |
445 SNDDBG(("CoreAudio: total seconds of sample == (%d).\n", internal->total_time)); | |
446 SNDDBG(("CoreAudio: sample->actual.format == (%d).\n", sample->actual.format)); | |
447 | |
448 | |
449 | |
450 error_result = ExtAudioFileWrapAudioFileID(*audio_file_id, | |
451 false, // set to false for read-only | |
452 &core_audio_file_container->extAudioFileRef | |
453 ); | |
454 if(error_result != noErr) | |
455 { | |
456 AudioFileClose(*audio_file_id); | |
457 free(audio_file_id); | |
458 free(core_audio_file_container); | |
459 SNDDBG(("Core Audio: can't wrap data. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
460 BAIL_MACRO("Core Audio: Failed to wrap data.", 0); | |
461 } /* if */ | |
462 | |
463 | |
464 /* The output format must be linear PCM because that's the only type OpenAL knows how to deal with. | |
465 * Set the client format to 16 bit signed integer (native-endian) data because that is the most | |
466 * optimal format on iPhone/iPod Touch hardware. | |
467 * Maintain the channel count and sample rate of the original source format. | |
468 */ | |
469 output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate | |
470 output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels | |
471 output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data | |
472 output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet | |
473 | |
474 if(sample->desired.format == 0) | |
475 { | |
476 // do AUDIO_S16SYS | |
477 output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; | |
478 output_format.mBitsPerChannel = 16; // We know we want 16-bit | |
479 } | |
480 else | |
481 { | |
482 output_format.mFormatFlags = 0; // clear flags | |
483 output_format.mFormatFlags |= kAudioFormatFlagIsPacked; // I seem to read failures problems without this | |
484 // Mask against bitsize | |
485 if(0xFF & sample->desired.format) | |
486 { | |
487 output_format.mBitsPerChannel = 16; /* 16-bit */ | |
488 } | |
489 else | |
490 { | |
491 output_format.mBitsPerChannel = 8; /* 8-bit */ | |
492 } | |
493 | |
494 // Mask for signed/unsigned | |
495 if((1<<15) & sample->desired.format) | |
496 { | |
497 output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsSignedInteger; | |
498 | |
499 } | |
500 else | |
501 { | |
502 // no flag set for unsigned | |
503 } | |
504 // Mask for big/little endian | |
505 if((1<<12) & sample->desired.format) | |
506 { | |
507 output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsBigEndian; | |
508 } | |
509 else | |
510 { | |
511 // no flag set for little endian | |
512 } | |
513 } | |
514 | |
515 output_format.mBitsPerChannel = 16; // We know we want 16-bit | |
516 output_format.mBytesPerPacket = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // e.g. 16-bits/8 * channels => so 2-bytes per channel per frame | |
517 output_format.mBytesPerFrame = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket | |
518 | |
519 | |
520 /* | |
521 output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate | |
522 output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels | |
523 output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data | |
524 // output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; | |
525 output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger; | |
526 output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet | |
527 output_format.mBitsPerChannel = 16; // We know we want 16-bit | |
528 output_format.mBytesPerPacket = 2 * output_format.mChannelsPerFrame; // We know we are using 16-bit, so 2-bytes per channel per frame | |
529 output_format.mBytesPerFrame = 2 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket | |
530 */ | |
531 SNDDBG(("output_format: mSampleRate: %lf\n", output_format.mSampleRate)); | |
532 SNDDBG(("output_format: mChannelsPerFrame: %d\n", output_format.mChannelsPerFrame)); | |
533 SNDDBG(("output_format: mFormatID: %d\n", output_format.mFormatID)); | |
534 SNDDBG(("output_format: mFormatFlags: %d\n", output_format.mFormatFlags)); | |
535 SNDDBG(("output_format: mFramesPerPacket: %d\n", output_format.mFramesPerPacket)); | |
536 SNDDBG(("output_format: mBitsPerChannel: %d\n", output_format.mBitsPerChannel)); | |
537 SNDDBG(("output_format: mBytesPerPacket: %d\n", output_format.mBytesPerPacket)); | |
538 SNDDBG(("output_format: mBytesPerFrame: %d\n", output_format.mBytesPerFrame)); | |
539 | |
540 | |
541 /* Set the desired client (output) data format */ | |
542 error_result = ExtAudioFileSetProperty(core_audio_file_container->extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(output_format), &output_format); | |
543 if(noErr != error_result) | |
544 { | |
545 ExtAudioFileDispose(core_audio_file_container->extAudioFileRef); | |
546 AudioFileClose(*audio_file_id); | |
547 free(audio_file_id); | |
548 free(core_audio_file_container); | |
549 SNDDBG(("Core Audio: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) failed, reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
550 BAIL_MACRO("Core Audio: Not valid audio data.", 0); | |
551 } | |
552 | |
553 | |
554 core_audio_file_container->outputFormat = (AudioStreamBasicDescription*)malloc(sizeof(AudioStreamBasicDescription)); | |
555 BAIL_IF_MACRO(core_audio_file_container->outputFormat == NULL, ERR_OUT_OF_MEMORY, 0); | |
556 | |
557 | |
558 | |
559 /* Copy the output format to the audio_description that was passed in so the | |
560 * info will be returned to the user. | |
561 */ | |
562 memcpy(core_audio_file_container->outputFormat, &output_format, sizeof(AudioStreamBasicDescription)); | |
563 | |
564 | |
565 | |
566 return(1); | |
567 } /* CoreAudio_open */ | |
568 | |
569 | |
570 static void CoreAudio_close(Sound_Sample *sample) | |
571 { | |
572 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
573 CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; | |
574 | |
575 ExtAudioFileDispose(core_audio_file_container->extAudioFileRef); | |
576 AudioFileClose(*core_audio_file_container->audioFileID); | |
577 free(core_audio_file_container->audioFileID); | |
578 free(core_audio_file_container); | |
579 | |
580 } /* CoreAudio_close */ | |
581 | |
582 | |
583 static Uint32 CoreAudio_read(Sound_Sample *sample) | |
584 { | |
585 OSStatus error_result = noErr; | |
586 SInt64 buffer_size_in_frames = 0; | |
587 SInt64 buffer_size_in_frames_remaining = 0; | |
588 SInt64 total_frames_read = 0; | |
589 Uint32 data_buffer_size = 0; | |
590 Uint32 bytes_remaining = 0; | |
591 Uint32 total_bytes_read = 0; | |
592 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
593 CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; | |
594 Uint32 max_buffer_size = internal->buffer_size; | |
595 | |
596 // printf("internal->buffer_size=%d, internal->buffer=0x%x, sample->buffer_size=%d\n", internal->buffer_size, internal->buffer, sample->buffer_size); | |
597 // printf("internal->max_buffer_size=%d\n", max_buffer_size); | |
598 | |
599 /* Compute how many frames will fit into our max buffer size */ | |
600 /* Warning: If this is not evenly divisible, the buffer will not be completely filled which violates the SDL_sound assumption. */ | |
601 buffer_size_in_frames = max_buffer_size / core_audio_file_container->outputFormat->mBytesPerFrame; | |
602 // printf("buffer_size_in_frames=%ld, internal->buffer_size=%d, internal->buffer=0x%x outputFormat->mBytesPerFrame=%d, sample->buffer_size=%d\n", buffer_size_in_frames, internal->buffer_size, internal->buffer, core_audio_file_container->outputFormat->mBytesPerFrame, sample->buffer_size); | |
603 | |
604 | |
605 // void* temp_buffer = malloc(max_buffer_size); | |
606 | |
607 AudioBufferList audio_buffer_list; | |
608 audio_buffer_list.mNumberBuffers = 1; | |
609 audio_buffer_list.mBuffers[0].mDataByteSize = max_buffer_size; | |
610 audio_buffer_list.mBuffers[0].mNumberChannels = core_audio_file_container->outputFormat->mChannelsPerFrame; | |
611 audio_buffer_list.mBuffers[0].mData = internal->buffer; | |
612 | |
613 | |
614 bytes_remaining = max_buffer_size; | |
615 buffer_size_in_frames_remaining = buffer_size_in_frames; | |
616 | |
617 // oops. Due to a bug in how I opened the file, I was misled to believe that Core Audio | |
618 // was not always filling my entire requested buffer. So this while-loop might be unnecessary. | |
619 // However, I have not exhaustively tested all formats, so maybe it is possible this loop is useful. | |
620 while(buffer_size_in_frames_remaining > 0 && !(sample->flags & SOUND_SAMPLEFLAG_EOF)) | |
621 { | |
622 | |
623 data_buffer_size = (Uint32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame); | |
624 // printf("data_buffer_size=%d\n", data_buffer_size); | |
625 | |
626 buffer_size_in_frames = buffer_size_in_frames_remaining; | |
627 | |
628 // printf("reading buffer_size_in_frames=%"PRId64"\n", buffer_size_in_frames); | |
629 | |
630 | |
631 audio_buffer_list.mBuffers[0].mDataByteSize = bytes_remaining; | |
632 audio_buffer_list.mBuffers[0].mData = &(((Uint8*)internal->buffer)[total_bytes_read]); | |
633 | |
634 | |
635 /* Read the data into an AudioBufferList */ | |
636 error_result = ExtAudioFileRead(core_audio_file_container->extAudioFileRef, (UInt32*)&buffer_size_in_frames, &audio_buffer_list); | |
637 if(error_result == noErr) | |
638 { | |
639 | |
640 | |
641 /* Success */ | |
642 | |
643 total_frames_read += buffer_size_in_frames; | |
644 buffer_size_in_frames_remaining = buffer_size_in_frames_remaining - buffer_size_in_frames; | |
645 | |
646 printf("read buffer_size_in_frames=%"PRId64", buffer_size_in_frames_remaining=%"PRId64"\n", buffer_size_in_frames, buffer_size_in_frames_remaining); | |
647 | |
648 /* ExtAudioFileRead returns the number of frames actually read. Need to convert back to bytes. */ | |
649 data_buffer_size = (Uint32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame); | |
650 // printf("data_buffer_size=%d\n", data_buffer_size); | |
651 | |
652 total_bytes_read += data_buffer_size; | |
653 bytes_remaining = bytes_remaining - data_buffer_size; | |
654 | |
655 /* Note: 0 == buffer_size_in_frames is a legitimate value meaning we are EOF. */ | |
656 if(0 == buffer_size_in_frames) | |
657 { | |
658 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
659 } | |
660 | |
661 } | |
662 else | |
663 { | |
664 SNDDBG(("Core Audio: ExtAudioFileReadfailed, reason: [%s].\n", CoreAudio_FourCCToString(error_result))); | |
665 | |
666 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
667 break; | |
668 | |
669 } | |
670 } | |
671 | |
672 if( (!(sample->flags & SOUND_SAMPLEFLAG_EOF)) && (total_bytes_read < max_buffer_size)) | |
673 { | |
674 SNDDBG(("Core Audio: ExtAudioFileReadfailed SOUND_SAMPLEFLAG_EAGAIN, reason: [total_bytes_read < max_buffer_size], %d, %d.\n", total_bytes_read , max_buffer_size)); | |
675 | |
676 // Don't know what to do here. | |
677 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | |
678 } | |
679 return total_bytes_read; | |
680 } /* CoreAudio_read */ | |
681 | |
682 | |
683 static int CoreAudio_rewind(Sound_Sample *sample) | |
684 { | |
685 OSStatus error_result = noErr; | |
686 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
687 CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; | |
688 | |
689 error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, 0); | |
690 if(error_result != noErr) | |
691 { | |
692 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
693 } | |
694 return(1); | |
695 } /* CoreAudio_rewind */ | |
696 | |
697 | |
698 static int CoreAudio_seek(Sound_Sample *sample, Uint32 ms) | |
699 { | |
700 OSStatus error_result = noErr; | |
701 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
702 CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; | |
703 SInt64 frame_offset = 0; | |
704 AudioStreamBasicDescription actual_format; | |
705 UInt32 format_size; | |
706 | |
707 format_size = sizeof(AudioStreamBasicDescription); | |
708 error_result = AudioFileGetProperty( | |
709 *core_audio_file_container->audioFileID, | |
710 kAudioFilePropertyDataFormat, | |
711 &format_size, | |
712 &actual_format | |
713 ); | |
714 if(error_result != noErr) | |
715 { | |
716 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
717 BAIL_MACRO("Core Audio: Could not GetProperty for kAudioFilePropertyDataFormat.", 0); | |
718 } /* if */ | |
719 | |
720 // packetIndex = (pos * sampleRate) / framesPerPacket | |
721 frame_offset = (SInt64)((ms/1000.0 * actual_format.mSampleRate) / actual_format.mFramesPerPacket); | |
722 // computed against actual format and not the client format | |
723 error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, frame_offset); | |
724 if(error_result != noErr) | |
725 { | |
726 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
727 } | |
728 | |
729 return(1); | |
730 } /* CoreAudio_seek */ | |
731 | |
732 #endif /* SOUND_SUPPORTS_COREAUDIO */ | |
733 | |
734 | |
735 /* end of ogg.c ... */ | |
736 |