Logo Search packages:      
Sourcecode: ardour version File versions  Download package

CAAudioFile.cpp

/*    Copyright:  © Copyright 2005 Apple Computer, Inc. All rights reserved.

      Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                  ("Apple") in consideration of your agreement to the following terms, and your
                  use, installation, modification or redistribution of this Apple software
                  constitutes acceptance of these terms.  If you do not agree with these terms,
                  please do not use, install, modify or redistribute this Apple software.

                  In consideration of your agreement to abide by the following terms, and subject
                  to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
                  copyrights in this original Apple software (the "Apple Software"), to use,
                  reproduce, modify and redistribute the Apple Software, with or without
                  modifications, in source and/or binary forms; provided that if you redistribute
                  the Apple Software in its entirety and without modifications, you must retain
                  this notice and the following text and disclaimers in all such redistributions of
                  the Apple Software.  Neither the name, trademarks, service marks or logos of
                  Apple Computer, Inc. may be used to endorse or promote products derived from the
                  Apple Software without specific prior written permission from Apple.  Except as
                  expressly stated in this notice, no other rights or licenses, express or implied,
                  are granted by Apple herein, including but not limited to any patent rights that
                  may be infringed by your derivative works or by other works in which the Apple
                  Software may be incorporated.

                  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                  WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                  PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                  COMBINATION WITH YOUR PRODUCTS.

                  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                  ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                  OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                  (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*=============================================================================
      CAAudioFile.cpp
      
=============================================================================*/

#include "CAAudioFile.h"

#if !CAAF_USE_EXTAUDIOFILE

#include "CAXException.h"
#include <algorithm>
#include "CAHostTimeBase.h"
#include "CADebugMacros.h"

#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
      #include <AudioToolbox/AudioToolbox.h>
#else
      #include <AudioFormat.h>
#endif

#if DEBUG
      //#define VERBOSE_IO 1
      //#define VERBOSE_CONVERTER 1
      //#define VERBOSE_CHANNELMAP 1
      //#define LOG_FUNCTION_ENTRIES 1

      #if VERBOSE_CHANNELMAP
            #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility
      #endif
#endif

#if LOG_FUNCTION_ENTRIES
      class FunctionLogger {
      public:
            FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) {
                  Indent();
                  printf("-> %s ", name);
                  if (fmt) {
                        va_list args;
                        va_start(args, fmt);
                        vprintf(fmt, args);
                        va_end(args);
                  }
                  printf("\n");
                  ++sIndent;
            }
            ~FunctionLogger() {
                  --sIndent;
                  Indent();
                  printf("<- %s\n", mName);
                  if (sIndent == 0)
                        printf("\n");
            }
            
            static void Indent() {
                  for (int i = sIndent; --i >= 0; ) {
                        putchar(' '); putchar(' ');
                  }
            }
            
            const char *mName;
            static int sIndent;
      };
      int FunctionLogger::sIndent = 0;

      #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
#else
      #define LOG_FUNCTION(name, format, foo)
#endif

static const UInt32 kDefaultIOBufferSizeBytes = 0x10000;

#if CAAUDIOFILE_PROFILE
      #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0
      #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime)
#else
      #define StartTiming(af, starttime)
      #define ElapsedTime(af, starttime, counter)
#endif

#define kNoMoreInputRightNow 'nein'

// _______________________________________________________________________________________
//
CAAudioFile::CAAudioFile() :
      mAudioFile(0),
      mUseCache(false),
      mFinishingEncoding(false),
      mMode(kClosed),
      mFileDataOffset(-1),
      mFramesToSkipFollowingSeek(0),
      
      mClientOwnsIOBuffer(false),
      mPacketDescs(NULL),
      mNumPacketDescs(0),
      mConverter(NULL),
      mMagicCookie(NULL),
      mWriteBufferList(NULL)
#if CAAUDIOFILE_PROFILE
    ,
      mProfiling(false),
      mTicksInConverter(0),
      mTicksInReadInConverter(0),
      mTicksInIO(0),
      mInConverter(false)
#endif
{
      mIOBufferList.mBuffers[0].mData = NULL;
      mIOBufferList.mBuffers[0].mDataByteSize = 0;
      mClientMaxPacketSize = 0;
      mIOBufferSizeBytes = kDefaultIOBufferSizeBytes;
}

// _______________________________________________________________________________________
//
CAAudioFile::~CAAudioFile()
{
      Close();
}

