comparison Isolated/coreaudio.c @ 38:71b465ff0622

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