half-fix logic issue with solo-isolate controls
[ardour2.git] / libs / appleutility / CAAudioFile.cpp
blobe1e39b0ec909ab53d572a19b9864c8b2f3613ca7
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 /*=============================================================================
39 CAAudioFile.cpp
41 =============================================================================*/
43 #include "CAAudioFile.h"
45 #if !CAAF_USE_EXTAUDIOFILE
47 #include "CAXException.h"
48 #include <algorithm>
49 #include "CAHostTimeBase.h"
50 #include "CADebugMacros.h"
52 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
53 #include <AudioToolbox/AudioToolbox.h>
54 #else
55 #include <AudioFormat.h>
56 #endif
58 #if DEBUG
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
66 #endif
67 #endif
69 #if LOG_FUNCTION_ENTRIES
70 class FunctionLogger {
71 public:
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);
81 printf("\n");
82 ++sIndent;
84 ~FunctionLogger() {
85 --sIndent;
86 Indent();
87 printf("<- %s\n", mName);
88 if (sIndent == 0)
89 printf("\n");
92 static void Indent() {
93 for (int i = sIndent; --i >= 0; ) {
94 putchar(' '); putchar(' ');
98 const char *mName;
99 static int sIndent;
101 int FunctionLogger::sIndent = 0;
103 #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
104 #else
105 #define LOG_FUNCTION(name, format, foo)
106 #endif
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)
113 #else
114 #define StartTiming(af, starttime)
115 #define ElapsedTime(af, starttime, counter)
116 #endif
118 #define kNoMoreInputRightNow 'nein'
120 // _______________________________________________________________________________________
122 CAAudioFile::CAAudioFile() :
123 mAudioFile(0),
124 mUseCache(false),
125 mFinishingEncoding(false),
126 mMode(kClosed),
127 mFileDataOffset(-1),
128 mFramesToSkipFollowingSeek(0),
130 mClientOwnsIOBuffer(false),
131 mPacketDescs(NULL),
132 mNumPacketDescs(0),
133 mConverter(NULL),
134 mMagicCookie(NULL),
135 mWriteBufferList(NULL)
136 #if CAAUDIOFILE_PROFILE
138 mProfiling(false),
139 mTicksInConverter(0),
140 mTicksInReadInConverter(0),
141 mTicksInIO(0),
142 mInConverter(false)
143 #endif
145 mIOBufferList.mBuffers[0].mData = NULL;
146 mIOBufferList.mBuffers[0].mDataByteSize = 0;
147 mClientMaxPacketSize = 0;
148 mIOBufferSizeBytes = kDefaultIOBufferSizeBytes;
151 // _______________________________________________________________________________________
153 CAAudioFile::~CAAudioFile()
155 Close();
158 // _______________________________________________________________________________________
160 void CAAudioFile::Close()
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;
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;
180 mMode = kClosed;
183 // _______________________________________________________________________________________
185 void CAAudioFile::CloseConverter()
187 if (mConverter) {
188 #if VERBOSE_CONVERTER
189 printf("CAAudioFile %p : CloseConverter\n", this);
190 #endif
191 AudioConverterDispose(mConverter);
192 mConverter = NULL;
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");
204 mFSRef = fsref;
205 XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file");
206 mOwnOpenFile = true;
207 mMode = kReading;
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");
218 mAudioFile = fileID;
219 mOwnOpenFile = false;
220 mMode = forWriting ? kPreparingToWrite : kReading;
221 GetExistingFileInfo();
222 if (forWriting)
223 FileFormatChanged();
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;
234 if (layout) {
235 mFileChannelLayout = layout;
236 #if VERBOSE_CHANNELMAP
237 printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
238 #endif
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
247 // setting change
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");
253 UInt32 propertySize;
254 OSStatus err;
255 AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat;
257 #if VERBOSE_CONVERTER
258 mFileDataFormat.PrintFormat(stdout, "", "Specified file data format");
259 #endif
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");
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);
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");
312 UInt32 deferSizeUpdates = 1;
313 err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates);
315 if (mConverter != NULL) {
316 // encoder
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");
337 #endif
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 */);
347 } else {
348 InitFileMaxPacketSize();
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
370 UpdateClientMaxPacketSize(); // also sets mFrame0Offset
371 mPacketMark = 0;
372 mFrameMark = 0;
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);
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;
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);
411 if (err == noErr)
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);
430 UInt32 propertySize;
431 OSStatus err;
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);
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
448 free(layout);
449 XThrowIfError(err, "get audio file's channel layout");
451 if (mMode != kReading)
452 return;
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
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");
470 InitFileMaxPacketSize();
471 mPacketMark = 0;
472 mFrameMark = 0;
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()));
487 #endif
488 if (mMode != kReading)
489 FileFormatChanged();
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) {
502 CloseConverter();
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");
521 #endif
522 } else {
523 differentLayouts = false;
524 #if VERBOSE_CHANNELMAP
525 printf("valid client layout, unknown file layout\n");
526 #endif
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
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);
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
559 SetConverterChannelLayout(false, mFileChannelLayout);
560 SetConverterChannelLayout(true, mClientChannelLayout);
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");
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");
590 UpdateClientMaxPacketSize();
593 // _______________________________________________________________________________________
595 OSStatus CAAudioFile::SetConverterProperty(
596 AudioConverterPropertyID inPropertyID,
597 UInt32 inPropertyDataSize,
598 const void* inPropertyData,
599 bool inCanFail)
601 OSStatus err = noErr;
602 //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
603 if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL)
605 else {
606 err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData);
607 if (!inCanFail) {
608 XThrowIfError(err, "set audio converter property");
611 UpdateClientMaxPacketSize();
612 if (mMode == kPreparingToWrite)
613 FileFormatChanged();
614 return err;
617 // _______________________________________________________________________________________
619 void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout)
621 LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
622 OSStatus err;
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");
638 if (mMode == kPreparingToWrite)
639 FileFormatChanged();
643 // _______________________________________________________________________________________
645 CFArrayRef CAAudioFile::GetConverterConfig()
647 CFArrayRef plist;
648 UInt32 propertySize = sizeof(plist);
649 XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings");
650 return plist;
653 // _______________________________________________________________________________________
655 void CAAudioFile::UpdateClientMaxPacketSize()
657 LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
658 mFrame0Offset = 0;
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);
672 if (err == noErr)
673 mFrame0Offset = primeInfo.leadingFrames;
674 #if VERBOSE_CONVERTER
675 printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset);
676 #endif
678 } else {
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) {
690 if (okToFail)
691 return;
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;
716 mPacketDescs = NULL;
717 mNumPacketDescs = 0;
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;
735 if (buf == NULL) {
736 mClientOwnsIOBuffer = false;
737 SetIOBufferSizeBytes(mIOBufferSizeBytes);
738 } else {
739 mClientOwnsIOBuffer = true;
740 AllocateBuffers();
742 // printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
745 // ===============================================================================
748 For Tiger:
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
759 SInt64 mFrame;
760 SInt64 mPacket;
761 UInt32 mFrameOffsetInPacket;
765 SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const
767 AudioFramePacketTranslation trans;
768 UInt32 propertySize;
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;
780 return packet * mFileDataFormat.mFramesPerPacket;
783 SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const
785 AudioFramePacketTranslation trans;
786 UInt32 propertySize;
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;
798 return inFrame / mFileDataFormat.mFramesPerPacket;
801 // _______________________________________________________________________________________
804 SInt64 CAAudioFile::Tell() const // frameNumber
806 return mFrameMark - mFrame0Offset;
809 void CAAudioFile::SeekToPacket(SInt64 packetNumber)
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;
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);
827 Example: AAC, 1024 frames/packet, 2112 frame offset
829 2112
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 ***
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
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");
857 #if VERBOSE_IO
858 SInt64 prevFrameMark = mFrameMark;
859 #endif
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;
870 #if VERBOSE_IO
871 printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek);
872 #endif
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) {
886 #if DEBUG
887 printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
888 #endif
889 AllocateBuffers();
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");
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
918 if (skipFrames == 0) { // hit EOF
919 ioNumPackets = 0;
920 return;
922 mFrameMark += skipFrames;
923 #if VERBOSE_IO
924 printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames);
925 #endif
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");
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
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,
963 void* inUserData)
965 CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
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
979 #endif
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
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;
1007 // read
1008 UInt32 bytesRead;
1009 OSStatus err;
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);
1019 if (err) {
1020 DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err);
1021 return err;
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);
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;
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;
1051 else {
1052 for (UInt32 i = 0; i < readPackets; ++i)
1053 This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket;
1056 *ioNumberDataPackets = readPackets;
1057 return noErr;
1060 // _______________________________________________________________________________________
1062 void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data)
1064 if (mIOBufferList.mBuffers[0].mData == NULL) {
1065 #if DEBUG
1066 printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
1067 #endif
1068 AllocateBuffers();
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
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;
1108 OSStatus err;
1109 AudioConverterPrimeInfo primeInfo;
1110 propertySize = sizeof(primeInfo);
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");
1131 // _______________________________________________________________________________________
1133 OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/,
1134 UInt32 * ioNumberDataPackets,
1135 AudioBufferList* ioData,
1136 AudioStreamPacketDescription ** outDataPacketDescription,
1137 void* inUserData)
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;
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;
1158 return noErr;
1161 // _______________________________________________________________________________________
1163 #if VERBOSE_IO
1164 static void hexdump(const void *addr, long len)
1166 const Byte *p = (Byte *)addr;
1167 UInt32 offset = 0;
1169 if (len > 0x400) len = 0x400;
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;
1188 #endif
1190 // _______________________________________________________________________________________
1192 void CAAudioFile::WritePacketsFromCallback(
1193 AudioConverterComplexInputDataProc inInputDataProc,
1194 void * inInputDataProcUserData)
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
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;
1236 if (err == kNoMoreInputRightNow)
1237 break;
1241 #endif // !CAAF_USE_EXTAUDIOFILE