// _______________________________________________________________________________________
//
void  CAAudioFile::Close()
{
      LOG_FUNCTION("CAAudioFile::Close", NULL, NULL);
      if (mMode == kClosed)
            return;
      if (mMode == kWriting)
            FlushEncoder();
      CloseConverter();
      if (mAudioFile != 0 && mOwnOpenFile) {
            AudioFileClose(mAudioFile);
            mAudioFile = 0;
      }
      if (!mClientOwnsIOBuffer) {
            delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
            mIOBufferList.mBuffers[0].mData = NULL;
            mIOBufferList.mBuffers[0].mDataByteSize = 0;
      }
      delete[] mPacketDescs;  mPacketDescs = NULL;    mNumPacketDescs = 0;
      delete[] mMagicCookie;  mMagicCookie = NULL;
      delete mWriteBufferList;      mWriteBufferList = NULL;
      mMode = kClosed;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::CloseConverter()
{
      if (mConverter) {
#if VERBOSE_CONVERTER
            printf("CAAudioFile %p : CloseConverter\n", this);
#endif
            AudioConverterDispose(mConverter);
            mConverter = NULL;
      }
}

// =======================================================================================

// _______________________________________________________________________________________
//
void  CAAudioFile::Open(const FSRef &fsref)
{
      LOG_FUNCTION("CAAudioFile::Open", "%p", this);
      XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
      mFSRef = fsref;
      XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file");
      mOwnOpenFile = true;
      mMode = kReading;
      GetExistingFileInfo();
}

// _______________________________________________________________________________________
//
void  CAAudioFile::Wrap(AudioFileID fileID, bool forWriting)
{
      LOG_FUNCTION("CAAudioFile::Wrap", "%p", this);
      XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");

      mAudioFile = fileID;
      mOwnOpenFile = false;
      mMode = forWriting ? kPreparingToWrite : kReading;
      GetExistingFileInfo();
      if (forWriting)
            FileFormatChanged();
}

// _______________________________________________________________________________________
//
void  CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout)
{
      LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this);
      XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
      
      mFileDataFormat = dataFormat;
      if (layout) {
            mFileChannelLayout = layout;
#if VERBOSE_CHANNELMAP
            printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
      }
      mMode = kPreparingToCreate;
      FileFormatChanged(&parentDir, filename, filetype);
}

// _______________________________________________________________________________________
//
// called to create the file -- or update its format/channel layout/properties based on an encoder 
// setting change
void  CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype)
{
      LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this);
      XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared");
      
      UInt32 propertySize;
      OSStatus err;
      AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat;
      
#if VERBOSE_CONVERTER
      mFileDataFormat.PrintFormat(stdout, "", "Specified file data format");
#endif
      
      // Find out the actual format the converter will produce. This is necessary in
      // case the bitrate has forced a lower sample rate, which needs to be set correctly
      // in the stream description passed to AudioFileCreate.
      if (mConverter != NULL) {
            propertySize = sizeof(AudioStreamBasicDescription);
            Float64 origSampleRate = mFileDataFormat.mSampleRate;
            XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description");
            // do the same for the channel layout being output by the converter
#if VERBOSE_CONVERTER
            mFileDataFormat.PrintFormat(stdout, "", "Converter output");
#endif
            if (fiszero(mFileDataFormat.mSampleRate))
                  mFileDataFormat.mSampleRate = origSampleRate;
            err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL);
            if (err == noErr && propertySize > 0) {
                  AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
                  err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout);
                  if (err) {
                        free(layout);
                        XThrow(err, "couldn't get audio converter's output channel layout");
                  }
                  mFileChannelLayout = layout;
