1 /* Copyright: � Copyright 2005 Apple Computer, Inc. All rights reserved.
2
3 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
4 ("Apple") in consideration of your agreement to the following terms, and your
5 use, installation, modification or redistribution of this Apple software
6 constitutes acceptance of these terms. If you do not agree with these terms,
7 please do not use, install, modify or redistribute this Apple software.
8
9 In consideration of your agreement to abide by the following terms, and subject
10 to these terms, Apple grants you a personal, non-exclusive license, under Apple�s
11 copyrights in this original Apple software (the "Apple Software"), to use,
12 reproduce, modify and redistribute the Apple Software, with or without
13 modifications, in source and/or binary forms; provided that if you redistribute
14 the Apple Software in its entirety and without modifications, you must retain
15 this notice and the following text and disclaimers in all such redistributions of
16 the Apple Software. Neither the name, trademarks, service marks or logos of
17 Apple Computer, Inc. may be used to endorse or promote products derived from the
18 Apple Software without specific prior written permission from Apple. Except as
19 expressly stated in this notice, no other rights or licenses, express or implied,
20 are granted by Apple herein, including but not limited to any patent rights that
21 may be infringed by your derivative works or by other works in which the Apple
22 Software may be incorporated.
23
24 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
25 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28 COMBINATION WITH YOUR PRODUCTS.
29
30 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /*=============================================================================
39 CAAudioFile.cpp
40
41 =============================================================================*/
42
43 #include "CAAudioFile.h"
44
45 #if !CAAF_USE_EXTAUDIOFILE
46
47 #include "CAXException.h"
48 #include <algorithm>
49 #include "CAHostTimeBase.h"
50 #include "CADebugMacros.h"
51
52 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
53 #include <AudioToolbox/AudioToolbox.h>
54 #else
55 #include <AudioFormat.h>
56 #endif
57
58 #if DEBUG
59 //#define VERBOSE_IO 1
60 //#define VERBOSE_CONVERTER 1
61 //#define VERBOSE_CHANNELMAP 1
62 //#define LOG_FUNCTION_ENTRIES 1
63
64 #if VERBOSE_CHANNELMAP
65 #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility
66 #endif
67 #endif
68
69 #if LOG_FUNCTION_ENTRIES
70 class FunctionLogger {
71 public:
FunctionLogger(const char * name,const char * fmt=NULL,...)72 FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) {
73 Indent();
74 printf("-> %s ", name);
75 if (fmt) {
76 va_list args;
77 va_start(args, fmt);
78 vprintf(fmt, args);
79 va_end(args);
80 }
81 printf("\n");
82 ++sIndent;
83 }
~FunctionLogger()84 ~FunctionLogger() {
85 --sIndent;
86 Indent();
87 printf("<- %s\n", mName);
88 if (sIndent == 0)
89 printf("\n");
90 }
91
Indent()92 static void Indent() {
93 for (int i = sIndent; --i >= 0; ) {
94 putchar(' '); putchar(' ');
95 }
96 }
97
98 const char *mName;
99 static int sIndent;
100 };
101 int FunctionLogger::sIndent = 0;
102
103 #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
104 #else
105 #define LOG_FUNCTION(name, format, foo)
106 #endif
107
108 static const UInt32 kDefaultIOBufferSizeBytes = 0x10000;
109
110 #if CAAUDIOFILE_PROFILE
111 #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0
112 #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime)
113 #else
114 #define StartTiming(af, starttime)
115 #define ElapsedTime(af, starttime, counter)
116 #endif
117
118 #define kNoMoreInputRightNow 'nein'
119
120 // _______________________________________________________________________________________
121 //
CAAudioFile()122 CAAudioFile::CAAudioFile() :
123 mAudioFile(0),
124 mUseCache(false),
125 mFinishingEncoding(false),
126 mMode(kClosed),
127 mFileDataOffset(-1),
128 mFramesToSkipFollowingSeek(0),
129
130 mClientOwnsIOBuffer(false),
131 mPacketDescs(NULL),
132 mNumPacketDescs(0),
133 mConverter(NULL),
134 mMagicCookie(NULL),
135 mWriteBufferList(NULL)
136 #if CAAUDIOFILE_PROFILE
137 ,
138 mProfiling(false),
139 mTicksInConverter(0),
140 mTicksInReadInConverter(0),
141 mTicksInIO(0),
142 mInConverter(false)
143 #endif
144 {
145 mIOBufferList.mBuffers[0].mData = NULL;
146 mIOBufferList.mBuffers[0].mDataByteSize = 0;
147 mClientMaxPacketSize = 0;
148 mIOBufferSizeBytes = kDefaultIOBufferSizeBytes;
149 }
150
151 // _______________________________________________________________________________________
152 //
~CAAudioFile()153 CAAudioFile::~CAAudioFile()
154 {
155 Close();
156 }
157
158 // _______________________________________________________________________________________
159 //
Close()160 void CAAudioFile::Close()
161 {
162 LOG_FUNCTION("CAAudioFile::Close", NULL, NULL);
163 if (mMode == kClosed)
164 return;
165 if (mMode == kWriting)
166 FlushEncoder();
167 CloseConverter();
168 if (mAudioFile != 0 && mOwnOpenFile) {
169 AudioFileClose(mAudioFile);
170 mAudioFile = 0;
171 }
172 if (!mClientOwnsIOBuffer) {
173 delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
174 mIOBufferList.mBuffers[0].mData = NULL;
175 mIOBufferList.mBuffers[0].mDataByteSize = 0;
176 }
177 delete[] mPacketDescs; mPacketDescs = NULL; mNumPacketDescs = 0;
178 delete[] mMagicCookie; mMagicCookie = NULL;
179 delete mWriteBufferList; mWriteBufferList = NULL;
180 mMode = kClosed;
181 }
182
183 // _______________________________________________________________________________________
184 //
CloseConverter()185 void CAAudioFile::CloseConverter()
186 {
187 if (mConverter) {
188 #if VERBOSE_CONVERTER
189 printf("CAAudioFile %p : CloseConverter\n", this);
190 #endif
191 AudioConverterDispose(mConverter);
192 mConverter = NULL;
193 }
194 }
195
196 // =======================================================================================
197
198 // _______________________________________________________________________________________
199 //
Open(const FSRef & fsref)200 void CAAudioFile::Open(const FSRef &fsref)
201 {
202 LOG_FUNCTION("CAAudioFile::Open", "%p", this);
203 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
204 mFSRef = fsref;
205 XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file");
206 mOwnOpenFile = true;
207 mMode = kReading;
208 GetExistingFileInfo();
209 }
210
211 // _______________________________________________________________________________________
212 //
Wrap(AudioFileID fileID,bool forWriting)213 void CAAudioFile::Wrap(AudioFileID fileID, bool forWriting)
214 {
215 LOG_FUNCTION("CAAudioFile::Wrap", "%p", this);
216 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
217
218 mAudioFile = fileID;
219 mOwnOpenFile = false;
220 mMode = forWriting ? kPreparingToWrite : kReading;
221 GetExistingFileInfo();
222 if (forWriting)
223 FileFormatChanged();
224 }
225
226 // _______________________________________________________________________________________
227 //
CreateNew(const FSRef & parentDir,CFStringRef filename,AudioFileTypeID filetype,const AudioStreamBasicDescription & dataFormat,const AudioChannelLayout * layout)228 void CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout)
229 {
230 LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this);
231 XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
232
233 mFileDataFormat = dataFormat;
234 if (layout) {
235 mFileChannelLayout = layout;
236 #if VERBOSE_CHANNELMAP
237 printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
238 #endif
239 }
240 mMode = kPreparingToCreate;
241 FileFormatChanged(&parentDir, filename, filetype);
242 }
243
244 // _______________________________________________________________________________________
245 //
246 // called to create the file -- or update its format/channel layout/properties based on an encoder
247 // setting change
FileFormatChanged(const FSRef * parentDir,CFStringRef filename,AudioFileTypeID filetype)248 void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype)
249 {
250 LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this);
251 XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared");
252
253 UInt32 propertySize;
254 OSStatus err;
255 AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat;
256
257 #if VERBOSE_CONVERTER
258 mFileDataFormat.PrintFormat(stdout, "", "Specified file data format");
259 #endif
260
261 // Find out the actual format the converter will produce. This is necessary in
262 // case the bitrate has forced a lower sample rate, which needs to be set correctly
263 // in the stream description passed to AudioFileCreate.
264 if (mConverter != NULL) {
265 propertySize = sizeof(AudioStreamBasicDescription);
266 Float64 origSampleRate = mFileDataFormat.mSampleRate;
267 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description");
268 // do the same for the channel layout being output by the converter
269 #if VERBOSE_CONVERTER
270 mFileDataFormat.PrintFormat(stdout, "", "Converter output");
271 #endif
272 if (fiszero(mFileDataFormat.mSampleRate))
273 mFileDataFormat.mSampleRate = origSampleRate;
274 err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL);
275 if (err == noErr && propertySize > 0) {
276 AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
277 err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout);
278 if (err) {
279 free(layout);
280 XThrow(err, "couldn't get audio converter's output channel layout");
281 }
282 mFileChannelLayout = layout;
283 #if VERBOSE_CHANNELMAP
284 printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
285 #endif
286 free(layout);
287 }
288 }
289
290 // create the output file
291 if (mMode == kPreparingToCreate) {
292 CAStreamBasicDescription newFileDataFormat = mFileDataFormat;
293 if (fiszero(newFileDataFormat.mSampleRate))
294 newFileDataFormat.mSampleRate = 44100; // just make something up for now
295 #if VERBOSE_CONVERTER
296 newFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
297 #endif
298 XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file");
299 mMode = kPreparingToWrite;
300 mOwnOpenFile = true;
301 } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) {
302 // second check must be explicit since operator== on ASBD treats SR of zero as "don't care"
303 if (fiszero(mFileDataFormat.mSampleRate))
304 mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate;
305 #if VERBOSE_CONVERTER
306 mFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
307 #endif
308 XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0");
309 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format");
310 }
311
312 UInt32 deferSizeUpdates = 1;
313 err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates);
314
315 if (mConverter != NULL) {
316 // encoder
317 // get the magic cookie, if any, from the converter
318 delete[] mMagicCookie; mMagicCookie = NULL;
319 mMagicCookieSize = 0;
320
321 err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL);
322
323 // we can get a noErr result and also a propertySize == 0
324 // -- if the file format does support magic cookies, but this file doesn't have one.
325 if (err == noErr && propertySize > 0) {
326 mMagicCookie = new Byte[propertySize];
327 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie");
328 mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size
329 // now set the magic cookie on the output file
330 UInt32 willEatTheCookie = false;
331 // the converter wants to give us one; will the file take it?
332 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData,
333 NULL, &willEatTheCookie);
334 if (err == noErr && willEatTheCookie) {
335 #if VERBOSE_CONVERTER
336 printf("Setting cookie on encoded file\n");
337 #endif
338 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie");
339 }
340 }
341
342 // get maximum packet size
343 propertySize = sizeof(UInt32);
344 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size");
345
346 AllocateBuffers(true /* okToFail */);
347 } else {
348 InitFileMaxPacketSize();
349 }
350
351 if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) {
352 // don't bother tagging mono/stereo files
353 UInt32 isWritable;
354 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable);
355 if (!err && isWritable) {
356 #if VERBOSE_CHANNELMAP
357 printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
358 #endif
359 err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout,
360 mFileChannelLayout.Size(), &mFileChannelLayout.Layout());
361 if (err)
362 CAXException::Warning("could not set the file's channel layout", err);
363 } else {
364 #if VERBOSE_CHANNELMAP
365 printf("file won't accept a channel layout (write)\n");
366 #endif
367 }
368 }
369
370 UpdateClientMaxPacketSize(); // also sets mFrame0Offset
371 mPacketMark = 0;
372 mFrameMark = 0;
373 }
374
375 // _______________________________________________________________________________________
376 //
InitFileMaxPacketSize()377 void CAAudioFile::InitFileMaxPacketSize()
378 {
379 LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this);
380 UInt32 propertySize = sizeof(UInt32);
381 OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize,
382 &propertySize, &mFileMaxPacketSize);
383 if (err) {
384 // workaround for 3361377: not all file formats' maximum packet sizes are supported
385 if (!mFileDataFormat.IsPCM())
386 XThrowIfError(err, "get audio file's maximum packet size");
387 mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame;
388 }
389 AllocateBuffers(true /* okToFail */);
390 }
391
392
393 // _______________________________________________________________________________________
394 //
FileDataOffset()395 SInt64 CAAudioFile::FileDataOffset()
396 {
397 if (mFileDataOffset < 0) {
398 UInt32 propertySize = sizeof(SInt64);
399 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset");
400 }
401 return mFileDataOffset;
402 }
403
404 // _______________________________________________________________________________________
405 //
GetNumberFrames() const406 SInt64 CAAudioFile::GetNumberFrames() const
407 {
408 AudioFilePacketTableInfo pti;
409 UInt32 propertySize = sizeof(pti);
410 OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
411 if (err == noErr)
412 return pti.mNumberValidFrames;
413 return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset;
414 }
415
416 // _______________________________________________________________________________________
417 //
SetNumberFrames(SInt64 nFrames)418 void CAAudioFile::SetNumberFrames(SInt64 nFrames)
419 {
420 XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM");
421 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file");
422 }
423
424 // _______________________________________________________________________________________
425 //
426 // call for existing file, NOT new one - from Open() or Wrap()
GetExistingFileInfo()427 void CAAudioFile::GetExistingFileInfo()
428 {
429 LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this);
430 UInt32 propertySize;
431 OSStatus err;
432
433 // get mFileDataFormat
434 propertySize = sizeof(AudioStreamBasicDescription);
435 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format");
436
437 // get mFileChannelLayout
438 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL);
439 if (err == noErr && propertySize > 0) {
440 AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
441 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout);
442 if (err == noErr) {
443 mFileChannelLayout = layout;
444 #if VERBOSE_CHANNELMAP
445 printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
446 #endif
447 }
448 free(layout);
449 XThrowIfError(err, "get audio file's channel layout");
450 }
451 if (mMode != kReading)
452 return;
453
454 #if 0
455 // get mNumberPackets
456 propertySize = sizeof(mNumberPackets);
457 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count");
458 #if VERBOSE_IO
459 printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets);
460 #endif
461 #endif
462
463 // get mMagicCookie
464 err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
465 if (err == noErr && propertySize > 0) {
466 mMagicCookie = new Byte[propertySize];
467 mMagicCookieSize = propertySize;
468 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie");
469 }
470 InitFileMaxPacketSize();
471 mPacketMark = 0;
472 mFrameMark = 0;
473
474 UpdateClientMaxPacketSize();
475 }
476
477 // =======================================================================================
478
479 // _______________________________________________________________________________________
480 //
SetFileChannelLayout(const CAAudioChannelLayout & layout)481 void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout)
482 {
483 LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this);
484 mFileChannelLayout = layout;
485 #if VERBOSE_CHANNELMAP
486 printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
487 #endif
488 if (mMode != kReading)
489 FileFormatChanged();
490 }
491
492 // _______________________________________________________________________________________
493 //
SetClientFormat(const CAStreamBasicDescription & dataFormat,const CAAudioChannelLayout * layout)494 void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
495 {
496 LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this);
497 XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file");
498
499 bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat);
500
501 if (dataFormatChanging) {
502 CloseConverter();
503 if (mWriteBufferList) {
504 delete mWriteBufferList;
505 mWriteBufferList = NULL;
506 }
507 mClientDataFormat = dataFormat;
508 }
509
510 if (layout && layout->IsValid()) {
511 XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map");
512 mClientChannelLayout = *layout;
513 }
514
515 bool differentLayouts;
516 if (mClientChannelLayout.IsValid()) {
517 if (mFileChannelLayout.IsValid()) {
518 differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag();
519 #if VERBOSE_CHANNELMAP
520 printf("two valid layouts, %s\n", differentLayouts ? "different" : "same");
521 #endif
522 } else {
523 differentLayouts = false;
524 #if VERBOSE_CHANNELMAP
525 printf("valid client layout, unknown file layout\n");
526 #endif
527 }
528 } else {
529 differentLayouts = false;
530 #if VERBOSE_CHANNELMAP
531 if (mFileChannelLayout.IsValid())
532 printf("valid file layout, unknown client layout\n");
533 else
534 printf("two invalid layouts\n");
535 #endif
536 }
537
538 if (mClientDataFormat != mFileDataFormat || differentLayouts) {
539 // We need an AudioConverter.
540 if (mMode == kReading) {
541 // file -> client (decode)
542 //mFileDataFormat.PrintFormat( stdout, "", "File: ");
543 //mClientDataFormat.PrintFormat(stdout, "", "Client: ");
544
545 if (mConverter == NULL)
546 XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter),
547 "create audio converter");
548
549 #if VERBOSE_CONVERTER
550 printf("CAAudioFile %p -- created converter\n", this);
551 CAShow(mConverter);
552 #endif
553 // set the magic cookie, if any (for decode)
554 if (mMagicCookie)
555 SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM());
556 // we get cookies from some AIFF's but the converter barfs on them,
557 // so we set canFail to true for PCM
558
559 SetConverterChannelLayout(false, mFileChannelLayout);
560 SetConverterChannelLayout(true, mClientChannelLayout);
561
562 // propagate leading/trailing frame counts
563 if (mFileDataFormat.mBitsPerChannel == 0) {
564 UInt32 propertySize;
565 OSStatus err;
566 AudioFilePacketTableInfo pti;
567 propertySize = sizeof(pti);
568 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
569 if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) {
570 AudioConverterPrimeInfo primeInfo;
571 primeInfo.leadingFrames = pti.mPrimingFrames;
572 primeInfo.trailingFrames = pti.mRemainderFrames;
573 /* ignore any error. better to play it at all than not. */
574 /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo);
575 //XThrowIfError(err, "couldn't set prime info on converter");
576 }
577 }
578 } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) {
579 // client -> file (encode)
580 if (mConverter == NULL)
581 XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter");
582 mWriteBufferList = CABufferList::New("", mClientDataFormat);
583 SetConverterChannelLayout(false, mClientChannelLayout);
584 SetConverterChannelLayout(true, mFileChannelLayout);
585 if (mMode == kPreparingToWrite)
586 FileFormatChanged();
587 } else
588 XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known");
589 }
590 UpdateClientMaxPacketSize();
591 }
592
593 // _______________________________________________________________________________________
594 //
SetConverterProperty(AudioConverterPropertyID inPropertyID,UInt32 inPropertyDataSize,const void * inPropertyData,bool inCanFail)595 OSStatus CAAudioFile::SetConverterProperty(
596 AudioConverterPropertyID inPropertyID,
597 UInt32 inPropertyDataSize,
598 const void* inPropertyData,
599 bool inCanFail)
600 {
601 OSStatus err = noErr;
602 //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
603 if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL)
604 ;
605 else {
606 err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData);
607 if (!inCanFail) {
608 XThrowIfError(err, "set audio converter property");
609 }
610 }
611 UpdateClientMaxPacketSize();
612 if (mMode == kPreparingToWrite)
613 FileFormatChanged();
614 return err;
615 }
616
617 // _______________________________________________________________________________________
618 //
SetConverterChannelLayout(bool output,const CAAudioChannelLayout & layout)619 void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout)
620 {
621 LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
622 OSStatus err;
623
624 if (layout.IsValid()) {
625 #if VERBOSE_CHANNELMAP
626 printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input",
627 CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
628 #endif
629 if (output) {
630 err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout,
631 layout.Size(), &layout.Layout());
632 XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout");
633 } else {
634 err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout,
635 layout.Size(), &layout.Layout());
636 XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout");
637 }
638 if (mMode == kPreparingToWrite)
639 FileFormatChanged();
640 }
641 }
642
643 // _______________________________________________________________________________________
644 //
GetConverterConfig()645 CFArrayRef CAAudioFile::GetConverterConfig()
646 {
647 CFArrayRef plist;
648 UInt32 propertySize = sizeof(plist);
649 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings");
650 return plist;
651 }
652
653 // _______________________________________________________________________________________
654 //
UpdateClientMaxPacketSize()655 void CAAudioFile::UpdateClientMaxPacketSize()
656 {
657 LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
658 mFrame0Offset = 0;
659 if (mConverter != NULL) {
660 AudioConverterPropertyID property = (mMode == kReading) ?
661 kAudioConverterPropertyMaximumOutputPacketSize :
662 kAudioConverterPropertyMaximumInputPacketSize;
663
664 UInt32 propertySize = sizeof(UInt32);
665 XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize),
666 "get audio converter's maximum packet size");
667
668 if (mFileDataFormat.mBitsPerChannel == 0) {
669 AudioConverterPrimeInfo primeInfo;
670 propertySize = sizeof(primeInfo);
671 OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
672 if (err == noErr)
673 mFrame0Offset = primeInfo.leadingFrames;
674 #if VERBOSE_CONVERTER
675 printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset);
676 #endif
677 }
678 } else {
679 mClientMaxPacketSize = mFileMaxPacketSize;
680 }
681 }
682
683 // _______________________________________________________________________________________
684 // Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs
685 // Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes
AllocateBuffers(bool okToFail)686 void CAAudioFile::AllocateBuffers(bool okToFail)
687 {
688 LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this);
689 if (mFileMaxPacketSize == 0) {
690 if (okToFail)
691 return;
692 XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0");
693 }
694 UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize);
695 // must be big enough for at least one maximum size packet
696
697 if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) {
698 mIOBufferList.mNumberBuffers = 1;
699 mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame;
700 if (!mClientOwnsIOBuffer) {
701 //printf("reallocating I/O buffer\n");
702 delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
703 mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes];
704 }
705 mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes;
706 mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize;
707 }
708
709 UInt32 propertySize = sizeof(UInt32);
710 UInt32 externallyFramed;
711 XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed,
712 sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed),
713 "is format externally framed");
714 if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) {
715 delete[] mPacketDescs;
716 mPacketDescs = NULL;
717 mNumPacketDescs = 0;
718
719 if (externallyFramed) {
720 //printf("reallocating packet descs\n");
721 mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets];
722 mNumPacketDescs = mIOBufferSizePackets;
723 }
724 }
725 }
726
727 // _______________________________________________________________________________________
728 //
SetIOBuffer(void * buf)729 void CAAudioFile::SetIOBuffer(void *buf)
730 {
731 if (!mClientOwnsIOBuffer)
732 delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
733 mIOBufferList.mBuffers[0].mData = buf;
734
735 if (buf == NULL) {
736 mClientOwnsIOBuffer = false;
737 SetIOBufferSizeBytes(mIOBufferSizeBytes);
738 } else {
739 mClientOwnsIOBuffer = true;
740 AllocateBuffers();
741 }
742 // printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
743 }
744
745 // ===============================================================================
746
747 /*
748 For Tiger:
749 added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket.
750 You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty.
751
752 kAudioFilePropertyPacketToFrame = 'pkfr',
753 // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored.
754 kAudioFilePropertyFrameToPacket = 'frpk',
755 // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back.
756
757 struct AudioFramePacketTranslation
758 {
759 SInt64 mFrame;
760 SInt64 mPacket;
761 UInt32 mFrameOffsetInPacket;
762 };
763 */
764
PacketToFrame(SInt64 packet) const765 SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const
766 {
767 AudioFramePacketTranslation trans;
768 UInt32 propertySize;
769
770 switch (mFileDataFormat.mFramesPerPacket) {
771 case 1:
772 return packet;
773 case 0:
774 trans.mPacket = packet;
775 propertySize = sizeof(trans);
776 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans),
777 "packet <-> frame translation unimplemented for format with variable frames/packet");
778 return trans.mFrame;
779 }
780 return packet * mFileDataFormat.mFramesPerPacket;
781 }
782
FrameToPacket(SInt64 inFrame) const783 SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const
784 {
785 AudioFramePacketTranslation trans;
786 UInt32 propertySize;
787
788 switch (mFileDataFormat.mFramesPerPacket) {
789 case 1:
790 return inFrame;
791 case 0:
792 trans.mFrame = inFrame;
793 propertySize = sizeof(trans);
794 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans),
795 "packet <-> frame translation unimplemented for format with variable frames/packet");
796 return trans.mPacket;
797 }
798 return inFrame / mFileDataFormat.mFramesPerPacket;
799 }
800
801 // _______________________________________________________________________________________
802 //
803
Tell() const804 SInt64 CAAudioFile::Tell() const // frameNumber
805 {
806 return mFrameMark - mFrame0Offset;
807 }
808
SeekToPacket(SInt64 packetNumber)809 void CAAudioFile::SeekToPacket(SInt64 packetNumber)
810 {
811 #if VERBOSE_IO
812 printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber);
813 #endif
814 XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file");
815 if (mPacketMark == packetNumber)
816 return; // already there! don't reset converter
817 mPacketMark = packetNumber;
818
819 mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset;
820 mFramesToSkipFollowingSeek = 0;
821 if (mConverter)
822 // must reset -- if we reached end of stream. converter will no longer work otherwise
823 AudioConverterReset(mConverter);
824 }
825
826 /*
827 Example: AAC, 1024 frames/packet, 2112 frame offset
828
829 2112
830 |
831 Absolute frames: 0 1024 2048 | 3072
832 +---------+---------+--|------+---------+---------+
833 Packets: | 0 | 1 | | 2 | 3 | 4 |
834 +---------+---------+--|------+---------+---------+
835 Client frames: -2112 -1088 -64 | 960 SeekToFrame, TellFrame
836 |
837 0
838
839 * Offset between absolute and client frames is mFrame0Offset.
840 *** mFrameMark is in client frames ***
841
842 Examples:
843 clientFrame 0 960 1000 1024
844 absoluteFrame 2112 3072 3112 3136
845 packet 0 0 0 1
846 tempFrameMark* -2112 -2112 -2112 -1088
847 mFramesToSkipFollowingSeek 2112 3072 3112 2112
848 */
Seek(SInt64 clientFrame)849 void CAAudioFile::Seek(SInt64 clientFrame)
850 {
851 if (clientFrame == mFrameMark)
852 return; // already there! don't reset converter
853
854 //SInt64 absoluteFrame = clientFrame + mFrame0Offset;
855 XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file");
856
857 #if VERBOSE_IO
858 SInt64 prevFrameMark = mFrameMark;
859 #endif
860
861 SInt64 packet;
862 packet = FrameToPacket(clientFrame);
863 if (packet < 0)
864 packet = 0;
865 SeekToPacket(packet);
866 // this will have backed up mFrameMark to match the beginning of the packet
867 mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0));
868 mFrameMark = clientFrame;
869
870 #if VERBOSE_IO
871 printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek);
872 #endif
873 }
874
875 // _______________________________________________________________________________________
876 //
Read(UInt32 & ioNumPackets,AudioBufferList * ioData)877 void CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData)
878 // May read fewer packets than requested if:
879 // buffer is not big enough
880 // file does not contain that many more packets
881 // Note that eofErr is not fatal, just results in 0 packets returned
882 // ioData's buffer sizes may be shortened
883 {
884 XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0");
885 if (mIOBufferList.mBuffers[0].mData == NULL) {
886 #if DEBUG
887 printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
888 #endif
889 AllocateBuffers();
890 }
891 UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize;
892 UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize;
893 // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check
894 UInt32 nPackets = std::min(ioNumPackets, maxNumPackets);
895
896 mMaxPacketsToRead = ~0UL;
897
898 if (mClientDataFormat.mFramesPerPacket == 1) { // PCM or equivalent
899 while (mFramesToSkipFollowingSeek > 0) {
900 UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets);
901 UInt32 framesPerPacket;
902 if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0)
903 mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket;
904
905 if (mConverter == NULL) {
906 XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file");
907 } else {
908 #if CAAUDIOFILE_PROFILE
909 mInConverter = true;
910 #endif
911 StartTiming(this, fill);
912 XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)");
913 ElapsedTime(this, fill, mTicksInConverter);
914 #if CAAUDIOFILE_PROFILE
915 mInConverter = false;
916 #endif
917 }
918 if (skipFrames == 0) { // hit EOF
919 ioNumPackets = 0;
920 return;
921 }
922 mFrameMark += skipFrames;
923 #if VERBOSE_IO
924 printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames);
925 #endif
926
927 mFramesToSkipFollowingSeek -= skipFrames;
928
929 // restore mDataByteSize
930 for (int i = ioData->mNumberBuffers; --i >= 0 ; )
931 ioData->mBuffers[i].mDataByteSize = bufferSizeBytes;
932 }
933 }
934
935 if (mFileDataFormat.mFramesPerPacket > 0)
936 // don't read more packets than we are being asked to produce
937 mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1;
938 if (mConverter == NULL) {
939 XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file");
940 } else {
941 #if CAAUDIOFILE_PROFILE
942 mInConverter = true;
943 #endif
944 StartTiming(this, fill);
945 XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)");
946 ElapsedTime(this, fill, mTicksInConverter);
947 #if CAAUDIOFILE_PROFILE
948 mInConverter = false;
949 #endif
950 }
951 if (mClientDataFormat.mFramesPerPacket == 1)
952 mFrameMark += nPackets;
953
954 ioNumPackets = nPackets;
955 }
956
957 // _______________________________________________________________________________________
958 //
ReadInputProc(AudioConverterRef inAudioConverter,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)959 OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter,
960 UInt32* ioNumberDataPackets,
961 AudioBufferList* ioData,
962 AudioStreamPacketDescription** outDataPacketDescription,
963 void* inUserData)
964 {
965 CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
966
967 #if 0
968 SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark;
969 if (remainingPacketsInFile <= 0) {
970 *ioNumberDataPackets = 0;
971 ioData->mBuffers[0].mDataByteSize = 0;
972 if (outDataPacketDescription)
973 *outDataPacketDescription = This->mPacketDescs;
974 #if VERBOSE_IO
975 printf("CAAudioFile::ReadInputProc: EOF\n");
976 #endif
977 return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes
978 }
979 #endif
980
981 // determine how much to read
982 AudioBufferList *readBuffer;
983 UInt32 readPackets;
984 if (inAudioConverter != NULL) {
985 // getting called from converter, need to use our I/O buffer
986 readBuffer = &This->mIOBufferList;
987 readPackets = This->mIOBufferSizePackets;
988 } else {
989 // getting called directly from ReadPackets, use supplied buffer
990 if (This->mFileMaxPacketSize == 0)
991 return kExtAudioFileError_MaxPacketSizeUnknown;
992 readBuffer = ioData;
993 readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize);
994 // don't attempt to read more packets than will fit in the buffer
995 }
996 // don't try to read past EOF
997 // if (readPackets > remainingPacketsInFile)
998 // readPackets = remainingPacketsInFile;
999 // don't read more packets than necessary to produce the requested amount of converted data
1000 if (readPackets > This->mMaxPacketsToRead) {
1001 #if VERBOSE_IO
1002 printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets);
1003 #endif
1004 readPackets = This->mMaxPacketsToRead;
1005 }
1006
1007 // read
1008 UInt32 bytesRead;
1009 OSStatus err;
1010
1011 StartTiming(This, read);
1012 StartTiming(This, readinconv);
1013 err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData);
1014 #if CAAUDIOFILE_PROFILE
1015 if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter);
1016 #endif
1017 ElapsedTime(This, read, This->mTicksInIO);
1018
1019 if (err) {
1020 DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err);
1021 return err;
1022 }
1023
1024 #if VERBOSE_IO
1025 printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err);
1026 #if VERBOSE_IO >= 2
1027 if (This->mPacketDescs) {
1028 for (UInt32 i = 0; i < readPackets; ++i) {
1029 printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize);
1030 }
1031 }
1032 printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4);
1033 #endif
1034 #endif
1035 if (readPackets == 0) {
1036 *ioNumberDataPackets = 0;
1037 ioData->mBuffers[0].mDataByteSize = 0;
1038 return noErr;
1039 }
1040
1041 if (outDataPacketDescription)
1042 *outDataPacketDescription = This->mPacketDescs;
1043 ioData->mBuffers[0].mDataByteSize = bytesRead;
1044 ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData;
1045
1046 This->mPacketMark += readPackets;
1047 if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read
1048 // but for non-PCM client format (weird case) we must update here/now
1049 if (This->mFileDataFormat.mFramesPerPacket > 0)
1050 This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket;
1051 else {
1052 for (UInt32 i = 0; i < readPackets; ++i)
1053 This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket;
1054 }
1055 }
1056 *ioNumberDataPackets = readPackets;
1057 return noErr;
1058 }
1059
1060 // _______________________________________________________________________________________
1061 //
Write(UInt32 numPackets,const AudioBufferList * data)1062 void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data)
1063 {
1064 if (mIOBufferList.mBuffers[0].mData == NULL) {
1065 #if DEBUG
1066 printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
1067 #endif
1068 AllocateBuffers();
1069 }
1070
1071 if (mMode == kPreparingToWrite)
1072 mMode = kWriting;
1073 else
1074 XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file");
1075 if (mConverter != NULL) {
1076 mWritePackets = numPackets;
1077 mWriteBufferList->SetFrom(data);
1078 WritePacketsFromCallback(WriteInputProc, this);
1079 } else {
1080 StartTiming(this, write);
1081 XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize,
1082 NULL, mPacketMark, &numPackets, data->mBuffers[0].mData),
1083 "write audio file");
1084 ElapsedTime(this, write, mTicksInIO);
1085 #if VERBOSE_IO
1086 printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize);
1087 #endif
1088 //mNumberPackets =
1089 mPacketMark += numPackets;
1090 if (mFileDataFormat.mFramesPerPacket > 0)
1091 mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket;
1092 // else: shouldn't happen since we're only called when there's no converter
1093 }
1094 }
1095
1096 // _______________________________________________________________________________________
1097 //
FlushEncoder()1098 void CAAudioFile::FlushEncoder()
1099 {
1100 if (mConverter != NULL) {
1101 mFinishingEncoding = true;
1102 WritePacketsFromCallback(WriteInputProc, this);
1103 mFinishingEncoding = false;
1104
1105 // get priming info from converter, set it on the file
1106 if (mFileDataFormat.mBitsPerChannel == 0) {
1107 UInt32 propertySize;
1108 OSStatus err;
1109 AudioConverterPrimeInfo primeInfo;
1110 propertySize = sizeof(primeInfo);
1111
1112 err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
1113 if (err == noErr) {
1114 AudioFilePacketTableInfo pti;
1115 propertySize = sizeof(pti);
1116 err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
1117 if (err == noErr) {
1118 //printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
1119 UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames;
1120 pti.mPrimingFrames = primeInfo.leadingFrames;
1121 pti.mRemainderFrames = primeInfo.trailingFrames;
1122 pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames;
1123 //printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
1124 XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file");
1125 }
1126 }
1127 }
1128 }
1129 }
1130
1131 // _______________________________________________________________________________________
1132 //
WriteInputProc(AudioConverterRef,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)1133 OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/,
1134 UInt32 * ioNumberDataPackets,
1135 AudioBufferList* ioData,
1136 AudioStreamPacketDescription ** outDataPacketDescription,
1137 void* inUserData)
1138 {
1139 CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
1140 if (This->mFinishingEncoding) {
1141 *ioNumberDataPackets = 0;
1142 ioData->mBuffers[0].mDataByteSize = 0;
1143 ioData->mBuffers[0].mData = NULL;
1144 if (outDataPacketDescription)
1145 *outDataPacketDescription = NULL;
1146 return noErr;
1147 }
1148 UInt32 numPackets = This->mWritePackets;
1149 if (numPackets == 0) {
1150 return kNoMoreInputRightNow;
1151 }
1152 This->mWriteBufferList->ToAudioBufferList(ioData);
1153 This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame);
1154 *ioNumberDataPackets = numPackets;
1155 if (outDataPacketDescription)
1156 *outDataPacketDescription = NULL;
1157 This->mWritePackets -= numPackets;
1158 return noErr;
1159 }
1160
1161 // _______________________________________________________________________________________
1162 //
1163 #if VERBOSE_IO
hexdump(const void * addr,long len)1164 static void hexdump(const void *addr, long len)
1165 {
1166 const Byte *p = (Byte *)addr;
1167 UInt32 offset = 0;
1168
1169 if (len > 0x400) len = 0x400;
1170
1171 while (len > 0) {
1172 int n = len > 16 ? 16 : len;
1173 printf("%08lX: ", offset);
1174 for (int i = 0; i < 16; ++i)
1175 if (i < n)
1176 printf("%02X ", p[i]);
1177 else printf(" ");
1178 for (int i = 0; i < 16; ++i)
1179 if (i < n)
1180 putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.');
1181 else putchar(' ');
1182 putchar('\n');
1183 p += 16;
1184 len -= 16;
1185 offset += 16;
1186 }
1187 }
1188 #endif
1189
1190 // _______________________________________________________________________________________
1191 //
WritePacketsFromCallback(AudioConverterComplexInputDataProc inInputDataProc,void * inInputDataProcUserData)1192 void CAAudioFile::WritePacketsFromCallback(
1193 AudioConverterComplexInputDataProc inInputDataProc,
1194 void * inInputDataProcUserData)
1195 {
1196 while (true) {
1197 // keep writing until we exhaust the input (temporary stop), or produce no output (EOF)
1198 UInt32 numEncodedPackets = mIOBufferSizePackets;
1199 mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes;
1200 #if CAAUDIOFILE_PROFILE
1201 mInConverter = true;
1202 #endif
1203 StartTiming(this, fill);
1204 OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData,
1205 &numEncodedPackets, &mIOBufferList, mPacketDescs);
1206 ElapsedTime(this, fill, mTicksInConverter);
1207 #if CAAUDIOFILE_PROFILE
1208 mInConverter = false;
1209 #endif
1210 XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)");
1211 if (numEncodedPackets == 0)
1212 break;
1213 Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData;
1214 #if VERBOSE_IO
1215 printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize);
1216 if (mPacketDescs) {
1217 for (UInt32 i = 0; i < numEncodedPackets; ++i) {
1218 printf(" write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
1219 #if VERBOSE_IO >= 2
1220 hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
1221 #endif
1222 }
1223 }
1224 #endif
1225 StartTiming(this, write);
1226 XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file");
1227 ElapsedTime(this, write, mTicksInIO);
1228 mPacketMark += numEncodedPackets;
1229 //mNumberPackets += numEncodedPackets;
1230 if (mFileDataFormat.mFramesPerPacket > 0)
1231 mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket;
1232 else {
1233 for (UInt32 i = 0; i < numEncodedPackets; ++i)
1234 mFrameMark += mPacketDescs[i].mVariableFramesInPacket;
1235 }
1236 if (err == kNoMoreInputRightNow)
1237 break;
1238 }
1239 }
1240
1241 #endif // !CAAF_USE_EXTAUDIOFILE
1242