From 78e13c37f482552881c3b68ce08a80793df15152 Mon Sep 17 00:00:00 2001 From: Taybin Rutkin Date: Fri, 1 Sep 2006 01:59:41 +0000 Subject: Synced string array in sfdb_ui.cc with ImportMode enum. Cleaned up CoreAudioSource by using CAAudioFile. git-svn-id: svn://localhost/ardour2/trunk@881 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/editing_syms.h | 9 +- gtk2_ardour/sfdb_ui.cc | 17 +- libs/appleutility/CAAudioFile.cpp | 1241 ++++++++++++++++++++++++++++++++++ libs/appleutility/CAAudioFile.h | 439 ++++++++++++ libs/appleutility/CABufferList.cpp | 179 +++++ libs/appleutility/CABufferList.h | 300 ++++++++ libs/appleutility/CAXException.cpp | 45 ++ libs/appleutility/CAXException.h | 158 +++++ libs/ardour/ardour/coreaudiosource.h | 5 +- libs/ardour/coreaudiosource.cc | 149 ++-- 10 files changed, 2436 insertions(+), 106 deletions(-) create mode 100644 libs/appleutility/CAAudioFile.cpp create mode 100644 libs/appleutility/CAAudioFile.h create mode 100644 libs/appleutility/CABufferList.cpp create mode 100644 libs/appleutility/CABufferList.h create mode 100644 libs/appleutility/CAXException.cpp create mode 100644 libs/appleutility/CAXException.h diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index 654ddc8852..3a503e2cb5 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -54,7 +54,8 @@ DISPLAYCONTROL(ShowMeasures) DISPLAYCONTROL(ShowWaveforms) DISPLAYCONTROL(ShowWaveformsRecording) -IMPORTMODE(ImportAsRegion) -IMPORTMODE(ImportAsTrack) -IMPORTMODE(ImportAsTapeTrack) -IMPORTMODE(ImportToTrack) +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTMODE(ImportAsRegion=0) +IMPORTMODE(ImportToTrack=1) +IMPORTMODE(ImportAsTrack=2) +IMPORTMODE(ImportAsTapeTrack=3) diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 0f167776b0..5cfcf56337 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -36,6 +36,7 @@ #include #include "ardour_ui.h" +#include "editing.h" #include "gui_thread.h" #include "prompter.h" #include "sfdb_ui.h" @@ -305,6 +306,15 @@ SoundFileBox::field_selected () } } +// this needs to be kept in sync with the ImportMode enum defined in editing.h and editing_syms.h. +static const char *import_mode_strings[] = { + X_("Add to Region list"), + X_("Add to selected Track(s)"), + X_("Add as new Track(s)"), + X_("Add as new Tape Track(s)"), + 0 +}; + SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) : ArdourDialog (title, false), chooser (Gtk::FILE_CHOOSER_ACTION_OPEN) @@ -339,13 +349,6 @@ SoundFileChooser::SoundFileChooser (string title, ARDOUR::Session* s) show_all (); } -static const char *import_mode_strings[] = { - X_("Add to Region list"), - X_("Add as new Track(s)"), - X_("Add to selected Track(s)"), - 0 -}; - vector SoundFileOmega::mode_strings; SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s) diff --git a/libs/appleutility/CAAudioFile.cpp b/libs/appleutility/CAAudioFile.cpp new file mode 100644 index 0000000000..e1e39b0ec9 --- /dev/null +++ b/libs/appleutility/CAAudioFile.cpp @@ -0,0 +1,1241 @@ +/* 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 +#include "CAHostTimeBase.h" +#include "CADebugMacros.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#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(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(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(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(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 diff --git a/libs/appleutility/CAAudioFile.h b/libs/appleutility/CAAudioFile.h new file mode 100644 index 0000000000..2cfb4f3031 --- /dev/null +++ b/libs/appleutility/CAAudioFile.h @@ -0,0 +1,439 @@ +/* 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.h + +=============================================================================*/ + +#ifndef __CAAudioFile_h__ +#define __CAAudioFile_h__ + +#include + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#include "CAStreamBasicDescription.h" +#include "CABufferList.h" +#include "CAAudioChannelLayout.h" +#include "CAXException.h" +#include "CAMath.h" + +#ifndef CAAF_USE_EXTAUDIOFILE +// option: use AudioToolbox/ExtAudioFile.h? Only available on Tiger. + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3 + // we are building software that must be deployable on Panther or earlier + #define CAAF_USE_EXTAUDIOFILE 0 + #else + // else we require Tiger and can use the API + #define CAAF_USE_EXTAUDIOFILE 1 + #endif +#endif + +#ifndef MAC_OS_X_VERSION_10_4 + // we have pre-Tiger headers; add our own declarations + typedef UInt32 AudioFileTypeID; + enum { + kExtAudioFileError_InvalidProperty = -66561, + kExtAudioFileError_InvalidPropertySize = -66562, + kExtAudioFileError_NonPCMClientFormat = -66563, + kExtAudioFileError_InvalidChannelMap = -66564, // number of channels doesn't match format + kExtAudioFileError_InvalidOperationOrder = -66565, + kExtAudioFileError_InvalidDataFormat = -66566, + kExtAudioFileError_MaxPacketSizeUnknown = -66567, + kExtAudioFileError_InvalidSeek = -66568, // writing, or offset out of bounds + kExtAudioFileError_AsyncWriteTooLarge = -66569, + kExtAudioFileError_AsyncWriteBufferOverflow = -66570 // an async write could not be completed in time + }; +#else + #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include + #else + #include "ExtendedAudioFile.h" + #endif +#endif + +// _______________________________________________________________________________________ +// Wrapper class for an AudioFile, supporting encode/decode to/from a PCM client format +class CAAudioFile { +public: + // implementation-independent helpers + void Open(const char *filePath) { + FSRef fsref; + XThrowIfError(FSPathMakeRef((UInt8 *)filePath, &fsref, NULL), "locate audio file"); + Open(fsref); + } + + bool HasConverter() const { return GetConverter() != NULL; } + + double GetDurationSeconds() { + double sr = GetFileDataFormat().mSampleRate; + return fnonzero(sr) ? GetNumberFrames() / sr : 0.; + } + // will be 0 if the file's frames/packet is 0 (variable) + // or the file's sample rate is 0 (unknown) + +#if CAAF_USE_EXTAUDIOFILE +public: + CAAudioFile() : mExtAF(NULL) { } + virtual ~CAAudioFile() { if (mExtAF) Close(); } + + void Open(const FSRef &fsref) { + // open an existing file + XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed"); + } + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) { + XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed"); + } + + void Wrap(AudioFileID fileID, bool forWriting) { + // use this to wrap an AudioFileID opened externally + XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed"); + } + + void Close() { + XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed"); + mExtAF = NULL; + } + + const CAStreamBasicDescription &GetFileDataFormat() { + UInt32 size = sizeof(mFileDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format"); + return mFileDataFormat; + } + + const CAAudioChannelLayout & GetFileChannelLayout() { + return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout); + } + + void SetFileChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout"); + mFileChannelLayout = layout; + } + + const CAStreamBasicDescription &GetClientDataFormat() { + UInt32 size = sizeof(mClientDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format"); + return mClientDataFormat; + } + + const CAAudioChannelLayout & GetClientChannelLayout() { + return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout); + } + + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format"); + if (layout) + SetClientChannelLayout(*layout); + } + + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout"); + } + + AudioConverterRef GetConverter() const { + UInt32 size = sizeof(AudioConverterRef); + AudioConverterRef converter; + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter"); + return converter; + } + + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false) + { + OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) + XThrowIfError(err, "Couldn't set audio converter property"); + if (!err) { + // must tell the file that we have changed the converter; a NULL converter config is sufficient + CFPropertyListRef config = NULL; + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed"); + } + return err; + } + + SInt64 GetNumberFrames() { + SInt64 length; + UInt32 size = sizeof(SInt64); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length"); + return length; + } + + void SetNumberFrames(SInt64 length) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length"); + } + + void Seek(SInt64 pos) { + XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file"); + } + + SInt64 Tell() { + SInt64 pos; + XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark"); + return pos; + } + + void Read(UInt32 &ioFrames, AudioBufferList *ioData) { + XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file"); + } + + void Write(UInt32 inFrames, const AudioBufferList *inData) { + XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file"); + } + + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size"); + } + +private: + const CAAudioChannelLayout & FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) { + UInt32 size; + XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout"); + AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size); + OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout); + if (err) { + free(layout); + XThrowIfError(err, "Couldn't get channel layout"); + } + layoutObj = layout; + free(layout); + return layoutObj; + } + + +private: + ExtAudioFileRef mExtAF; + + CAStreamBasicDescription mFileDataFormat; + CAAudioChannelLayout mFileChannelLayout; + + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mClientChannelLayout; +#endif + +#if !CAAF_USE_EXTAUDIOFILE + CAAudioFile(); + virtual ~CAAudioFile(); + + // --- second-stage initializers --- + // Use exactly one of the following: + // - Open + // - PrepareNew followed by Create + // - Wrap + + void Open(const FSRef &fsref); + // open an existing file + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL); + + void Wrap(AudioFileID fileID, bool forWriting); + // use this to wrap an AudioFileID opened externally + + // --- + + void Close(); + // In case you want to close the file before the destructor executes + + // --- Data formats --- + + // Allow specifying the file's channel layout. Must be called before SetClientFormat. + // When writing, the specified channel layout is written to the file (if the file format supports + // the channel layout). When reading, the specified layout overrides the one read from the file, + // if any. + void SetFileChannelLayout(const CAAudioChannelLayout &layout); + + // This specifies the data format which the client will use for reading/writing the file, + // which may be different from the file's format. An AudioConverter is created if necessary. + // The client format must be linear PCM. + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL); + void SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); } + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); } + + // Wrapping the underlying converter, if there is one + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void * inPropertyData, + bool inCanFail = false); + void SetConverterConfig(CFArrayRef config) { + SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); } + CFArrayRef GetConverterConfig(); + + // --- I/O --- + // All I/O is sequential, but you can seek to an arbitrary position when reading. + // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's. + // However, ReadPackets/WritePackets use packet counts in the client data format. + + void Read(UInt32 &ioNumFrames, AudioBufferList *ioData); + void Write(UInt32 numFrames, const AudioBufferList *data); + + // These can fail for files without a constant mFramesPerPacket + void Seek(SInt64 frameNumber); + SInt64 Tell() const; // frameNumber + + // --- Accessors --- + // note: client parameters only valid if SetClientFormat has been called + AudioFileID GetAudioFileID() const { return mAudioFile; } + const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; } + const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; } + const CAAudioChannelLayout & GetFileChannelLayout() const { return mFileChannelLayout; } + const CAAudioChannelLayout & GetClientChannelLayout() const { return mClientChannelLayout; } + AudioConverterRef GetConverter() const { return mConverter; } + + UInt32 GetFileMaxPacketSize() const { return mFileMaxPacketSize; } + UInt32 GetClientMaxPacketSize() const { return mClientMaxPacketSize; } + SInt64 GetNumberPackets() const { + SInt64 npackets; + UInt32 propertySize = sizeof(npackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count"); + return npackets; + } + SInt64 GetNumberFrames() const; + // will be 0 if the file's frames/packet is 0 (variable) + void SetNumberFrames(SInt64 length); // should only be set on a PCM file + + // --- Tunable performance parameters --- + void SetUseCache(bool b) { mUseCache = b; } + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; } + UInt32 GetIOBufferSizeBytes() { return mIOBufferSizeBytes; } + void * GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; } + void SetIOBuffer(void *buf); + + // -- Profiling --- +#if CAAUDIOFILE_PROFILE + void EnableProfiling(bool b) { mProfiling = b; } + UInt64 TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; } + UInt64 TicksInIO() const { return mTicksInIO; } +#endif + +// _______________________________________________________________________________________ +private: + SInt64 FileDataOffset(); + void SeekToPacket(SInt64 packetNumber); + SInt64 TellPacket() const { return mPacketMark; } // will be imprecise if SeekToFrame was called + + void SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout); + void WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData); + // will use I/O buffer size + void InitFileMaxPacketSize(); + void FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0); + + void GetExistingFileInfo(); + void FlushEncoder(); + void CloseConverter(); + void UpdateClientMaxPacketSize(); + void AllocateBuffers(bool okToFail=false); + SInt64 PacketToFrame(SInt64 packet) const; + SInt64 FrameToPacket(SInt64 inFrame) const; + + static OSStatus ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); + + static OSStatus WriteInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); +// _______________________________________________________________________________________ +private: + + // the file + FSRef mFSRef; + AudioFileID mAudioFile; + bool mOwnOpenFile; + bool mUseCache; + bool mFinishingEncoding; + enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode; + +// SInt64 mNumberPackets; // in file's format + SInt64 mFileDataOffset; + SInt64 mPacketMark; // in file's format + SInt64 mFrameMark; // this may be offset from the start of the file + // by the codec's latency; i.e. our frame 0 could + // lie at frame 2112 of a decoded AAC file + SInt32 mFrame0Offset; + UInt32 mFramesToSkipFollowingSeek; + + // buffers + UInt32 mIOBufferSizeBytes; + UInt32 mIOBufferSizePackets; + AudioBufferList mIOBufferList; // only one buffer -- USE ACCESSOR so it can be lazily initialized + bool mClientOwnsIOBuffer; + AudioStreamPacketDescription *mPacketDescs; + UInt32 mNumPacketDescs; + + // formats/conversion + AudioConverterRef mConverter; + CAStreamBasicDescription mFileDataFormat; + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mFileChannelLayout; + CAAudioChannelLayout mClientChannelLayout; + UInt32 mFileMaxPacketSize; + UInt32 mClientMaxPacketSize; + + // cookie + Byte * mMagicCookie; + UInt32 mMagicCookieSize; + + // for ReadPackets + UInt32 mMaxPacketsToRead; + + // for WritePackets + UInt32 mWritePackets; + CABufferList * mWriteBufferList; + +#if CAAUDIOFILE_PROFILE + // performance + bool mProfiling; + UInt64 mTicksInConverter; + UInt64 mTicksInReadInConverter; + UInt64 mTicksInIO; + bool mInConverter; +#endif + +#endif // CAAF_USE_EXTAUDIOFILE +}; + +#endif // __CAAudioFile_h__ diff --git a/libs/appleutility/CABufferList.cpp b/libs/appleutility/CABufferList.cpp new file mode 100644 index 0000000000..81298f918a --- /dev/null +++ b/libs/appleutility/CABufferList.cpp @@ -0,0 +1,179 @@ +/* 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. +*/ +/*============================================================================= + CABufferList.cpp + +=============================================================================*/ + +#include "CABufferList.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +void CABufferList::AllocateBuffers(UInt32 nBytes) +{ + if (nBytes <= GetNumBytes()) return; + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // get page faults now, not later + + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + if (buf->mData != NULL && buf->mDataByteSize > 0) + // preserve existing buffer contents + memcpy(p, buf->mData, buf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + delete[] oldMemory; +} + +void CABufferList::AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inSrcList, CABufferList *inSetPtrList) +{ + if (mNumberBuffers != inSrcList->mNumberBuffers) return; + if (mNumberBuffers != inSetPtrList->mNumberBuffers) return; + if (nBytes <= GetNumBytes()) { + CopyAllFrom(inSrcList, inSetPtrList); + return; + } + inSetPtrList->VerifyNotTrashingOwnedBuffer(); + UInt32 fromByteSize = inSrcList->GetNumBytes(); + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // make buffer "hot" + + AudioBuffer *buf = mBuffers; + AudioBuffer *ptrBuf = inSetPtrList->mBuffers; + AudioBuffer *srcBuf = inSrcList->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf, ++ptrBuf, ++srcBuf) { + if (srcBuf->mData != NULL && srcBuf->mDataByteSize > 0) + // preserve existing buffer contents + memmove(p, srcBuf->mData, srcBuf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + ptrBuf->mDataByteSize = srcBuf->mDataByteSize; + ptrBuf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + if (inSrcList != inSetPtrList) + inSrcList->BytesConsumed(fromByteSize); + delete[] oldMemory; +} + +void CABufferList::DeallocateBuffers() +{ + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = NULL; + buf->mDataByteSize = 0; + } + if (mBufferMemory != NULL) { + delete[] mBufferMemory; + mBufferMemory = NULL; + } + +} + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize) +{ + printf("AudioBufferList @ %p:\n", abl); + const AudioBuffer *buf = abl->mBuffers; + for (UInt32 i = 0; i < abl->mNumberBuffers; ++i, ++buf) { + printf(" [%2ld]: %2ldch, %5ld bytes @ %8p", + i, buf->mNumberChannels, buf->mDataByteSize, buf->mData); + if (framesToPrint) { + printf(":"); + Byte *p = (Byte *)buf->mData; + for (int j = framesToPrint * buf->mNumberChannels; --j >= 0; ) + switch (wordSize) { + case 0: + printf(" %6.3f", *(Float32 *)p); + p += sizeof(Float32); + break; + case 1: + case -1: + printf(" %02X", *p); + p += 1; + break; + case 2: + printf(" %04X", EndianU16_BtoN(*(UInt16 *)p)); + p += 2; + break; + case 3: + printf(" %06X", (p[0] << 16) | (p[1] << 8) | p[2]); + p += 3; + break; + case 4: + printf(" %08lX", EndianU32_BtoN(*(UInt32 *)p)); + p += 4; + break; + case -2: + printf(" %04X", EndianU16_LtoN(*(UInt16 *)p)); + p += 2; + break; + case -3: + printf(" %06X", (p[2] << 16) | (p[1] << 8) | p[0]); + p += 3; + break; + case -4: + printf(" %08lX", EndianU32_LtoN(*(UInt32 *)p)); + p += 4; + break; + } + } + printf("\n"); + } +} + diff --git a/libs/appleutility/CABufferList.h b/libs/appleutility/CABufferList.h new file mode 100644 index 0000000000..3b0cef9a52 --- /dev/null +++ b/libs/appleutility/CABufferList.h @@ -0,0 +1,300 @@ +/* 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. +*/ +/*============================================================================= + CABufferList.h + +=============================================================================*/ + +#ifndef __CABufferList_h__ +#define __CABufferList_h__ + +#include +#include "CAStreamBasicDescription.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize); + // wordSize: 0 = float32, else integer word size, negative if little-endian + +/* ____________________________________________________________________________ +// CABufferList - variable length buffer list + + This class is designed for use in non-simplistic cases. For AudioUnits, AUBufferList + is preferred. + + CABufferList can be used in one of two ways: + - as mutable pointers into non-owned memory + - as an immutable array of buffers (owns its own memory). + + All buffers are assumed to have the same format (number of channels, word size), so that + we can assume their mDataByteSizes are all the same. +____________________________________________________________________________ */ +class CABufferList { +public: + void * operator new(size_t /*size*/, int nBuffers) { + return ::operator new(sizeof(CABufferList) + (nBuffers-1) * sizeof(AudioBuffer)); + } + static CABufferList * New(const char *name, const CAStreamBasicDescription &format) + { + UInt32 numBuffers = format.NumberChannelStreams(), channelsPerBuffer = format.NumberInterleavedChannels(); + return new(numBuffers) CABufferList(name, numBuffers, channelsPerBuffer); + } + +protected: + CABufferList(const char *name, UInt32 numBuffers, UInt32 channelsPerBuffer) : + mName(name), + mBufferMemory(NULL) + { + check(numBuffers > 0 /*&& channelsPerBuffer > 0*/); + mNumberBuffers = numBuffers; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mNumberChannels = channelsPerBuffer; + buf->mDataByteSize = 0; + buf->mData = NULL; + } + } + +public: + ~CABufferList() + { + if (mBufferMemory) + delete[] mBufferMemory; + } + + const char * Name() { return mName; } + + const AudioBufferList & GetBufferList() const { return *(AudioBufferList *)&mNumberBuffers; } + + AudioBufferList & GetModifiableBufferList() + { + VerifyNotTrashingOwnedBuffer(); + return _GetBufferList(); + } + + UInt32 GetNumBytes() const + { + return mBuffers[0].mDataByteSize; + } + + void SetBytes(UInt32 nBytes, void *data) + { + VerifyNotTrashingOwnedBuffer(); + check(mNumberBuffers == 1); + mBuffers[0].mDataByteSize = nBytes; + mBuffers[0].mData = data; + } + + void CopyAllFrom(CABufferList *srcbl, CABufferList *ptrbl) + // copies bytes from srcbl + // make ptrbl reflect the length copied + // note that srcbl may be same as ptrbl! + { + // Note that this buffer *can* own memory and its pointers/lengths are not + // altered; only its buffer contents, which are copied from srcbl. + // The pointers/lengths in ptrbl are updated to reflect the addresses/lengths + // of the copied data, and srcbl's contents are consumed. + ptrbl->VerifyNotTrashingOwnedBuffer(); + UInt32 nBytes = srcbl->GetNumBytes(); + AudioBuffer *mybuf = mBuffers, *srcbuf = srcbl->mBuffers, + *ptrbuf = ptrbl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf, ++ptrbuf) { + memmove(mybuf->mData, srcbuf->mData, srcbuf->mDataByteSize); + ptrbuf->mData = mybuf->mData; + ptrbuf->mDataByteSize = srcbuf->mDataByteSize; + } + if (srcbl != ptrbl) + srcbl->BytesConsumed(nBytes); + } + + void AppendFrom(CABufferList *blp, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers, *srcbuf = blp->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + check(nBytes <= srcbuf->mDataByteSize); + memcpy((Byte *)mybuf->mData + mybuf->mDataByteSize, srcbuf->mData, nBytes); + mybuf->mDataByteSize += nBytes; + } + blp->BytesConsumed(nBytes); + } + + void PadWithZeroes(UInt32 desiredBufferSize) + // for cases where an algorithm (e.g. SRC) requires some + // padding to create silence following end-of-file + { + VerifyNotTrashingOwnedBuffer(); + if (GetNumBytes() > desiredBufferSize) return; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData + buf->mDataByteSize, 0, desiredBufferSize - buf->mDataByteSize); + buf->mDataByteSize = desiredBufferSize; + } + } + + void SetToZeroes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData, 0, nBytes); + buf->mDataByteSize = nBytes; + } + } + + void Reset() + { + DeallocateBuffers(); + } + + Boolean SameDataAs(const CABufferList* anotherBufferList) + { + // check to see if two buffer lists point to the same memory. + if (mNumberBuffers != anotherBufferList->mNumberBuffers) return false; + + for (UInt32 i = 0; i < mNumberBuffers; ++i) { + if (mBuffers[i].mData != anotherBufferList->mBuffers[i].mData) return false; + } + return true; + } + + void BytesConsumed(UInt32 nBytes) + // advance buffer pointers, decrease buffer sizes + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + check(nBytes <= buf->mDataByteSize); + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetFrom(const AudioBufferList *abl) + { + VerifyNotTrashingOwnedBuffer(); + memcpy(&_GetBufferList(), abl, (char *)&abl->mBuffers[abl->mNumberBuffers] - (char *)abl); + } + + void SetFrom(const CABufferList *blp) + { + SetFrom(&blp->GetBufferList()); + } + + void SetFrom(const AudioBufferList *abl, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers; + const AudioBuffer *srcbuf = abl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + mybuf->mNumberChannels = srcbuf->mNumberChannels; + mybuf->mDataByteSize = nBytes; + mybuf->mData = srcbuf->mData; + } + } + + void SetFrom(const CABufferList *blp, UInt32 nBytes) + { + SetFrom(&blp->GetBufferList(), nBytes); + } + + AudioBufferList * ToAudioBufferList(AudioBufferList *abl) const + { + memcpy(abl, &GetBufferList(), (char *)&abl->mBuffers[mNumberBuffers] - (char *)abl); + return abl; + } + + void AllocateBuffers(UInt32 nBytes); + void AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inCopyFromList, CABufferList *inSetPtrList); + + void DeallocateBuffers(); + + void UseExternalBuffer(Byte *ptr, UInt32 nBytes); + + void AdvanceBufferPointers(UInt32 nBytes) + // this is for bufferlists that function simply as + // an array of pointers into another bufferlist, being advanced, + // as in RenderOutput implementations + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetNumBytes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) + buf->mDataByteSize = nBytes; + } + + void Print(const char *label=NULL, int nframes=0, int wordSize=0) const + { + if (label == NULL) + label = mName; + printf("%s - ", label); + CAShowAudioBufferList(&GetBufferList(), nframes, wordSize); + if (mBufferMemory) + printf(" owned memory @ 0x%p:\n", mBufferMemory); + } + +protected: + AudioBufferList & _GetBufferList() { return *(AudioBufferList *)&mNumberBuffers; } // use with care + // if we make this public, then we lose ability to call VerifyNotTrashingOwnedBuffer + void VerifyNotTrashingOwnedBuffer() + { + // This needs to be called from places where we are modifying the buffer list. + // It's an error to modify the buffer pointers or lengths if we own the buffer memory. + check(mBufferMemory == NULL); + } + + const char * mName; // for debugging + Byte * mBufferMemory; + // the rest must exactly mirror the structure of AudioBufferList + UInt32 mNumberBuffers; + AudioBuffer mBuffers[1]; +}; + +#endif // __CABufferList_h__ diff --git a/libs/appleutility/CAXException.cpp b/libs/appleutility/CAXException.cpp new file mode 100644 index 0000000000..088575f041 --- /dev/null +++ b/libs/appleutility/CAXException.cpp @@ -0,0 +1,45 @@ +/* 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. +*/ +/*============================================================================= + CAXException.cpp + +=============================================================================*/ + +#include "CAXException.h" + +CAXException::WarningHandler CAXException::sWarningHandler = NULL; diff --git a/libs/appleutility/CAXException.h b/libs/appleutility/CAXException.h new file mode 100644 index 0000000000..796119763d --- /dev/null +++ b/libs/appleutility/CAXException.h @@ -0,0 +1,158 @@ +/* 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. +*/ +/*============================================================================= + CAXException.h + +=============================================================================*/ + +#ifndef __CAXException_h__ +#define __CAXException_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include + #include +#endif +#include "CADebugMacros.h" +#include +#include +#include + +// An extended exception class that includes the name of the failed operation +class CAXException { +public: + CAXException(const char *operation, OSStatus err) : + mError(err) + { + if (operation == NULL) + mOperation[0] = '\0'; + else if (strlen(operation) >= sizeof(mOperation)) { + memcpy(mOperation, operation, sizeof(mOperation) - 1); + mOperation[sizeof(mOperation) - 1] = '\0'; + } else + strcpy(mOperation, operation); + } + + char *FormatError(char *str) const + { + return FormatError(str, mError); + } + + char mOperation[256]; + const OSStatus mError; + + // ------------------------------------------------- + + typedef void (*WarningHandler)(const char *msg, OSStatus err); + + /*static void Throw(const char *operation, OSStatus err) + { + throw CAXException(operation, err); + }*/ + + static char *FormatError(char *str, OSStatus error) + { + // see if it appears to be a 4-char-code + *(UInt32 *)(str + 1) = EndianU32_NtoB(error); + if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) { + str[0] = str[5] = '\''; + str[6] = '\0'; + } else + // no, format it as an integer + sprintf(str, "%ld", error); + return str; + } + + static void Warning(const char *s, OSStatus error) + { + if (sWarningHandler) + (*sWarningHandler)(s, error); + } + + static void SetWarningHandler(WarningHandler f) { sWarningHandler = f; } +private: + static WarningHandler sWarningHandler; +}; + +#if DEBUG || CoreAudio_Debug + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#else + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#endif + +#define XThrow(error, operation) XThrowIf(true, error, operation) +#define XThrowIfErr(error) XThrowIfError(error, #error) + +#endif // __CAXException_h__ diff --git a/libs/ardour/ardour/coreaudiosource.h b/libs/ardour/ardour/coreaudiosource.h index cf25c466ee..6aa3722ed7 100644 --- a/libs/ardour/ardour/coreaudiosource.h +++ b/libs/ardour/ardour/coreaudiosource.h @@ -20,8 +20,9 @@ #ifndef __coreaudio_source_h__ #define __coreaudio_source_h__ +#include + #include -#include namespace ARDOUR { @@ -45,7 +46,7 @@ class CoreAudioSource : public AudioFileSource { private: - ExtAudioFileRef af; + mutable CAAudioFile af; uint16_t n_channels; mutable float *tmpbuf; diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index 0d7e690a25..fa22dde347 100644 --- a/libs/ardour/coreaudiosource.cc +++ b/libs/ardour/coreaudiosource.cc @@ -20,6 +20,9 @@ #include #include +#include +#include + #include "i18n.h" #include @@ -43,93 +46,48 @@ void CoreAudioSource::init (const string& idstr) { string::size_type pos; - string file; tmpbuf = 0; tmpbufsize = 0; - af = 0; - OSStatus err = noErr; _name = idstr; if ((pos = idstr.find_last_of (':')) == string::npos) { channel = 0; - file = idstr; + _path = idstr; } else { channel = atoi (idstr.substr (pos+1).c_str()); - file = idstr.substr (0, pos); - } - - /* note that we temporarily truncated _id at the colon */ - FSRef fsr; - err = FSPathMakeRef ((UInt8*)file.c_str(), &fsr, 0); - if (err != noErr) { - error << string_compose (_("Could not make reference to file: %1"), name()) << endmsg; - throw failed_constructor(); - } - - err = ExtAudioFileOpen (&fsr, &af); - if (err != noErr) { - error << string_compose (_("Could not open file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); + _path = idstr.substr (0, pos); } - AudioStreamBasicDescription file_asbd; - memset(&file_asbd, 0, sizeof(AudioStreamBasicDescription)); - size_t asbd_size = sizeof(AudioStreamBasicDescription); - err = ExtAudioFileGetProperty(af, - kExtAudioFileProperty_FileDataFormat, &asbd_size, &file_asbd); - if (err != noErr) { - error << string_compose (_("Could not get file data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } - n_channels = file_asbd.mChannelsPerFrame; - - cerr << "number of channels: " << n_channels << endl; + cerr << "CoreAudioSource::init() " << name() << endl; - if (channel >= n_channels) { - error << string_compose(_("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number"), n_channels, channel) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } + /* note that we temporarily truncated _id at the colon */ + try { + af.Open(_path.c_str()); - int64_t ca_frames; - size_t prop_size = sizeof(int64_t); + CAStreamBasicDescription file_asbd (af.GetFileDataFormat()); + n_channels = file_asbd.NumberChannels(); + cerr << "number of channels: " << n_channels << endl; + + if (channel >= n_channels) { + error << string_compose("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number (%3)", n_channels, channel, name()) << endmsg; + throw failed_constructor(); + } - err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &prop_size, &ca_frames); - if (err != noErr) { - error << string_compose (_("Could not get file length for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); - throw failed_constructor(); - } + _length = af.GetNumberFrames(); - _length = ca_frames; - _path = file; - - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); - client_asbd.mSampleRate = file_asbd.mSampleRate; - client_asbd.mFormatID = kAudioFormatLinearPCM; - client_asbd.mFormatFlags = kLinearPCMFormatFlagIsFloat; - client_asbd.mBytesPerPacket = file_asbd.mChannelsPerFrame * 4; - client_asbd.mFramesPerPacket = 1; - client_asbd.mBytesPerFrame = client_asbd.mBytesPerPacket; - client_asbd.mChannelsPerFrame = file_asbd.mChannelsPerFrame; - client_asbd.mBitsPerChannel = 32; - - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose (_("Could not set client data format for file: %1"), name()) << endmsg; - ExtAudioFileDispose (af); + CAStreamBasicDescription client_asbd(file_asbd); + client_asbd.SetCanonical(client_asbd.NumberChannels(), false); + af.SetClientFormat (client_asbd); + } catch (CAXException& cax) { + error << string_compose ("CoreAudioSource: %1 (%2)", cax.mOperation, name()) << endmsg; throw failed_constructor (); } if (_build_peakfiles) { - if (initialize_peakfile (false, file)) { - error << string_compose(_("initialize peakfile failed for file %1"), name()) << endmsg; - ExtAudioFileDispose (af); + if (initialize_peakfile (false, _path)) { + error << string_compose("CoreAudioSource: initialize peakfile failed (%1)", name()) << endmsg; throw failed_constructor (); } } @@ -137,41 +95,44 @@ CoreAudioSource::init (const string& idstr) CoreAudioSource::~CoreAudioSource () { + cerr << "CoreAudioSource::~CoreAudioSource() " << name() << endl; GoingAway (); /* EMIT SIGNAL */ - if (af) { - ExtAudioFileDispose (af); - } - if (tmpbuf) { delete [] tmpbuf; } + + cerr << "deletion done" << endl; } jack_nframes_t CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_t cnt) const { - OSStatus err = noErr; - - err = ExtAudioFileSeek(af, start); - if (err != noErr) { - error << string_compose(_("CoreAudioSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), err) << endmsg; + try { + af.Seek (start); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start, _name.substr (1)) << endmsg; return 0; } AudioBufferList abl; abl.mNumberBuffers = 1; abl.mBuffers[0].mNumberChannels = n_channels; - abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); - abl.mBuffers[0].mData = dst; + UInt32 new_cnt = cnt; if (n_channels == 1) { - err = ExtAudioFileRead(af, (UInt32*) &cnt, &abl); - _read_data_count = cnt * sizeof(float); - return cnt; + abl.mBuffers[0].mDataByteSize = cnt * sizeof(Sample); + abl.mBuffers[0].mData = dst; + try { + af.Read (new_cnt, &abl); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); + } + _read_data_count = new_cnt * sizeof(float); + return new_cnt; } - uint32_t real_cnt = cnt * n_channels; + UInt32 real_cnt = cnt * n_channels; { Glib::Mutex::Lock lm (_tmpbuf_lock); @@ -185,10 +146,16 @@ CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_ tmpbuf = new float[tmpbufsize]; } - abl.mBuffers[0].mDataByteSize = real_cnt * sizeof(Sample); + abl.mBuffers[0].mDataByteSize = tmpbufsize * sizeof(Sample); abl.mBuffers[0].mData = tmpbuf; + + cerr << "channel: " << channel << endl; - err = ExtAudioFileRead(af, (UInt32*) &real_cnt, &abl); + try { + af.Read (real_cnt, &abl); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); + } float *ptr = tmpbuf + channel; real_cnt /= n_channels; @@ -208,15 +175,12 @@ CoreAudioSource::read_unlocked (Sample *dst, jack_nframes_t start, jack_nframes_ float CoreAudioSource::sample_rate() const { - AudioStreamBasicDescription client_asbd; - memset(&client_asbd, 0, sizeof(AudioStreamBasicDescription)); + CAStreamBasicDescription client_asbd; - OSStatus err = noErr; - size_t asbd_size = sizeof(AudioStreamBasicDescription); - - err = ExtAudioFileSetProperty (af, kExtAudioFileProperty_ClientDataFormat, asbd_size, &client_asbd); - if (err != noErr) { - error << string_compose(_("Could not detect samplerate for: %1"), name()) << endmsg; + try { + client_asbd = af.GetClientDataFormat (); + } catch (CAXException& cax) { + error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name); return 0.0; } @@ -228,4 +192,3 @@ CoreAudioSource::update_header (jack_nframes_t when, struct tm&, time_t) { return 0; } - -- cgit v1.2.3