#if VERBOSE_CHANNELMAP
                  printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
                  free(layout);
            }
      }
      
      // create the output file
      if (mMode == kPreparingToCreate) {
            CAStreamBasicDescription newFileDataFormat = mFileDataFormat;
            if (fiszero(newFileDataFormat.mSampleRate))
                  newFileDataFormat.mSampleRate = 44100;    // just make something up for now
#if VERBOSE_CONVERTER
            newFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
#endif
            XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file");
            mMode = kPreparingToWrite;
            mOwnOpenFile = true;
      } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) {
            // second check must be explicit since operator== on ASBD treats SR of zero as "don't care"
            if (fiszero(mFileDataFormat.mSampleRate))
                  mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate;
#if VERBOSE_CONVERTER
            mFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
#endif
            XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0");
            XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format");
      }

      UInt32 deferSizeUpdates = 1;
      err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates);

      if (mConverter != NULL) {
            // encoder
            // get the magic cookie, if any, from the converter         
            delete[] mMagicCookie;  mMagicCookie = NULL;
            mMagicCookieSize = 0;

            err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL);
            
            // we can get a noErr result and also a propertySize == 0
            // -- if the file format does support magic cookies, but this file doesn't have one.
            if (err == noErr && propertySize > 0) {
                  mMagicCookie = new Byte[propertySize];
                  XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie");
                  mMagicCookieSize = propertySize;    // the converter lies and tell us the wrong size
                  // now set the magic cookie on the output file
                  UInt32 willEatTheCookie = false;
                  // the converter wants to give us one; will the file take it?
                  err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData,
                              NULL, &willEatTheCookie);
                  if (err == noErr && willEatTheCookie) {
#if VERBOSE_CONVERTER
                        printf("Setting cookie on encoded file\n");
#endif
                        XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie");
                  }
            }
            
            // get maximum packet size
            propertySize = sizeof(UInt32);
            XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size");

            AllocateBuffers(true /* okToFail */);
      } else {
            InitFileMaxPacketSize();
      }
      
      if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) {
            // don't bother tagging mono/stereo files
            UInt32 isWritable;
            err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable);
            if (!err && isWritable) {
#if VERBOSE_CHANNELMAP
                  printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
                  err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, 
                        mFileChannelLayout.Size(), &mFileChannelLayout.Layout());
                  if (err)
                        CAXException::Warning("could not set the file's channel layout", err);
            } else {
#if VERBOSE_CHANNELMAP
                  printf("file won't accept a channel layout (write)\n");
#endif
            }
      }
      
      UpdateClientMaxPacketSize();  // also sets mFrame0Offset
      mPacketMark = 0;
      mFrameMark = 0;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::InitFileMaxPacketSize()
{
      LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this);
      UInt32 propertySize = sizeof(UInt32);
      OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize, 
            &propertySize, &mFileMaxPacketSize);
      if (err) {
            // workaround for 3361377: not all file formats' maximum packet sizes are supported
            if (!mFileDataFormat.IsPCM())
                  XThrowIfError(err, "get audio file's maximum packet size");
            mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame;
      }
      AllocateBuffers(true /* okToFail */);
}


// _______________________________________________________________________________________
//
SInt64  CAAudioFile::FileDataOffset()
{
      if (mFileDataOffset < 0) {
            UInt32 propertySize = sizeof(SInt64);
            XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset");
      }
      return mFileDataOffset;
}

// _______________________________________________________________________________________
//
SInt64  CAAudioFile::GetNumberFrames() const
{
      AudioFilePacketTableInfo pti;
      UInt32 propertySize = sizeof(pti);
      OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
      if (err == noErr)
            return pti.mNumberValidFrames;
      return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::SetNumberFrames(SInt64 nFrames)
{
      XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM");
      XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file");
}

// _______________________________________________________________________________________
//
// call for existing file, NOT new one - from Open() or Wrap()
void  CAAudioFile::GetExistingFileInfo()
{
      LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this);
      UInt32 propertySize;
      OSStatus err;
      
      // get mFileDataFormat
      propertySize = sizeof(AudioStreamBasicDescription);
      XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format");
      
      // get mFileChannelLayout
      err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL);
      if (err == noErr && propertySize > 0) {
            AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
            err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout);
            if (err == noErr) {
                  mFileChannelLayout = layout;
#if VERBOSE_CHANNELMAP
                  printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
            }
            free(layout);
            XThrowIfError(err, "get audio file's channel layout");
      }
      if (mMode != kReading)
            return;
      
#if 0
      // get mNumberPackets
      propertySize = sizeof(mNumberPackets);
      XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count");
#if VERBOSE_IO
      printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets);
#endif
#endif
      
      // get mMagicCookie
      err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
      if (err == noErr && propertySize > 0) {
            mMagicCookie = new Byte[propertySize];
            mMagicCookieSize = propertySize;
            XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie");
      }
      InitFileMaxPacketSize();
      mPacketMark = 0;
      mFrameMark = 0;
      
      UpdateClientMaxPacketSize();
}

// =======================================================================================

// _______________________________________________________________________________________
//
void  CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout)
{
      LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this);
      mFileChannelLayout = layout;
