summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaybin Rutkin <taybin@taybin.com>2006-09-01 01:59:41 +0000
committerTaybin Rutkin <taybin@taybin.com>2006-09-01 01:59:41 +0000
commit78e13c37f482552881c3b68ce08a80793df15152 (patch)
treef0f042b1efb402924c65e62638562bde84dfea15
parent9bc22f6f86deb4258faf298b45cb117f09af8f96 (diff)
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
-rw-r--r--gtk2_ardour/editing_syms.h9
-rw-r--r--gtk2_ardour/sfdb_ui.cc17
-rw-r--r--libs/appleutility/CAAudioFile.cpp1241
-rw-r--r--libs/appleutility/CAAudioFile.h439
-rw-r--r--libs/appleutility/CABufferList.cpp179
-rw-r--r--libs/appleutility/CABufferList.h300
-rw-r--r--libs/appleutility/CAXException.cpp45
-rw-r--r--libs/appleutility/CAXException.h158
-rw-r--r--libs/ardour/ardour/coreaudiosource.h5
-rw-r--r--libs/ardour/coreaudiosource.cc149
10 files changed, 2436 insertions, 106 deletions
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 <ardour/source_factory.h>
#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<string> 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 <algorithm>
+#include "CAHostTimeBase.h"
+#include "CADebugMacros.h"
+
+#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
+ #include <AudioToolbox/AudioToolbox.h>
+#else
+ #include <AudioFormat.h>
+#endif
+
+#if DEBUG
+ //#define VERBOSE_IO 1
+ //#define VERBOSE_CONVERTER 1
+ //#define VERBOSE_CHANNELMAP 1
+ //#define LOG_FUNCTION_ENTRIES 1
+
+ #if VERBOSE_CHANNELMAP
+ #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility
+ #endif
+#endif
+
+#if LOG_FUNCTION_ENTRIES
+ class FunctionLogger {
+ public:
+ FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) {
+ Indent();
+ printf("-> %s ", name);
+ if (fmt) {
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ }
+ printf("\n");
+ ++sIndent;
+ }
+ ~FunctionLogger() {
+ --sIndent;
+ Indent();
+ printf("<- %s\n", mName);
+ if (sIndent == 0)
+ printf("\n");
+ }
+
+ static void Indent() {
+ for (int i = sIndent; --i >= 0; ) {
+ putchar(' '); putchar(' ');
+ }
+ }
+
+ const char *mName;
+ static int sIndent;
+ };
+ int FunctionLogger::sIndent = 0;
+
+ #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
+#else
+ #define LOG_FUNCTION(name, format, foo)
+#endif
+
+static const UInt32 kDefaultIOBufferSizeBytes = 0x10000;
+
+#if CAAUDIOFILE_PROFILE
+ #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0
+ #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime)
+#else
+ #define StartTiming(af, starttime)
+ #define ElapsedTime(af, starttime, counter)
+#endif
+
+#define kNoMoreInputRightNow 'nein'
+
+// _______________________________________________________________________________________
+//
+CAAudioFile::CAAudioFile() :
+ mAudioFile(0),
+ mUseCache(false),
+ mFinishingEncoding(false),
+ mMode(kClosed),
+ mFileDataOffset(-1),
+ mFramesToSkipFollowingSeek(0),
+
+ mClientOwnsIOBuffer(false),
+ mPacketDescs(NULL),
+ mNumPacketDescs(0),
+ mConverter(NULL),
+ mMagicCookie(NULL),
+ mWriteBufferList(NULL)
+#if CAAUDIOFILE_PROFILE
+ ,
+ mProfiling(false),
+ mTicksInConverter(0),
+ mTicksInReadInConverter(0),
+ mTicksInIO(0),
+ mInConverter(false)
+#endif
+{
+ mIOBufferList.mBuffers[0].mData = NULL;
+ mIOBufferList.mBuffers[0].mDataByteSize = 0;
+ mClientMaxPacketSize = 0;
+ mIOBufferSizeBytes = kDefaultIOBufferSizeBytes;
+}
+
+// _______________________________________________________________________________________
+//
+CAAudioFile::~CAAudioFile()
+{
+ Close();
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::Close()
+{
+ LOG_FUNCTION("CAAudioFile::Close", NULL, NULL);
+ if (mMode == kClosed)
+ return;
+ if (mMode == kWriting)
+ FlushEncoder();
+ CloseConverter();
+ if (mAudioFile != 0 && mOwnOpenFile) {
+ AudioFileClose(mAudioFile);
+ mAudioFile = 0;
+ }
+ if (!mClientOwnsIOBuffer) {
+ delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
+ mIOBufferList.mBuffers[0].mData = NULL;
+ mIOBufferList.mBuffers[0].mDataByteSize = 0;
+ }
+ delete[] mPacketDescs; mPacketDescs = NULL; mNumPacketDescs = 0;
+ delete[] mMagicCookie; mMagicCookie = NULL;
+ delete mWriteBufferList; mWriteBufferList = NULL;
+ mMode = kClosed;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::CloseConverter()
+{
+ if (mConverter) {
+#if VERBOSE_CONVERTER
+ printf("CAAudioFile %p : CloseConverter\n", this);
+#endif
+ AudioConverterDispose(mConverter);
+ mConverter = NULL;
+ }
+}
+
+// =======================================================================================
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::Open(const FSRef &fsref)
+{
+ LOG_FUNCTION("CAAudioFile::Open", "%p", this);
+ XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
+ mFSRef = fsref;
+ XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file");
+ mOwnOpenFile = true;
+ mMode = kReading;
+ GetExistingFileInfo();
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::Wrap(AudioFileID fileID, bool forWriting)
+{
+ LOG_FUNCTION("CAAudioFile::Wrap", "%p", this);
+ XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
+
+ mAudioFile = fileID;
+ mOwnOpenFile = false;
+ mMode = forWriting ? kPreparingToWrite : kReading;
+ GetExistingFileInfo();
+ if (forWriting)
+ FileFormatChanged();
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout)
+{
+ LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this);
+ XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
+
+ mFileDataFormat = dataFormat;
+ if (layout) {
+ mFileChannelLayout = layout;
+#if VERBOSE_CHANNELMAP
+ printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ }
+ mMode = kPreparingToCreate;
+ FileFormatChanged(&parentDir, filename, filetype);
+}
+
+// _______________________________________________________________________________________
+//
+// called to create the file -- or update its format/channel layout/properties based on an encoder
+// setting change
+void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype)
+{
+ LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this);
+ XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared");
+
+ UInt32 propertySize;
+ OSStatus err;
+ AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat;
+
+#if VERBOSE_CONVERTER
+ mFileDataFormat.PrintFormat(stdout, "", "Specified file data format");
+#endif
+
+ // Find out the actual format the converter will produce. This is necessary in
+ // case the bitrate has forced a lower sample rate, which needs to be set correctly
+ // in the stream description passed to AudioFileCreate.
+ if (mConverter != NULL) {
+ propertySize = sizeof(AudioStreamBasicDescription);
+ Float64 origSampleRate = mFileDataFormat.mSampleRate;
+ XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description");
+ // do the same for the channel layout being output by the converter
+#if VERBOSE_CONVERTER
+ mFileDataFormat.PrintFormat(stdout, "", "Converter output");
+#endif
+ if (fiszero(mFileDataFormat.mSampleRate))
+ mFileDataFormat.mSampleRate = origSampleRate;
+ err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL);
+ if (err == noErr && propertySize > 0) {
+ AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
+ err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout);
+ if (err) {
+ free(layout);
+ XThrow(err, "couldn't get audio converter's output channel layout");
+ }
+ mFileChannelLayout = layout;
+#if VERBOSE_CHANNELMAP
+ printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ free(layout);
+ }
+ }
+
+ // create the output file
+ if (mMode == kPreparingToCreate) {
+ CAStreamBasicDescription newFileDataFormat = mFileDataFormat;
+ if (fiszero(newFileDataFormat.mSampleRate))
+ newFileDataFormat.mSampleRate = 44100; // just make something up for now
+#if VERBOSE_CONVERTER
+ newFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
+#endif
+ XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file");
+ mMode = kPreparingToWrite;
+ mOwnOpenFile = true;
+ } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) {
+ // second check must be explicit since operator== on ASBD treats SR of zero as "don't care"
+ if (fiszero(mFileDataFormat.mSampleRate))
+ mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate;
+#if VERBOSE_CONVERTER
+ mFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
+#endif
+ XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0");
+ XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format");
+ }
+
+ UInt32 deferSizeUpdates = 1;
+ err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates);
+
+ if (mConverter != NULL) {
+ // encoder
+ // get the magic cookie, if any, from the converter
+ delete[] mMagicCookie; mMagicCookie = NULL;
+ mMagicCookieSize = 0;
+
+ err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL);
+
+ // we can get a noErr result and also a propertySize == 0
+ // -- if the file format does support magic cookies, but this file doesn't have one.
+ if (err == noErr && propertySize > 0) {
+ mMagicCookie = new Byte[propertySize];
+ XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie");
+ mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size
+ // now set the magic cookie on the output file
+ UInt32 willEatTheCookie = false;
+ // the converter wants to give us one; will the file take it?
+ err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData,
+ NULL, &willEatTheCookie);
+ if (err == noErr && willEatTheCookie) {
+#if VERBOSE_CONVERTER
+ printf("Setting cookie on encoded file\n");
+#endif
+ XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie");
+ }
+ }
+
+ // get maximum packet size
+ propertySize = sizeof(UInt32);
+ XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size");
+
+ AllocateBuffers(true /* okToFail */);
+ } else {
+ InitFileMaxPacketSize();
+ }
+
+ if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) {
+ // don't bother tagging mono/stereo files
+ UInt32 isWritable;
+ err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable);
+ if (!err && isWritable) {
+#if VERBOSE_CHANNELMAP
+ printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout,
+ mFileChannelLayout.Size(), &mFileChannelLayout.Layout());
+ if (err)
+ CAXException::Warning("could not set the file's channel layout", err);
+ } else {
+#if VERBOSE_CHANNELMAP
+ printf("file won't accept a channel layout (write)\n");
+#endif
+ }
+ }
+
+ UpdateClientMaxPacketSize(); // also sets mFrame0Offset
+ mPacketMark = 0;
+ mFrameMark = 0;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::InitFileMaxPacketSize()
+{
+ LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this);
+ UInt32 propertySize = sizeof(UInt32);
+ OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize,
+ &propertySize, &mFileMaxPacketSize);
+ if (err) {
+ // workaround for 3361377: not all file formats' maximum packet sizes are supported
+ if (!mFileDataFormat.IsPCM())
+ XThrowIfError(err, "get audio file's maximum packet size");
+ mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame;
+ }
+ AllocateBuffers(true /* okToFail */);
+}
+
+
+// _______________________________________________________________________________________
+//
+SInt64 CAAudioFile::FileDataOffset()
+{
+ if (mFileDataOffset < 0) {
+ UInt32 propertySize = sizeof(SInt64);
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset");
+ }
+ return mFileDataOffset;
+}
+
+// _______________________________________________________________________________________
+//
+SInt64 CAAudioFile::GetNumberFrames() const
+{
+ AudioFilePacketTableInfo pti;
+ UInt32 propertySize = sizeof(pti);
+ OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
+ if (err == noErr)
+ return pti.mNumberValidFrames;
+ return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::SetNumberFrames(SInt64 nFrames)
+{
+ XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM");
+ XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file");
+}
+
+// _______________________________________________________________________________________
+//
+// call for existing file, NOT new one - from Open() or Wrap()
+void CAAudioFile::GetExistingFileInfo()
+{
+ LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this);
+ UInt32 propertySize;
+ OSStatus err;
+
+ // get mFileDataFormat
+ propertySize = sizeof(AudioStreamBasicDescription);
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format");
+
+ // get mFileChannelLayout
+ err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL);
+ if (err == noErr && propertySize > 0) {
+ AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
+ err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout);
+ if (err == noErr) {
+ mFileChannelLayout = layout;
+#if VERBOSE_CHANNELMAP
+ printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ }
+ free(layout);
+ XThrowIfError(err, "get audio file's channel layout");
+ }
+ if (mMode != kReading)
+ return;
+
+#if 0
+ // get mNumberPackets
+ propertySize = sizeof(mNumberPackets);
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count");
+#if VERBOSE_IO
+ printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets);
+#endif
+#endif
+
+ // get mMagicCookie
+ err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
+ if (err == noErr && propertySize > 0) {
+ mMagicCookie = new Byte[propertySize];
+ mMagicCookieSize = propertySize;
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie");
+ }
+ InitFileMaxPacketSize();
+ mPacketMark = 0;
+ mFrameMark = 0;
+
+ UpdateClientMaxPacketSize();
+}
+
+// =======================================================================================
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout)
+{
+ LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this);
+ mFileChannelLayout = layout;
+#if VERBOSE_CHANNELMAP
+ printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ if (mMode != kReading)
+ FileFormatChanged();
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
+{
+ LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this);
+ XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file");
+
+ bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat);
+
+ if (dataFormatChanging) {
+ CloseConverter();
+ if (mWriteBufferList) {
+ delete mWriteBufferList;
+ mWriteBufferList = NULL;
+ }
+ mClientDataFormat = dataFormat;
+ }
+
+ if (layout && layout->IsValid()) {
+ XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map");
+ mClientChannelLayout = *layout;
+ }
+
+ bool differentLayouts;
+ if (mClientChannelLayout.IsValid()) {
+ if (mFileChannelLayout.IsValid()) {
+ differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag();
+#if VERBOSE_CHANNELMAP
+ printf("two valid layouts, %s\n", differentLayouts ? "different" : "same");
+#endif
+ } else {
+ differentLayouts = false;
+#if VERBOSE_CHANNELMAP
+ printf("valid client layout, unknown file layout\n");
+#endif
+ }
+ } else {
+ differentLayouts = false;
+#if VERBOSE_CHANNELMAP
+ if (mFileChannelLayout.IsValid())
+ printf("valid file layout, unknown client layout\n");
+ else
+ printf("two invalid layouts\n");
+#endif
+ }
+
+ if (mClientDataFormat != mFileDataFormat || differentLayouts) {
+ // We need an AudioConverter.
+ if (mMode == kReading) {
+ // file -> client (decode)
+//mFileDataFormat.PrintFormat( stdout, "", "File: ");
+//mClientDataFormat.PrintFormat(stdout, "", "Client: ");
+
+ if (mConverter == NULL)
+ XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter),
+ "create audio converter");
+
+#if VERBOSE_CONVERTER
+ printf("CAAudioFile %p -- created converter\n", this);
+ CAShow(mConverter);
+#endif
+ // set the magic cookie, if any (for decode)
+ if (mMagicCookie)
+ SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM());
+ // we get cookies from some AIFF's but the converter barfs on them,
+ // so we set canFail to true for PCM
+
+ SetConverterChannelLayout(false, mFileChannelLayout);
+ SetConverterChannelLayout(true, mClientChannelLayout);
+
+ // propagate leading/trailing frame counts
+ if (mFileDataFormat.mBitsPerChannel == 0) {
+ UInt32 propertySize;
+ OSStatus err;
+ AudioFilePacketTableInfo pti;
+ propertySize = sizeof(pti);
+ err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
+ if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) {
+ AudioConverterPrimeInfo primeInfo;
+ primeInfo.leadingFrames = pti.mPrimingFrames;
+ primeInfo.trailingFrames = pti.mRemainderFrames;
+ /* ignore any error. better to play it at all than not. */
+ /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo);
+ //XThrowIfError(err, "couldn't set prime info on converter");
+ }
+ }
+ } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) {
+ // client -> file (encode)
+ if (mConverter == NULL)
+ XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter");
+ mWriteBufferList = CABufferList::New("", mClientDataFormat);
+ SetConverterChannelLayout(false, mClientChannelLayout);
+ SetConverterChannelLayout(true, mFileChannelLayout);
+ if (mMode == kPreparingToWrite)
+ FileFormatChanged();
+ } else
+ XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known");
+ }
+ UpdateClientMaxPacketSize();
+}
+
+// _______________________________________________________________________________________
+//
+OSStatus CAAudioFile::SetConverterProperty(
+ AudioConverterPropertyID inPropertyID,
+ UInt32 inPropertyDataSize,
+ const void* inPropertyData,
+ bool inCanFail)
+{
+ OSStatus err = noErr;
+ //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
+ if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL)
+ ;
+ else {
+ err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData);
+ if (!inCanFail) {
+ XThrowIfError(err, "set audio converter property");
+ }
+ }
+ UpdateClientMaxPacketSize();
+ if (mMode == kPreparingToWrite)
+ FileFormatChanged();
+ return err;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout)
+{
+ LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
+ OSStatus err;
+
+ if (layout.IsValid()) {
+#if VERBOSE_CHANNELMAP
+ printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input",
+ CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
+#endif
+ if (output) {
+ err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout,
+ layout.Size(), &layout.Layout());
+ XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout");
+ } else {
+ err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout,
+ layout.Size(), &layout.Layout());
+ XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout");
+ }
+ if (mMode == kPreparingToWrite)
+ FileFormatChanged();
+ }
+}
+
+// _______________________________________________________________________________________
+//
+CFArrayRef CAAudioFile::GetConverterConfig()
+{
+ CFArrayRef plist;
+ UInt32 propertySize = sizeof(plist);
+ XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings");
+ return plist;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::UpdateClientMaxPacketSize()
+{
+ LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
+ mFrame0Offset = 0;
+ if (mConverter != NULL) {
+ AudioConverterPropertyID property = (mMode == kReading) ?
+ kAudioConverterPropertyMaximumOutputPacketSize :
+ kAudioConverterPropertyMaximumInputPacketSize;
+
+ UInt32 propertySize = sizeof(UInt32);
+ XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize),
+ "get audio converter's maximum packet size");
+
+ if (mFileDataFormat.mBitsPerChannel == 0) {
+ AudioConverterPrimeInfo primeInfo;
+ propertySize = sizeof(primeInfo);
+ OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
+ if (err == noErr)
+ mFrame0Offset = primeInfo.leadingFrames;
+#if VERBOSE_CONVERTER
+ printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset);
+#endif
+ }
+ } else {
+ mClientMaxPacketSize = mFileMaxPacketSize;
+ }
+}
+
+// _______________________________________________________________________________________
+// Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs
+// Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes
+void CAAudioFile::AllocateBuffers(bool okToFail)
+{
+ LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this);
+ if (mFileMaxPacketSize == 0) {
+ if (okToFail)
+ return;
+ XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0");
+ }
+ UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize);
+ // must be big enough for at least one maximum size packet
+
+ if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) {
+ mIOBufferList.mNumberBuffers = 1;
+ mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame;
+ if (!mClientOwnsIOBuffer) {
+ //printf("reallocating I/O buffer\n");
+ delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
+ mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes];
+ }
+ mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes;
+ mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize;
+ }
+
+ UInt32 propertySize = sizeof(UInt32);
+ UInt32 externallyFramed;
+ XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed,
+ sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed),
+ "is format externally framed");
+ if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) {
+ delete[] mPacketDescs;
+ mPacketDescs = NULL;
+ mNumPacketDescs = 0;
+
+ if (externallyFramed) {
+ //printf("reallocating packet descs\n");
+ mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets];
+ mNumPacketDescs = mIOBufferSizePackets;
+ }
+ }
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::SetIOBuffer(void *buf)
+{
+ if (!mClientOwnsIOBuffer)
+ delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
+ mIOBufferList.mBuffers[0].mData = buf;
+
+ if (buf == NULL) {
+ mClientOwnsIOBuffer = false;
+ SetIOBufferSizeBytes(mIOBufferSizeBytes);
+ } else {
+ mClientOwnsIOBuffer = true;
+ AllocateBuffers();
+ }
+// printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
+}
+
+// ===============================================================================
+
+/*
+For Tiger:
+added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket.
+You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty.
+
+ kAudioFilePropertyPacketToFrame = 'pkfr',
+ // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored.
+ kAudioFilePropertyFrameToPacket = 'frpk',
+ // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back.
+
+struct AudioFramePacketTranslation
+{
+ SInt64 mFrame;
+ SInt64 mPacket;
+ UInt32 mFrameOffsetInPacket;
+};
+*/
+
+SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const
+{
+ AudioFramePacketTranslation trans;
+ UInt32 propertySize;
+
+ switch (mFileDataFormat.mFramesPerPacket) {
+ case 1:
+ return packet;
+ case 0:
+ trans.mPacket = packet;
+ propertySize = sizeof(trans);
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans),
+ "packet <-> frame translation unimplemented for format with variable frames/packet");
+ return trans.mFrame;
+ }
+ return packet * mFileDataFormat.mFramesPerPacket;
+}
+
+SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const
+{
+ AudioFramePacketTranslation trans;
+ UInt32 propertySize;
+
+ switch (mFileDataFormat.mFramesPerPacket) {
+ case 1:
+ return inFrame;
+ case 0:
+ trans.mFrame = inFrame;
+ propertySize = sizeof(trans);
+ XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans),
+ "packet <-> frame translation unimplemented for format with variable frames/packet");
+ return trans.mPacket;
+ }
+ return inFrame / mFileDataFormat.mFramesPerPacket;
+}
+
+// _______________________________________________________________________________________
+//
+
+SInt64 CAAudioFile::Tell() const // frameNumber
+{
+ return mFrameMark - mFrame0Offset;
+}
+
+void CAAudioFile::SeekToPacket(SInt64 packetNumber)
+{
+#if VERBOSE_IO
+ printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber);
+#endif
+ XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file");
+ if (mPacketMark == packetNumber)
+ return; // already there! don't reset converter
+ mPacketMark = packetNumber;
+
+ mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset;
+ mFramesToSkipFollowingSeek = 0;
+ if (mConverter)
+ // must reset -- if we reached end of stream. converter will no longer work otherwise
+ AudioConverterReset(mConverter);
+}
+
+/*
+ Example: AAC, 1024 frames/packet, 2112 frame offset
+
+ 2112
+ |
+ Absolute frames: 0 1024 2048 | 3072
+ +---------+---------+--|------+---------+---------+
+ Packets: | 0 | 1 | | 2 | 3 | 4 |
+ +---------+---------+--|------+---------+---------+
+ Client frames: -2112 -1088 -64 | 960 SeekToFrame, TellFrame
+ |
+ 0
+
+ * Offset between absolute and client frames is mFrame0Offset.
+ *** mFrameMark is in client frames ***
+
+ Examples:
+ clientFrame 0 960 1000 1024
+ absoluteFrame 2112 3072 3112 3136
+ packet 0 0 0 1
+ tempFrameMark* -2112 -2112 -2112 -1088
+ mFramesToSkipFollowingSeek 2112 3072 3112 2112
+*/
+void CAAudioFile::Seek(SInt64 clientFrame)
+{
+ if (clientFrame == mFrameMark)
+ return; // already there! don't reset converter
+
+ //SInt64 absoluteFrame = clientFrame + mFrame0Offset;
+ XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file");
+
+#if VERBOSE_IO
+ SInt64 prevFrameMark = mFrameMark;
+#endif
+
+ SInt64 packet;
+ packet = FrameToPacket(clientFrame);
+ if (packet < 0)
+ packet = 0;
+ SeekToPacket(packet);
+ // this will have backed up mFrameMark to match the beginning of the packet
+ mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0));
+ mFrameMark = clientFrame;
+
+#if VERBOSE_IO
+ printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek);
+#endif
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData)
+ // May read fewer packets than requested if:
+ // buffer is not big enough
+ // file does not contain that many more packets
+ // Note that eofErr is not fatal, just results in 0 packets returned
+ // ioData's buffer sizes may be shortened
+{
+ XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0");
+ if (mIOBufferList.mBuffers[0].mData == NULL) {
+#if DEBUG
+ printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
+#endif
+ AllocateBuffers();
+ }
+ UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize;
+ UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize;
+ // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check
+ UInt32 nPackets = std::min(ioNumPackets, maxNumPackets);
+
+ mMaxPacketsToRead = ~0UL;
+
+ if (mClientDataFormat.mFramesPerPacket == 1) { // PCM or equivalent
+ while (mFramesToSkipFollowingSeek > 0) {
+ UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets);
+ UInt32 framesPerPacket;
+ if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0)
+ mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket;
+
+ if (mConverter == NULL) {
+ XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file");
+ } else {
+#if CAAUDIOFILE_PROFILE
+ mInConverter = true;
+#endif
+ StartTiming(this, fill);
+ XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)");
+ ElapsedTime(this, fill, mTicksInConverter);
+#if CAAUDIOFILE_PROFILE
+ mInConverter = false;
+#endif
+ }
+ if (skipFrames == 0) { // hit EOF
+ ioNumPackets = 0;
+ return;
+ }
+ mFrameMark += skipFrames;
+#if VERBOSE_IO
+ printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames);
+#endif
+
+ mFramesToSkipFollowingSeek -= skipFrames;
+
+ // restore mDataByteSize
+ for (int i = ioData->mNumberBuffers; --i >= 0 ; )
+ ioData->mBuffers[i].mDataByteSize = bufferSizeBytes;
+ }
+ }
+
+ if (mFileDataFormat.mFramesPerPacket > 0)
+ // don't read more packets than we are being asked to produce
+ mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1;
+ if (mConverter == NULL) {
+ XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file");
+ } else {
+#if CAAUDIOFILE_PROFILE
+ mInConverter = true;
+#endif
+ StartTiming(this, fill);
+ XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)");
+ ElapsedTime(this, fill, mTicksInConverter);
+#if CAAUDIOFILE_PROFILE
+ mInConverter = false;
+#endif
+ }
+ if (mClientDataFormat.mFramesPerPacket == 1)
+ mFrameMark += nPackets;
+
+ ioNumPackets = nPackets;
+}
+
+// _______________________________________________________________________________________
+//
+OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter,
+ UInt32* ioNumberDataPackets,
+ AudioBufferList* ioData,
+ AudioStreamPacketDescription** outDataPacketDescription,
+ void* inUserData)
+{
+ CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
+
+#if 0
+ SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark;
+ if (remainingPacketsInFile <= 0) {
+ *ioNumberDataPackets = 0;
+ ioData->mBuffers[0].mDataByteSize = 0;
+ if (outDataPacketDescription)
+ *outDataPacketDescription = This->mPacketDescs;
+#if VERBOSE_IO
+ printf("CAAudioFile::ReadInputProc: EOF\n");
+#endif
+ return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes
+ }
+#endif
+
+ // determine how much to read
+ AudioBufferList *readBuffer;
+ UInt32 readPackets;
+ if (inAudioConverter != NULL) {
+ // getting called from converter, need to use our I/O buffer
+ readBuffer = &This->mIOBufferList;
+ readPackets = This->mIOBufferSizePackets;
+ } else {
+ // getting called directly from ReadPackets, use supplied buffer
+ if (This->mFileMaxPacketSize == 0)
+ return kExtAudioFileError_MaxPacketSizeUnknown;
+ readBuffer = ioData;
+ readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize);
+ // don't attempt to read more packets than will fit in the buffer
+ }
+ // don't try to read past EOF
+// if (readPackets > remainingPacketsInFile)
+// readPackets = remainingPacketsInFile;
+ // don't read more packets than necessary to produce the requested amount of converted data
+ if (readPackets > This->mMaxPacketsToRead) {
+#if VERBOSE_IO
+ printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets);
+#endif
+ readPackets = This->mMaxPacketsToRead;
+ }
+
+ // read
+ UInt32 bytesRead;
+ OSStatus err;
+
+ StartTiming(This, read);
+ StartTiming(This, readinconv);
+ err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData);
+#if CAAUDIOFILE_PROFILE
+ if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter);
+#endif
+ ElapsedTime(This, read, This->mTicksInIO);
+
+ if (err) {
+ DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err);
+ return err;
+ }
+
+#if VERBOSE_IO
+ printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err);
+#if VERBOSE_IO >= 2
+ if (This->mPacketDescs) {
+ for (UInt32 i = 0; i < readPackets; ++i) {
+ printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize);
+ }
+ }
+ printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4);
+#endif
+#endif
+ if (readPackets == 0) {
+ *ioNumberDataPackets = 0;
+ ioData->mBuffers[0].mDataByteSize = 0;
+ return noErr;
+ }
+
+ if (outDataPacketDescription)
+ *outDataPacketDescription = This->mPacketDescs;
+ ioData->mBuffers[0].mDataByteSize = bytesRead;
+ ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData;
+
+ This->mPacketMark += readPackets;
+ if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read
+ // but for non-PCM client format (weird case) we must update here/now
+ if (This->mFileDataFormat.mFramesPerPacket > 0)
+ This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket;
+ else {
+ for (UInt32 i = 0; i < readPackets; ++i)
+ This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket;
+ }
+ }
+ *ioNumberDataPackets = readPackets;
+ return noErr;
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data)
+{
+ if (mIOBufferList.mBuffers[0].mData == NULL) {
+#if DEBUG
+ printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
+#endif
+ AllocateBuffers();
+ }
+
+ if (mMode == kPreparingToWrite)
+ mMode = kWriting;
+ else
+ XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file");
+ if (mConverter != NULL) {
+ mWritePackets = numPackets;
+ mWriteBufferList->SetFrom(data);
+ WritePacketsFromCallback(WriteInputProc, this);
+ } else {
+ StartTiming(this, write);
+ XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize,
+ NULL, mPacketMark, &numPackets, data->mBuffers[0].mData),
+ "write audio file");
+ ElapsedTime(this, write, mTicksInIO);
+#if VERBOSE_IO
+ printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize);
+#endif
+ //mNumberPackets =
+ mPacketMark += numPackets;
+ if (mFileDataFormat.mFramesPerPacket > 0)
+ mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket;
+ // else: shouldn't happen since we're only called when there's no converter
+ }
+}
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::FlushEncoder()
+{
+ if (mConverter != NULL) {
+ mFinishingEncoding = true;
+ WritePacketsFromCallback(WriteInputProc, this);
+ mFinishingEncoding = false;
+
+ // get priming info from converter, set it on the file
+ if (mFileDataFormat.mBitsPerChannel == 0) {
+ UInt32 propertySize;
+ OSStatus err;
+ AudioConverterPrimeInfo primeInfo;
+ propertySize = sizeof(primeInfo);
+
+ err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
+ if (err == noErr) {
+ AudioFilePacketTableInfo pti;
+ propertySize = sizeof(pti);
+ err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
+ if (err == noErr) {
+//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
+ UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames;
+ pti.mPrimingFrames = primeInfo.leadingFrames;
+ pti.mRemainderFrames = primeInfo.trailingFrames;
+ pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames;
+//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
+ XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file");
+ }
+ }
+ }
+ }
+}
+
+// _______________________________________________________________________________________
+//
+OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/,
+ UInt32 * ioNumberDataPackets,
+ AudioBufferList* ioData,
+ AudioStreamPacketDescription ** outDataPacketDescription,
+ void* inUserData)
+{
+ CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
+ if (This->mFinishingEncoding) {
+ *ioNumberDataPackets = 0;
+ ioData->mBuffers[0].mDataByteSize = 0;
+ ioData->mBuffers[0].mData = NULL;
+ if (outDataPacketDescription)
+ *outDataPacketDescription = NULL;
+ return noErr;
+ }
+ UInt32 numPackets = This->mWritePackets;
+ if (numPackets == 0) {
+ return kNoMoreInputRightNow;
+ }
+ This->mWriteBufferList->ToAudioBufferList(ioData);
+ This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame);
+ *ioNumberDataPackets = numPackets;
+ if (outDataPacketDescription)
+ *outDataPacketDescription = NULL;
+ This->mWritePackets -= numPackets;
+ return noErr;
+}
+
+// _______________________________________________________________________________________
+//
+#if VERBOSE_IO
+static void hexdump(const void *addr, long len)
+{
+ const Byte *p = (Byte *)addr;
+ UInt32 offset = 0;
+
+ if (len > 0x400) len = 0x400;
+
+ while (len > 0) {
+ int n = len > 16 ? 16 : len;
+ printf("%08lX: ", offset);
+ for (int i = 0; i < 16; ++i)
+ if (i < n)
+ printf("%02X ", p[i]);
+ else printf(" ");
+ for (int i = 0; i < 16; ++i)
+ if (i < n)
+ putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.');
+ else putchar(' ');
+ putchar('\n');
+ p += 16;
+ len -= 16;
+ offset += 16;
+ }
+}
+#endif
+
+// _______________________________________________________________________________________
+//
+void CAAudioFile::WritePacketsFromCallback(
+ AudioConverterComplexInputDataProc inInputDataProc,
+ void * inInputDataProcUserData)
+{
+ while (true) {
+ // keep writing until we exhaust the input (temporary stop), or produce no output (EOF)
+ UInt32 numEncodedPackets = mIOBufferSizePackets;
+ mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes;
+#if CAAUDIOFILE_PROFILE
+ mInConverter = true;
+#endif
+ StartTiming(this, fill);
+ OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData,
+ &numEncodedPackets, &mIOBufferList, mPacketDescs);
+ ElapsedTime(this, fill, mTicksInConverter);
+#if CAAUDIOFILE_PROFILE
+ mInConverter = false;
+#endif
+ XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)");
+ if (numEncodedPackets == 0)
+ break;
+ Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData;
+#if VERBOSE_IO
+ printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize);
+ if (mPacketDescs) {
+ for (UInt32 i = 0; i < numEncodedPackets; ++i) {
+ printf(" write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
+#if VERBOSE_IO >= 2
+ hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
+#endif
+ }
+ }
+#endif
+ StartTiming(this, write);
+ XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file");
+ ElapsedTime(this, write, mTicksInIO);
+ mPacketMark += numEncodedPackets;
+ //mNumberPackets += numEncodedPackets;
+ if (mFileDataFormat.mFramesPerPacket > 0)
+ mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket;
+ else {
+ for (UInt32 i = 0; i < numEncodedPackets; ++i)
+ mFrameMark += mPacketDescs[i].mVariableFramesInPacket;
+ }
+ if (err == kNoMoreInputRightNow)
+ break;
+ }
+}
+
+#endif // !CAAF_USE_EXTAUDIOFILE
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 <AvailabilityMacros.h>
+
+#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
+ #include <AudioToolbox/AudioToolbox.h>
+#else
+ #include <AudioToolbox.h>
+#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 <AudioToolbox/ExtendedAudioFile.h>
+ #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 <CoreServices/CoreServices.h>
+#else
+ #include <Endian.h>
+#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 <stddef.h>
+#include "CAStreamBasicDescription.h"
+#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
+ #include <CoreServices/CoreServices.h>
+#else
+ #include <AssertMacros.h>
+#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 <CoreServices/CoreServices.h>
+#else
+ #include <ConditionalMacros.h>
+ #include <CoreServices.h>
+#endif
+#include "CADebugMacros.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+// 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 <appleutility/CAAudioFile.h>
+
#include <ardour/audiofilesource.h>
-#include <AudioToolbox/ExtendedAudioFile.h>
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 <pbd/error.h>
#include <ardour/coreaudiosource.h>
+#include <appleutility/CAAudioFile.h>
+#include <appleutility/CAStreamBasicDescription.h>
+
#include "i18n.h"
#include <AudioToolbox/AudioFormat.h>
@@ -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;
}
-