1 /* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved.
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.
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.
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.
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.
38 /*=============================================================================
41 =============================================================================*/
43 #include "CAAudioFile.h"
45 #if !CAAF_USE_EXTAUDIOFILE
47 #include "CAXException.h"
49 #include "CAHostTimeBase.h"
50 #include "CADebugMacros.h"
52 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
53 #include <AudioToolbox/AudioToolbox.h>
55 #include <AudioFormat.h>
59 //#define VERBOSE_IO 1
60 //#define VERBOSE_CONVERTER 1
61 //#define VERBOSE_CHANNELMAP 1
62 //#define LOG_FUNCTION_ENTRIES 1
64 #if VERBOSE_CHANNELMAP
65 #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility
69 #if LOG_FUNCTION_ENTRIES
70 class FunctionLogger
{
72 FunctionLogger(const char *name
, const char *fmt
=NULL
, ...) : mName(name
) {
74 printf("-> %s ", name
);
87 printf("<- %s\n", mName
);
92 static void Indent() {
93 for (int i
= sIndent
; --i
>= 0; ) {
94 putchar(' '); putchar(' ');
101 int FunctionLogger::sIndent
= 0;
103 #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
105 #define LOG_FUNCTION(name, format, foo)
108 static const UInt32 kDefaultIOBufferSizeBytes
= 0x10000;
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)
114 #define StartTiming(af, starttime)
115 #define ElapsedTime(af, starttime, counter)
118 #define kNoMoreInputRightNow 'nein'
120 // _______________________________________________________________________________________
122 CAAudioFile::CAAudioFile() :
125 mFinishingEncoding(false),
128 mFramesToSkipFollowingSeek(0),
130 mClientOwnsIOBuffer(false),
135 mWriteBufferList(NULL
)
136 #if CAAUDIOFILE_PROFILE
139 mTicksInConverter(0),
140 mTicksInReadInConverter(0),
145 mIOBufferList
.mBuffers
[0].mData
= NULL
;
146 mIOBufferList
.mBuffers
[0].mDataByteSize
= 0;
147 mClientMaxPacketSize
= 0;
148 mIOBufferSizeBytes
= kDefaultIOBufferSizeBytes
;
151 // _______________________________________________________________________________________
153 CAAudioFile::~CAAudioFile()
158 // _______________________________________________________________________________________
160 void CAAudioFile::Close()
162 LOG_FUNCTION("CAAudioFile::Close", NULL
, NULL
);
163 if (mMode
== kClosed
)
165 if (mMode
== kWriting
)
168 if (mAudioFile
!= 0 && mOwnOpenFile
) {
169 AudioFileClose(mAudioFile
);
172 if (!mClientOwnsIOBuffer
) {
173 delete[] (Byte
*)mIOBufferList
.mBuffers
[0].mData
;
174 mIOBufferList
.mBuffers
[0].mData
= NULL
;
175 mIOBufferList
.mBuffers
[0].mDataByteSize
= 0;
177 delete[] mPacketDescs
; mPacketDescs
= NULL
; mNumPacketDescs
= 0;
178 delete[] mMagicCookie
; mMagicCookie
= NULL
;
179 delete mWriteBufferList
; mWriteBufferList
= NULL
;
183 // _______________________________________________________________________________________
185 void CAAudioFile::CloseConverter()
188 #if VERBOSE_CONVERTER
189 printf("CAAudioFile %p : CloseConverter\n", this);
191 AudioConverterDispose(mConverter
);
196 // =======================================================================================
198 // _______________________________________________________________________________________
200 void CAAudioFile::Open(const FSRef
&fsref
)
202 LOG_FUNCTION("CAAudioFile::Open", "%p", this);
203 XThrowIf(mMode
!= kClosed
, kExtAudioFileError_InvalidOperationOrder
, "file already open");
205 XThrowIfError(AudioFileOpen(&mFSRef
, fsRdPerm
, 0, &mAudioFile
), "open audio file");
208 GetExistingFileInfo();
211 // _______________________________________________________________________________________
213 void CAAudioFile::Wrap(AudioFileID fileID
, bool forWriting
)
215 LOG_FUNCTION("CAAudioFile::Wrap", "%p", this);
216 XThrowIf(mMode
!= kClosed
, kExtAudioFileError_InvalidOperationOrder
, "file already open");
219 mOwnOpenFile
= false;
220 mMode
= forWriting
? kPreparingToWrite
: kReading
;
221 GetExistingFileInfo();
226 // _______________________________________________________________________________________
228 void CAAudioFile::CreateNew(const FSRef
&parentDir
, CFStringRef filename
, AudioFileTypeID filetype
, const AudioStreamBasicDescription
&dataFormat
, const AudioChannelLayout
*layout
)
230 LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this);
231 XThrowIf(mMode
!= kClosed
, kExtAudioFileError_InvalidOperationOrder
, "file already open");
233 mFileDataFormat
= dataFormat
;
235 mFileChannelLayout
= layout
;
236 #if VERBOSE_CHANNELMAP
237 printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout
.Tag()));
240 mMode
= kPreparingToCreate
;
241 FileFormatChanged(&parentDir
, filename
, filetype
);
244 // _______________________________________________________________________________________
246 // called to create the file -- or update its format/channel layout/properties based on an encoder
248 void CAAudioFile::FileFormatChanged(const FSRef
*parentDir
, CFStringRef filename
, AudioFileTypeID filetype
)
250 LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this);
251 XThrowIf(mMode
!= kPreparingToCreate
&& mMode
!= kPreparingToWrite
, kExtAudioFileError_InvalidOperationOrder
, "new file not prepared");
255 AudioStreamBasicDescription saveFileDataFormat
= mFileDataFormat
;
257 #if VERBOSE_CONVERTER
258 mFileDataFormat
.PrintFormat(stdout
, "", "Specified file data format");
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");
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
);
280 XThrow(err
, "couldn't get audio converter's output channel layout");
282 mFileChannelLayout
= layout
;
283 #if VERBOSE_CHANNELMAP
284 printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout
.Tag()));
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");
298 XThrowIfError(AudioFileCreate(parentDir
, filename
, filetype
, &newFileDataFormat
, 0, &mFSRef
, &mAudioFile
), "create audio file");
299 mMode
= kPreparingToWrite
;
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");
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");
312 UInt32 deferSizeUpdates
= 1;
313 err
= AudioFileSetProperty(mAudioFile
, kAudioFilePropertyDeferSizeUpdates
, sizeof(UInt32
), &deferSizeUpdates
);
315 if (mConverter
!= NULL
) {
317 // get the magic cookie, if any, from the converter
318 delete[] mMagicCookie
; mMagicCookie
= NULL
;
319 mMagicCookieSize
= 0;
321 err
= AudioConverterGetPropertyInfo(mConverter
, kAudioConverterCompressionMagicCookie
, &propertySize
, NULL
);
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");
338 XThrowIfError(AudioFileSetProperty(mAudioFile
, kAudioFilePropertyMagicCookieData
, mMagicCookieSize
, mMagicCookie
), "set audio file's magic cookie");
342 // get maximum packet size
343 propertySize
= sizeof(UInt32
);
344 XThrowIfError(AudioConverterGetProperty(mConverter
, kAudioConverterPropertyMaximumOutputPacketSize
, &propertySize
, &mFileMaxPacketSize
), "get audio converter's maximum output packet size");
346 AllocateBuffers(true /* okToFail */);
348 InitFileMaxPacketSize();
351 if (mFileChannelLayout
.IsValid() && mFileChannelLayout
.NumberChannels() > 2) {
352 // don't bother tagging mono/stereo files
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()));
359 err
= AudioFileSetProperty(mAudioFile
, kAudioFilePropertyChannelLayout
,
360 mFileChannelLayout
.Size(), &mFileChannelLayout
.Layout());
362 CAXException::Warning("could not set the file's channel layout", err
);
364 #if VERBOSE_CHANNELMAP
365 printf("file won't accept a channel layout (write)\n");
370 UpdateClientMaxPacketSize(); // also sets mFrame0Offset
375 // _______________________________________________________________________________________
377 void CAAudioFile::InitFileMaxPacketSize()
379 LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this);
380 UInt32 propertySize
= sizeof(UInt32
);
381 OSStatus err
= AudioFileGetProperty(mAudioFile
, kAudioFilePropertyMaximumPacketSize
,
382 &propertySize
, &mFileMaxPacketSize
);
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
;
389 AllocateBuffers(true /* okToFail */);
393 // _______________________________________________________________________________________
395 SInt64
CAAudioFile::FileDataOffset()
397 if (mFileDataOffset
< 0) {
398 UInt32 propertySize
= sizeof(SInt64
);
399 XThrowIfError(AudioFileGetProperty(mAudioFile
, kAudioFilePropertyDataOffset
, &propertySize
, &mFileDataOffset
), "couldn't get file's data offset");
401 return mFileDataOffset
;
404 // _______________________________________________________________________________________
406 SInt64
CAAudioFile::GetNumberFrames() const
408 AudioFilePacketTableInfo pti
;
409 UInt32 propertySize
= sizeof(pti
);
410 OSStatus err
= AudioFileGetProperty(mAudioFile
, kAudioFilePropertyPacketTableInfo
, &propertySize
, &pti
);
412 return pti
.mNumberValidFrames
;
413 return mFileDataFormat
.mFramesPerPacket
* GetNumberPackets() - mFrame0Offset
;
416 // _______________________________________________________________________________________
418 void CAAudioFile::SetNumberFrames(SInt64 nFrames
)
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");
424 // _______________________________________________________________________________________
426 // call for existing file, NOT new one - from Open() or Wrap()
427 void CAAudioFile::GetExistingFileInfo()
429 LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this);
433 // get mFileDataFormat
434 propertySize
= sizeof(AudioStreamBasicDescription
);
435 XThrowIfError(AudioFileGetProperty(mAudioFile
, kAudioFilePropertyDataFormat
, &propertySize
, &mFileDataFormat
), "get audio file's data format");
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
);
443 mFileChannelLayout
= layout
;
444 #if VERBOSE_CHANNELMAP
445 printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout
.Tag()));
449 XThrowIfError(err
, "get audio file's channel layout");
451 if (mMode
!= kReading
)
455 // get mNumberPackets
456 propertySize
= sizeof(mNumberPackets
);
457 XThrowIfError(AudioFileGetProperty(mAudioFile
, kAudioFilePropertyAudioDataPacketCount
, &propertySize
, &mNumberPackets
), "get audio file's packet count");
459 printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets
);
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");
470 InitFileMaxPacketSize();
474 UpdateClientMaxPacketSize();
477 // =======================================================================================
479 // _______________________________________________________________________________________
481 void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout
&layout
)
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()));
488 if (mMode
!= kReading
)
492 // _______________________________________________________________________________________
494 void CAAudioFile::SetClientFormat(const CAStreamBasicDescription
&dataFormat
, const CAAudioChannelLayout
*layout
)
496 LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this);
497 XThrowIf(!dataFormat
.IsPCM(), kExtAudioFileError_NonPCMClientFormat
, "non-PCM client format on audio file");
499 bool dataFormatChanging
= (mClientDataFormat
.mFormatID
== 0 || mClientDataFormat
!= dataFormat
);
501 if (dataFormatChanging
) {
503 if (mWriteBufferList
) {
504 delete mWriteBufferList
;
505 mWriteBufferList
= NULL
;
507 mClientDataFormat
= dataFormat
;
510 if (layout
&& layout
->IsValid()) {
511 XThrowIf(layout
->NumberChannels() != mClientDataFormat
.NumberChannels(), kExtAudioFileError_InvalidChannelMap
, "inappropriate channel map");
512 mClientChannelLayout
= *layout
;
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");
523 differentLayouts
= false;
524 #if VERBOSE_CHANNELMAP
525 printf("valid client layout, unknown file layout\n");
529 differentLayouts
= false;
530 #if VERBOSE_CHANNELMAP
531 if (mFileChannelLayout
.IsValid())
532 printf("valid file layout, unknown client layout\n");
534 printf("two invalid layouts\n");
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: ");
545 if (mConverter
== NULL
)
546 XThrowIfError(AudioConverterNew(&mFileDataFormat
, &mClientDataFormat
, &mConverter
),
547 "create audio converter");
549 #if VERBOSE_CONVERTER
550 printf("CAAudioFile %p -- created converter\n", this);
553 // set the magic cookie, if any (for decode)
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
559 SetConverterChannelLayout(false, mFileChannelLayout
);
560 SetConverterChannelLayout(true, mClientChannelLayout
);
562 // propagate leading/trailing frame counts
563 if (mFileDataFormat
.mBitsPerChannel
== 0) {
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");
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
)
588 XThrowIfError(kExtAudioFileError_InvalidOperationOrder
, "audio file format not yet known");
590 UpdateClientMaxPacketSize();
593 // _______________________________________________________________________________________
595 OSStatus
CAAudioFile::SetConverterProperty(
596 AudioConverterPropertyID inPropertyID
,
597 UInt32 inPropertyDataSize
,
598 const void* inPropertyData
,
601 OSStatus err
= noErr
;
602 //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
603 if (inPropertyID
== kAudioConverterPropertySettings
&& *(CFPropertyListRef
*)inPropertyData
== NULL
)
606 err
= AudioConverterSetProperty(mConverter
, inPropertyID
, inPropertyDataSize
, inPropertyData
);
608 XThrowIfError(err
, "set audio converter property");
611 UpdateClientMaxPacketSize();
612 if (mMode
== kPreparingToWrite
)
617 // _______________________________________________________________________________________
619 void CAAudioFile::SetConverterChannelLayout(bool output
, const CAAudioChannelLayout
&layout
)
621 LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
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()));
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");
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");
638 if (mMode
== kPreparingToWrite
)
643 // _______________________________________________________________________________________
645 CFArrayRef
CAAudioFile::GetConverterConfig()
648 UInt32 propertySize
= sizeof(plist
);
649 XThrowIfError(AudioConverterGetProperty(mConverter
, kAudioConverterPropertySettings
, &propertySize
, &plist
), "get converter property settings");
653 // _______________________________________________________________________________________
655 void CAAudioFile::UpdateClientMaxPacketSize()
657 LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
659 if (mConverter
!= NULL
) {
660 AudioConverterPropertyID property
= (mMode
== kReading
) ?
661 kAudioConverterPropertyMaximumOutputPacketSize
:
662 kAudioConverterPropertyMaximumInputPacketSize
;
664 UInt32 propertySize
= sizeof(UInt32
);
665 XThrowIfError(AudioConverterGetProperty(mConverter
, property
, &propertySize
, &mClientMaxPacketSize
),
666 "get audio converter's maximum packet size");
668 if (mFileDataFormat
.mBitsPerChannel
== 0) {
669 AudioConverterPrimeInfo primeInfo
;
670 propertySize
= sizeof(primeInfo
);
671 OSStatus err
= AudioConverterGetProperty(mConverter
, kAudioConverterPrimeInfo
, &propertySize
, &primeInfo
);
673 mFrame0Offset
= primeInfo
.leadingFrames
;
674 #if VERBOSE_CONVERTER
675 printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err
, mFrame0Offset
);
679 mClientMaxPacketSize
= mFileMaxPacketSize
;
683 // _______________________________________________________________________________________
684 // Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs
685 // Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes
686 void CAAudioFile::AllocateBuffers(bool okToFail
)
688 LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this);
689 if (mFileMaxPacketSize
== 0) {
692 XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown
, "file's maximum packet size is 0");
694 UInt32 bufferSizeBytes
= mIOBufferSizeBytes
= std::max(mIOBufferSizeBytes
, mFileMaxPacketSize
);
695 // must be big enough for at least one maximum size packet
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
];
705 mIOBufferList
.mBuffers
[0].mDataByteSize
= bufferSizeBytes
;
706 mIOBufferSizePackets
= bufferSizeBytes
/ mFileMaxPacketSize
;
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
;
719 if (externallyFramed
) {
720 //printf("reallocating packet descs\n");
721 mPacketDescs
= new AudioStreamPacketDescription
[mIOBufferSizePackets
];
722 mNumPacketDescs
= mIOBufferSizePackets
;
727 // _______________________________________________________________________________________
729 void CAAudioFile::SetIOBuffer(void *buf
)
731 if (!mClientOwnsIOBuffer
)
732 delete[] (Byte
*)mIOBufferList
.mBuffers
[0].mData
;
733 mIOBufferList
.mBuffers
[0].mData
= buf
;
736 mClientOwnsIOBuffer
= false;
737 SetIOBufferSizeBytes(mIOBufferSizeBytes
);
739 mClientOwnsIOBuffer
= true;
742 // printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
745 // ===============================================================================
749 added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket.
750 You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty.
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.
757 struct AudioFramePacketTranslation
761 UInt32 mFrameOffsetInPacket;
765 SInt64
CAAudioFile::PacketToFrame(SInt64 packet
) const
767 AudioFramePacketTranslation trans
;
770 switch (mFileDataFormat
.mFramesPerPacket
) {
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");
780 return packet
* mFileDataFormat
.mFramesPerPacket
;
783 SInt64
CAAudioFile::FrameToPacket(SInt64 inFrame
) const
785 AudioFramePacketTranslation trans
;
788 switch (mFileDataFormat
.mFramesPerPacket
) {
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
;
798 return inFrame
/ mFileDataFormat
.mFramesPerPacket
;
801 // _______________________________________________________________________________________
804 SInt64
CAAudioFile::Tell() const // frameNumber
806 return mFrameMark
- mFrame0Offset
;
809 void CAAudioFile::SeekToPacket(SInt64 packetNumber
)
812 printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber
);
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
;
819 mFrameMark
= PacketToFrame(packetNumber
) - mFrame0Offset
;
820 mFramesToSkipFollowingSeek
= 0;
822 // must reset -- if we reached end of stream. converter will no longer work otherwise
823 AudioConverterReset(mConverter
);
827 Example: AAC, 1024 frames/packet, 2112 frame offset
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
839 * Offset between absolute and client frames is mFrame0Offset.
840 *** mFrameMark is in client frames ***
843 clientFrame 0 960 1000 1024
844 absoluteFrame 2112 3072 3112 3136
846 tempFrameMark* -2112 -2112 -2112 -1088
847 mFramesToSkipFollowingSeek 2112 3072 3112 2112
849 void CAAudioFile::Seek(SInt64 clientFrame
)
851 if (clientFrame
== mFrameMark
)
852 return; // already there! don't reset converter
854 //SInt64 absoluteFrame = clientFrame + mFrame0Offset;
855 XThrowIf(mMode
!= kReading
|| clientFrame
< 0 || !mClientDataFormat
.IsPCM(), kExtAudioFileError_InvalidSeek
, "seek to frame in audio file");
858 SInt64 prevFrameMark
= mFrameMark
;
862 packet
= FrameToPacket(clientFrame
);
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
;
871 printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark
, prevFrameMark
, packet
, mFramesToSkipFollowingSeek
);
875 // _______________________________________________________________________________________
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
884 XThrowIf(mClientMaxPacketSize
== 0, kExtAudioFileError_MaxPacketSizeUnknown
, "client maximum packet size is 0");
885 if (mIOBufferList
.mBuffers
[0].mData
== NULL
) {
887 printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
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
);
896 mMaxPacketsToRead
= ~0UL;
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
;
905 if (mConverter
== NULL
) {
906 XThrowIfError(ReadInputProc(NULL
, &skipFrames
, ioData
, NULL
, this), "read audio file");
908 #if CAAUDIOFILE_PROFILE
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;
918 if (skipFrames
== 0) { // hit EOF
922 mFrameMark
+= skipFrames
;
924 printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames
);
927 mFramesToSkipFollowingSeek
-= skipFrames
;
929 // restore mDataByteSize
930 for (int i
= ioData
->mNumberBuffers
; --i
>= 0 ; )
931 ioData
->mBuffers
[i
].mDataByteSize
= bufferSizeBytes
;
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");
941 #if CAAUDIOFILE_PROFILE
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;
951 if (mClientDataFormat
.mFramesPerPacket
== 1)
952 mFrameMark
+= nPackets
;
954 ioNumPackets
= nPackets
;
957 // _______________________________________________________________________________________
959 OSStatus
CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter
,
960 UInt32
* ioNumberDataPackets
,
961 AudioBufferList
* ioData
,
962 AudioStreamPacketDescription
** outDataPacketDescription
,
965 CAAudioFile
*This
= static_cast<CAAudioFile
*>(inUserData
);
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
;
975 printf("CAAudioFile::ReadInputProc: EOF\n");
977 return noErr
; // not eofErr; EOF is signified by 0 packets/0 bytes
981 // determine how much to read
982 AudioBufferList
*readBuffer
;
984 if (inAudioConverter
!= NULL
) {
985 // getting called from converter, need to use our I/O buffer
986 readBuffer
= &This
->mIOBufferList
;
987 readPackets
= This
->mIOBufferSizePackets
;
989 // getting called directly from ReadPackets, use supplied buffer
990 if (This
->mFileMaxPacketSize
== 0)
991 return kExtAudioFileError_MaxPacketSizeUnknown
;
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
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
) {
1002 printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This
->mMaxPacketsToRead
, readPackets
);
1004 readPackets
= This
->mMaxPacketsToRead
;
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
);
1017 ElapsedTime(This
, read
, This
->mTicksInIO
);
1020 DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err
);
1025 printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets
, This
->mPacketMark
, This
->mPacketMark
+ readPackets
, bytesRead
, err
);
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
);
1032 printf(" read buffer:"); CAShowAudioBufferList(readBuffer
, 0, 4);
1035 if (readPackets
== 0) {
1036 *ioNumberDataPackets
= 0;
1037 ioData
->mBuffers
[0].mDataByteSize
= 0;
1041 if (outDataPacketDescription
)
1042 *outDataPacketDescription
= This
->mPacketDescs
;
1043 ioData
->mBuffers
[0].mDataByteSize
= bytesRead
;
1044 ioData
->mBuffers
[0].mData
= readBuffer
->mBuffers
[0].mData
;
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
;
1052 for (UInt32 i
= 0; i
< readPackets
; ++i
)
1053 This
->mFrameMark
+= This
->mPacketDescs
[i
].mVariableFramesInPacket
;
1056 *ioNumberDataPackets
= readPackets
;
1060 // _______________________________________________________________________________________
1062 void CAAudioFile::Write(UInt32 numPackets
, const AudioBufferList
*data
)
1064 if (mIOBufferList
.mBuffers
[0].mData
== NULL
) {
1066 printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
1071 if (mMode
== kPreparingToWrite
)
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);
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
);
1086 printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets
, mPacketMark
, data
->mBuffers
[0].mDataByteSize
);
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
1096 // _______________________________________________________________________________________
1098 void CAAudioFile::FlushEncoder()
1100 if (mConverter
!= NULL
) {
1101 mFinishingEncoding
= true;
1102 WritePacketsFromCallback(WriteInputProc
, this);
1103 mFinishingEncoding
= false;
1105 // get priming info from converter, set it on the file
1106 if (mFileDataFormat
.mBitsPerChannel
== 0) {
1107 UInt32 propertySize
;
1109 AudioConverterPrimeInfo primeInfo
;
1110 propertySize
= sizeof(primeInfo
);
1112 err
= AudioConverterGetProperty(mConverter
, kAudioConverterPrimeInfo
, &propertySize
, &primeInfo
);
1114 AudioFilePacketTableInfo pti
;
1115 propertySize
= sizeof(pti
);
1116 err
= AudioFileGetProperty(mAudioFile
, kAudioFilePropertyPacketTableInfo
, &propertySize
, &pti
);
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");
1131 // _______________________________________________________________________________________
1133 OSStatus
CAAudioFile::WriteInputProc( AudioConverterRef
/*inAudioConverter*/,
1134 UInt32
* ioNumberDataPackets
,
1135 AudioBufferList
* ioData
,
1136 AudioStreamPacketDescription
** outDataPacketDescription
,
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
;
1148 UInt32 numPackets
= This
->mWritePackets
;
1149 if (numPackets
== 0) {
1150 return kNoMoreInputRightNow
;
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
;
1161 // _______________________________________________________________________________________
1164 static void hexdump(const void *addr
, long len
)
1166 const Byte
*p
= (Byte
*)addr
;
1169 if (len
> 0x400) len
= 0x400;
1172 int n
= len
> 16 ? 16 : len
;
1173 printf("%08lX: ", offset
);
1174 for (int i
= 0; i
< 16; ++i
)
1176 printf("%02X ", p
[i
]);
1178 for (int i
= 0; i
< 16; ++i
)
1180 putchar(p
[i
] >= ' ' && p
[i
] < 127 ? p
[i
] : '.');
1190 // _______________________________________________________________________________________
1192 void CAAudioFile::WritePacketsFromCallback(
1193 AudioConverterComplexInputDataProc inInputDataProc
,
1194 void * inInputDataProcUserData
)
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;
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;
1210 XThrowIf(err
!= 0 && err
!= kNoMoreInputRightNow
, err
, "convert audio packets (write)");
1211 if (numEncodedPackets
== 0)
1213 Byte
*buf
= (Byte
*)mIOBufferList
.mBuffers
[0].mData
;
1215 printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets
, mIOBufferList
.mBuffers
[0].mDataByteSize
);
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
);
1220 hexdump(buf
+ mPacketDescs
[i
].mStartOffset
, mPacketDescs
[i
].mDataByteSize
);
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
;
1233 for (UInt32 i
= 0; i
< numEncodedPackets
; ++i
)
1234 mFrameMark
+= mPacketDescs
[i
].mVariableFramesInPacket
;
1236 if (err
== kNoMoreInputRightNow
)
1241 #endif // !CAAF_USE_EXTAUDIOFILE