#if VERBOSE_CHANNELMAP
      printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
      if (mMode != kReading)
            FileFormatChanged();
}

// _______________________________________________________________________________________
//
void  CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
{
      LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this);
      XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file");
      
      bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat);
      
      if (dataFormatChanging) {
            CloseConverter();
            if (mWriteBufferList) {
                  delete mWriteBufferList;
                  mWriteBufferList = NULL;
            }
            mClientDataFormat = dataFormat;
      }
      
      if (layout && layout->IsValid()) {
            XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map");
            mClientChannelLayout = *layout;
      }
      
      bool differentLayouts;
      if (mClientChannelLayout.IsValid()) {
            if (mFileChannelLayout.IsValid()) {
                  differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag();
#if VERBOSE_CHANNELMAP
                  printf("two valid layouts, %s\n", differentLayouts ? "different" : "same");
#endif
            } else {
                  differentLayouts = false;
#if VERBOSE_CHANNELMAP
                  printf("valid client layout, unknown file layout\n");
#endif
            }
      } else {
            differentLayouts = false;
#if VERBOSE_CHANNELMAP
            if (mFileChannelLayout.IsValid())
                  printf("valid file layout, unknown client layout\n");
            else
                  printf("two invalid layouts\n");
#endif
      }
      
      if (mClientDataFormat != mFileDataFormat || differentLayouts) {
            // We need an AudioConverter.
            if (mMode == kReading) {
                  // file -> client (decode)
//mFileDataFormat.PrintFormat(  stdout, "", "File:   ");
//mClientDataFormat.PrintFormat(stdout, "", "Client: ");

                  if (mConverter == NULL)
                        XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter),
                        "create audio converter");
                  
#if VERBOSE_CONVERTER
                  printf("CAAudioFile %p -- created converter\n", this);
                  CAShow(mConverter);
#endif
                  // set the magic cookie, if any (for decode)
                  if (mMagicCookie)
                        SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM());
                              // we get cookies from some AIFF's but the converter barfs on them,
                              // so we set canFail to true for PCM

                  SetConverterChannelLayout(false, mFileChannelLayout);
                  SetConverterChannelLayout(true, mClientChannelLayout);
                  
                  // propagate leading/trailing frame counts
                  if (mFileDataFormat.mBitsPerChannel == 0) {
                        UInt32 propertySize;
                        OSStatus err;
                        AudioFilePacketTableInfo pti;
                        propertySize = sizeof(pti);
                        err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
                        if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) {
                              AudioConverterPrimeInfo primeInfo;
                              primeInfo.leadingFrames = pti.mPrimingFrames;
                              primeInfo.trailingFrames = pti.mRemainderFrames;
                              /* ignore any error. better to play it at all than not. */
                              /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo);
                              //XThrowIfError(err, "couldn't set prime info on converter");
                        }
                  }
            } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) {
                  // client -> file (encode)
                  if (mConverter == NULL)
                        XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter");
                  mWriteBufferList = CABufferList::New("", mClientDataFormat);
                  SetConverterChannelLayout(false, mClientChannelLayout);
                  SetConverterChannelLayout(true, mFileChannelLayout);
                  if (mMode == kPreparingToWrite)
                        FileFormatChanged();
            } else
                  XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known");
      }
      UpdateClientMaxPacketSize();
}

// _______________________________________________________________________________________
//
OSStatus    CAAudioFile::SetConverterProperty(  
                                                                  AudioConverterPropertyID      inPropertyID,
                                                                  UInt32                                    inPropertyDataSize,
                                                                  const void*                         inPropertyData,
                                                                  bool                                inCanFail)
{
      OSStatus err = noErr;
      //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
      if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL)
            ;
      else {
            err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData);
            if (!inCanFail) {
                  XThrowIfError(err, "set audio converter property");
            }
      }
      UpdateClientMaxPacketSize();
      if (mMode == kPreparingToWrite)
            FileFormatChanged();
      return err;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout)
{
      LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
      OSStatus err;
      
      if (layout.IsValid()) {
#if VERBOSE_CHANNELMAP
            printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input",
                  CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
#endif
            if (output) {
                  err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout,
                        layout.Size(), &layout.Layout());
                  XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout");
            } else {
                  err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout,
                        layout.Size(), &layout.Layout());
                  XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout");
            }
            if (mMode == kPreparingToWrite)
                  FileFormatChanged();
      }
}

