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