// _______________________________________________________________________________________
//
CFArrayRef  CAAudioFile::GetConverterConfig()
{
      CFArrayRef plist;
      UInt32 propertySize = sizeof(plist);
      XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings");
      return plist;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::UpdateClientMaxPacketSize()
{
      LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
      mFrame0Offset = 0;
      if (mConverter != NULL) {
            AudioConverterPropertyID property = (mMode == kReading) ? 
                  kAudioConverterPropertyMaximumOutputPacketSize :
                  kAudioConverterPropertyMaximumInputPacketSize;
                  
            UInt32 propertySize = sizeof(UInt32);
            XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize),
                  "get audio converter's maximum packet size");
            
            if (mFileDataFormat.mBitsPerChannel == 0) {
                  AudioConverterPrimeInfo primeInfo;
                  propertySize = sizeof(primeInfo);
                  OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
                  if (err == noErr)
                        mFrame0Offset = primeInfo.leadingFrames;
#if VERBOSE_CONVERTER
                  printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset);
#endif
            }
      } else {
            mClientMaxPacketSize = mFileMaxPacketSize;
      }
}

// _______________________________________________________________________________________
//    Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs
//    Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes
void  CAAudioFile::AllocateBuffers(bool okToFail)
{
      LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this);
      if (mFileMaxPacketSize == 0) {
            if (okToFail)
                  return;
            XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0");
      }
      UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize);
            // must be big enough for at least one maximum size packet
      
      if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) {
            mIOBufferList.mNumberBuffers = 1;
            mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame;
            if (!mClientOwnsIOBuffer) {
                  //printf("reallocating I/O buffer\n");
                  delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
                  mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes];
            }
            mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes;
            mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize;
      }
      
      UInt32 propertySize = sizeof(UInt32);
      UInt32 externallyFramed;
      XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed,
                  sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed),
                  "is format externally framed");
      if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) {
            delete[] mPacketDescs;
            mPacketDescs = NULL;
            mNumPacketDescs = 0;

            if (externallyFramed) {
                  //printf("reallocating packet descs\n");
                  mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets];
                  mNumPacketDescs = mIOBufferSizePackets;
            }
      }
}

// _______________________________________________________________________________________
//
void  CAAudioFile::SetIOBuffer(void *buf)
{
      if (!mClientOwnsIOBuffer)
            delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
      mIOBufferList.mBuffers[0].mData = buf;

      if (buf == NULL) {
            mClientOwnsIOBuffer = false;
            SetIOBufferSizeBytes(mIOBufferSizeBytes);
      } else {
            mClientOwnsIOBuffer = true;
            AllocateBuffers();
      }
//    printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
}

// ===============================================================================

/*
For Tiger:
added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket.
You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty.

      kAudioFilePropertyPacketToFrame                 =     'pkfr',
            // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored.
      kAudioFilePropertyFrameToPacket                 =     'frpk',
            // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back.

struct AudioFramePacketTranslation
{
      SInt64 mFrame;
      SInt64 mPacket;
      UInt32 mFrameOffsetInPacket;
};
*/

SInt64  CAAudioFile::PacketToFrame(SInt64 packet) const
{
      AudioFramePacketTranslation trans;
      UInt32 propertySize;
      
      switch (mFileDataFormat.mFramesPerPacket) {
      case 1:
            return packet;
      case 0:
            trans.mPacket = packet;
            propertySize = sizeof(trans);
            XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans),
                  "packet <-> frame translation unimplemented for format with variable frames/packet");
            return trans.mFrame;
      }
      return packet * mFileDataFormat.mFramesPerPacket;
}

SInt64      CAAudioFile::FrameToPacket(SInt64 inFrame) const
{
      AudioFramePacketTranslation trans;
      UInt32 propertySize;
      
      switch (mFileDataFormat.mFramesPerPacket) {
      case 1:
            return inFrame;
      case 0:
            trans.mFrame = inFrame;
            propertySize = sizeof(trans);
            XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans),
                  "packet <-> frame translation unimplemented for format with variable frames/packet");
            return trans.mPacket;
      }
      return inFrame / mFileDataFormat.mFramesPerPacket;
}

// _______________________________________________________________________________________
//

SInt64  CAAudioFile::Tell() const   // frameNumber
{
      return mFrameMark - mFrame0Offset;
}

void  CAAudioFile::SeekToPacket(SInt64 packetNumber)
{
#if VERBOSE_IO
      printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber);
#endif
      XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file");
      if (mPacketMark == packetNumber)
            return; // already there! don't reset converter
      mPacketMark = packetNumber;
      
      mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset;
      mFramesToSkipFollowingSeek = 0;
      if (mConverter)
            // must reset -- if we reached end of stream. converter will no longer work otherwise
            AudioConverterReset(mConverter);
}

/*
      Example: AAC, 1024 frames/packet, 2112 frame offset
      
                                           2112
                                             |
    Absolute frames:  0       1024      2048 |    3072
                      +---------+---------+--|------+---------+---------+
    Packets:          |    0    |    1    |  | 2    |    3    |    4    |
                      +---------+---------+--|------+---------+---------+
    Client frames:  -2112   -1088       -64  |     960                                    SeekToFrame, TellFrame
                                             |
                                             0

      *   Offset between absolute and client frames is mFrame0Offset.
      *** mFrameMark is in client frames ***
      
      Examples:
            clientFrame                         0           960         1000  1024
            absoluteFrame                       2112  3072  3112  3136
            packet                                    0           0           0           1
            tempFrameMark*                      -2112 -2112 -2112 -1088
            mFramesToSkipFollowingSeek    2112  3072  3112  2112
*/
void  CAAudioFile::Seek(SInt64 clientFrame)
{
      if (clientFrame == mFrameMark)
            return; // already there! don't reset converter

      //SInt64 absoluteFrame = clientFrame + mFrame0Offset;
      XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file");

#if VERBOSE_IO
      SInt64 prevFrameMark = mFrameMark;
#endif
      
      SInt64 packet;
      packet = FrameToPacket(clientFrame);
      if (packet < 0)
            packet = 0;
      SeekToPacket(packet);
      // this will have backed up mFrameMark to match the beginning of the packet
      mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0));
      mFrameMark = clientFrame;
      
#if VERBOSE_IO
      printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek);
#endif
}

// _______________________________________________________________________________________
//
void  CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData)
                  // May read fewer packets than requested if:
                  //          buffer is not big enough
                  //          file does not contain that many more packets
                  // Note that eofErr is not fatal, just results in 0 packets returned
                  // ioData's buffer sizes may be shortened
{
      XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0");
      if (mIOBufferList.mBuffers[0].mData == NULL) {
#if DEBUG
            printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
#endif
            AllocateBuffers();
      }
      UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize;
      UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize;    
      // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check
      UInt32 nPackets = std::min(ioNumPackets, maxNumPackets);
      
      mMaxPacketsToRead = ~0UL;
      
      if (mClientDataFormat.mFramesPerPacket == 1) {  // PCM or equivalent
            while (mFramesToSkipFollowingSeek > 0) {
                  UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets);
                  UInt32 framesPerPacket;
                  if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0)
                        mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket;

                  if (mConverter == NULL) {
                        XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file");
                  } else {
#if CAAUDIOFILE_PROFILE
                        mInConverter = true;
#endif
                        StartTiming(this, fill);
                        XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)");
                        ElapsedTime(this, fill, mTicksInConverter);
#if CAAUDIOFILE_PROFILE
                        mInConverter = false;
#endif
                  }
                  if (skipFrames == 0) {  // hit EOF
                        ioNumPackets = 0;
                        return;
                  }
                  mFrameMark += skipFrames;
#if VERBOSE_IO
                  printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames);
#endif

                  mFramesToSkipFollowingSeek -= skipFrames;

                  // restore mDataByteSize
                  for (int i = ioData->mNumberBuffers; --i >= 0 ; )
                        ioData->mBuffers[i].mDataByteSize = bufferSizeBytes;
            }
      }
      
      if (mFileDataFormat.mFramesPerPacket > 0)
            // don't read more packets than we are being asked to produce
            mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1;
      if (mConverter == NULL) {
            XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file");
      } else {
#if CAAUDIOFILE_PROFILE
            mInConverter = true;
#endif
            StartTiming(this, fill);
            XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)");
            ElapsedTime(this, fill, mTicksInConverter);
#if CAAUDIOFILE_PROFILE
            mInConverter = false;
#endif
      }
      if (mClientDataFormat.mFramesPerPacket == 1)
            mFrameMark += nPackets;
      
      ioNumPackets = nPackets;
}

// _______________________________________________________________________________________
//
OSStatus CAAudioFile::ReadInputProc(      AudioConverterRef                   inAudioConverter,
                                                            UInt32*                                         ioNumberDataPackets,
                                                            AudioBufferList*                    ioData,
                                                            AudioStreamPacketDescription**      outDataPacketDescription,
                                                            void*                                     inUserData)
{
      CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);

#if 0
      SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark;
      if (remainingPacketsInFile <= 0) {
            *ioNumberDataPackets = 0;
            ioData->mBuffers[0].mDataByteSize = 0;
            if (outDataPacketDescription)
                  *outDataPacketDescription = This->mPacketDescs;
#if VERBOSE_IO
            printf("CAAudioFile::ReadInputProc: EOF\n");
#endif
            return noErr;     // not eofErr; EOF is signified by 0 packets/0 bytes
      }
#endif
      
      // determine how much to read
      AudioBufferList *readBuffer;
      UInt32 readPackets;
      if (inAudioConverter != NULL) {
            // getting called from converter, need to use our I/O buffer
            readBuffer = &This->mIOBufferList;
            readPackets = This->mIOBufferSizePackets;
      } else {
            // getting called directly from ReadPackets, use supplied buffer
            if (This->mFileMaxPacketSize == 0)
                  return kExtAudioFileError_MaxPacketSizeUnknown;
            readBuffer = ioData;
            readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize);
                  // don't attempt to read more packets than will fit in the buffer
      }
      // don't try to read past EOF
//    if (readPackets > remainingPacketsInFile)
//          readPackets = remainingPacketsInFile;
      // don't read more packets than necessary to produce the requested amount of converted data
      if (readPackets > This->mMaxPacketsToRead) {
#if VERBOSE_IO
            printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets);
#endif
            readPackets = This->mMaxPacketsToRead;
      }
      
      // read
      UInt32 bytesRead;
      OSStatus err;
      
      StartTiming(This, read);
      StartTiming(This, readinconv);
      err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData);
#if CAAUDIOFILE_PROFILE
      if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter);
#endif
      ElapsedTime(This, read, This->mTicksInIO);

      if (err) {
            DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err);
            return err;
      }
      
#if VERBOSE_IO
      printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err);
#if VERBOSE_IO >= 2
      if (This->mPacketDescs) {
            for (UInt32 i = 0; i < readPackets; ++i) {
                  printf("  read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize);
            }
      }
      printf("  read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4);
#endif
#endif
      if (readPackets == 0) {
            *ioNumberDataPackets = 0;
            ioData->mBuffers[0].mDataByteSize = 0;
            return noErr;
      }

      if (outDataPacketDescription)
            *outDataPacketDescription = This->mPacketDescs;
      ioData->mBuffers[0].mDataByteSize = bytesRead;
      ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData;

      This->mPacketMark += readPackets;
      if (This->mClientDataFormat.mFramesPerPacket != 1) {  // for PCM client formats we update in Read
            // but for non-PCM client format (weird case) we must update here/now
            if (This->mFileDataFormat.mFramesPerPacket > 0)
                  This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket;
            else {
                  for (UInt32 i = 0; i < readPackets; ++i)
                        This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket;
            }
      }
      *ioNumberDataPackets = readPackets;
      return noErr;
}

// _______________________________________________________________________________________
//
void  CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data)
{
      if (mIOBufferList.mBuffers[0].mData == NULL) {
#if DEBUG
            printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
#endif
            AllocateBuffers();
      }

      if (mMode == kPreparingToWrite)
            mMode = kWriting;
      else
            XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file");
      if (mConverter != NULL) {
            mWritePackets = numPackets;
            mWriteBufferList->SetFrom(data);
            WritePacketsFromCallback(WriteInputProc, this);
      } else {
            StartTiming(this, write);
            XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize, 
                                    NULL, mPacketMark, &numPackets, data->mBuffers[0].mData),
                                    "write audio file");
            ElapsedTime(this, write, mTicksInIO);
#if VERBOSE_IO
            printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize);
#endif
            //mNumberPackets = 
            mPacketMark += numPackets;
            if (mFileDataFormat.mFramesPerPacket > 0)
                  mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket;
            // else: shouldn't happen since we're only called when there's no converter
      }
}

// _______________________________________________________________________________________
//
void  CAAudioFile::FlushEncoder()
{
      if (mConverter != NULL) {
            mFinishingEncoding = true;
            WritePacketsFromCallback(WriteInputProc, this);
            mFinishingEncoding = false;

            // get priming info from converter, set it on the file
            if (mFileDataFormat.mBitsPerChannel == 0) {
                  UInt32 propertySize;
                  OSStatus err;
                  AudioConverterPrimeInfo primeInfo;
                  propertySize = sizeof(primeInfo);
      
                  err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
                  if (err == noErr) {
                        AudioFilePacketTableInfo pti;
                        propertySize = sizeof(pti);
                        err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
                        if (err == noErr) {
//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
                              UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames;
                              pti.mPrimingFrames = primeInfo.leadingFrames;
                              pti.mRemainderFrames = primeInfo.trailingFrames;
                              pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames;
//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
                              XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file");
                        }
                  }
            }
      }
}

// _______________________________________________________________________________________
//
OSStatus CAAudioFile::WriteInputProc(     AudioConverterRef                   /*inAudioConverter*/,
                                                            UInt32 *                                  ioNumberDataPackets,
                                                            AudioBufferList*                    ioData,
                                                            AudioStreamPacketDescription **     outDataPacketDescription,
                                                            void*                                     inUserData)
{
      CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
      if (This->mFinishingEncoding) {
            *ioNumberDataPackets = 0;
            ioData->mBuffers[0].mDataByteSize = 0;
            ioData->mBuffers[0].mData = NULL;
            if (outDataPacketDescription)
                  *outDataPacketDescription = NULL;
            return noErr;
      }
      UInt32 numPackets = This->mWritePackets;
      if (numPackets == 0) {
            return kNoMoreInputRightNow;
      }
      This->mWriteBufferList->ToAudioBufferList(ioData);
      This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame);
      *ioNumberDataPackets = numPackets;
      if (outDataPacketDescription)
            *outDataPacketDescription = NULL;
      This->mWritePackets -= numPackets;
      return noErr;
}

// _______________________________________________________________________________________
//
#if VERBOSE_IO
static void hexdump(const void *addr, long len)
{
      const Byte *p = (Byte *)addr;
      UInt32 offset = 0;
      
      if (len > 0x400) len = 0x400;
      
      while (len > 0) {
            int n = len > 16 ? 16 : len;
            printf("%08lX:  ", offset);
            for (int i = 0; i < 16; ++i)
                  if (i < n)
                        printf("%02X ", p[i]);
                  else printf("   ");
            for (int i = 0; i < 16; ++i)
                  if (i < n)
                        putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.');
                  else putchar(' ');
            putchar('\n');
            p += 16;
            len -= 16;
            offset += 16;
      }
}
#endif

// _______________________________________________________________________________________
//
void  CAAudioFile::WritePacketsFromCallback(
                                                AudioConverterComplexInputDataProc  inInputDataProc,
                                                void *                                                inInputDataProcUserData)
{
      while (true) {
            // keep writing until we exhaust the input (temporary stop), or produce no output (EOF)
            UInt32 numEncodedPackets = mIOBufferSizePackets;
            mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes;
#if CAAUDIOFILE_PROFILE
            mInConverter = true;
#endif
            StartTiming(this, fill);
            OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData, 
                              &numEncodedPackets, &mIOBufferList, mPacketDescs);
            ElapsedTime(this, fill, mTicksInConverter);
#if CAAUDIOFILE_PROFILE
            mInConverter = false;
#endif
            XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)");
            if (numEncodedPackets == 0)
                  break;
            Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData;
#if VERBOSE_IO
            printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize);
            if (mPacketDescs) {
                  for (UInt32 i = 0; i < numEncodedPackets; ++i) {
                        printf("  write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
#if VERBOSE_IO >= 2
                        hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
#endif
                  }
            }
#endif
            StartTiming(this, write);
            XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file");
            ElapsedTime(this, write, mTicksInIO);
            mPacketMark += numEncodedPackets;
            //mNumberPackets += numEncodedPackets;
            if (mFileDataFormat.mFramesPerPacket > 0)
                  mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket;
            else {
                  for (UInt32 i = 0; i < numEncodedPackets; ++i)
                        mFrameMark += mPacketDescs[i].mVariableFramesInPacket;
            }
            if (err == kNoMoreInputRightNow)
                  break;
      }
}

#endif // !CAAF_USE_EXTAUDIOFILE

Generated by  Doxygen 1.6.0   Back to index