From 7bd41538d951c3e476655df741adfbebbb990bde Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 19 Sep 2006 03:29:16 +0000 Subject: Merged with trunk R920. git-svn-id: svn://localhost/ardour2/branches/midi@921 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/appleutility/CAAudioFile.cpp | 1241 +++++++++++++++++++++++++++ libs/appleutility/CAAudioFile.h | 439 ++++++++++ libs/appleutility/CABufferList.cpp | 179 ++++ libs/appleutility/CABufferList.h | 300 +++++++ libs/appleutility/CAXException.cpp | 45 + libs/appleutility/CAXException.h | 158 ++++ libs/ardour/SConscript | 28 +- libs/ardour/ardour/audio_diskstream.h | 2 - libs/ardour/ardour/audiofilesource.h | 9 +- libs/ardour/ardour/audioplaylist.h | 2 + libs/ardour/ardour/audioregion.h | 1 + libs/ardour/ardour/audiosource.h | 4 +- libs/ardour/ardour/automation_event.h | 4 +- libs/ardour/ardour/coreaudiosource.h | 9 +- libs/ardour/ardour/destructive_filesource.h | 6 +- libs/ardour/ardour/diskstream.h | 2 - libs/ardour/ardour/io.h | 3 - libs/ardour/ardour/location.h | 6 - libs/ardour/ardour/midi_source.h | 4 +- libs/ardour/ardour/playlist.h | 3 - libs/ardour/ardour/port.h | 2 +- libs/ardour/ardour/region.h | 3 - libs/ardour/ardour/session.h | 9 +- libs/ardour/ardour/smf_source.h | 4 +- libs/ardour/ardour/sndfilesource.h | 6 +- libs/ardour/ardour/source.h | 11 +- libs/ardour/ardour/source_factory.h | 9 +- libs/ardour/ardour/tempo.h | 3 - libs/ardour/audio_diskstream.cc | 7 +- libs/ardour/audio_playlist.cc | 1 + libs/ardour/audioengine.cc | 5 + libs/ardour/audiofilesource.cc | 24 +- libs/ardour/audiofilter.cc | 2 +- libs/ardour/audioregion.cc | 23 +- libs/ardour/audiosource.cc | 8 +- libs/ardour/coreaudiosource.cc | 157 ++-- libs/ardour/destructive_filesource.cc | 12 +- libs/ardour/globals.cc | 4 +- libs/ardour/import.cc | 2 +- libs/ardour/io.cc | 5 +- libs/ardour/location.cc | 5 + libs/ardour/midi_source.cc | 8 +- libs/ardour/panner.cc | 2 +- libs/ardour/plugin.cc | 4 +- libs/ardour/route.cc | 14 +- libs/ardour/session.cc | 74 +- libs/ardour/session_state.cc | 175 ++-- libs/ardour/session_time.cc | 4 +- libs/ardour/session_timefx.cc | 3 +- libs/ardour/smf_source.cc | 8 +- libs/ardour/sndfilesource.cc | 16 +- libs/ardour/source.cc | 13 +- libs/ardour/source_factory.cc | 81 +- libs/ardour/tempo.cc | 30 + libs/ardour/utils.cc | 2 +- libs/ardour/vst_plugin.cc | 4 +- libs/gtkmm2ext/dndtreeview.cc | 7 +- libs/pbd/pbd/stateful.h | 2 +- 58 files changed, 2791 insertions(+), 403 deletions(-) create mode 100644 libs/appleutility/CAAudioFile.cpp create mode 100644 libs/appleutility/CAAudioFile.h create mode 100644 libs/appleutility/CABufferList.cpp create mode 100644 libs/appleutility/CABufferList.h create mode 100644 libs/appleutility/CAXException.cpp create mode 100644 libs/appleutility/CAXException.h (limited to 'libs') diff --git a/libs/appleutility/CAAudioFile.cpp b/libs/appleutility/CAAudioFile.cpp new file mode 100644 index 0000000000..e1e39b0ec9 --- /dev/null +++ b/libs/appleutility/CAAudioFile.cpp @@ -0,0 +1,1241 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAudioFile.cpp + +=============================================================================*/ + +#include "CAAudioFile.h" + +#if !CAAF_USE_EXTAUDIOFILE + +#include "CAXException.h" +#include +#include "CAHostTimeBase.h" +#include "CADebugMacros.h" + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#if DEBUG + //#define VERBOSE_IO 1 + //#define VERBOSE_CONVERTER 1 + //#define VERBOSE_CHANNELMAP 1 + //#define LOG_FUNCTION_ENTRIES 1 + + #if VERBOSE_CHANNELMAP + #include "CAChannelLayouts.h" // this is in Source/Tests/AudioFileTools/Utility + #endif +#endif + +#if LOG_FUNCTION_ENTRIES + class FunctionLogger { + public: + FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) { + Indent(); + printf("-> %s ", name); + if (fmt) { + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + } + printf("\n"); + ++sIndent; + } + ~FunctionLogger() { + --sIndent; + Indent(); + printf("<- %s\n", mName); + if (sIndent == 0) + printf("\n"); + } + + static void Indent() { + for (int i = sIndent; --i >= 0; ) { + putchar(' '); putchar(' '); + } + } + + const char *mName; + static int sIndent; + }; + int FunctionLogger::sIndent = 0; + + #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__); +#else + #define LOG_FUNCTION(name, format, foo) +#endif + +static const UInt32 kDefaultIOBufferSizeBytes = 0x10000; + +#if CAAUDIOFILE_PROFILE + #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0 + #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime) +#else + #define StartTiming(af, starttime) + #define ElapsedTime(af, starttime, counter) +#endif + +#define kNoMoreInputRightNow 'nein' + +// _______________________________________________________________________________________ +// +CAAudioFile::CAAudioFile() : + mAudioFile(0), + mUseCache(false), + mFinishingEncoding(false), + mMode(kClosed), + mFileDataOffset(-1), + mFramesToSkipFollowingSeek(0), + + mClientOwnsIOBuffer(false), + mPacketDescs(NULL), + mNumPacketDescs(0), + mConverter(NULL), + mMagicCookie(NULL), + mWriteBufferList(NULL) +#if CAAUDIOFILE_PROFILE + , + mProfiling(false), + mTicksInConverter(0), + mTicksInReadInConverter(0), + mTicksInIO(0), + mInConverter(false) +#endif +{ + mIOBufferList.mBuffers[0].mData = NULL; + mIOBufferList.mBuffers[0].mDataByteSize = 0; + mClientMaxPacketSize = 0; + mIOBufferSizeBytes = kDefaultIOBufferSizeBytes; +} + +// _______________________________________________________________________________________ +// +CAAudioFile::~CAAudioFile() +{ + Close(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Close() +{ + LOG_FUNCTION("CAAudioFile::Close", NULL, NULL); + if (mMode == kClosed) + return; + if (mMode == kWriting) + FlushEncoder(); + CloseConverter(); + if (mAudioFile != 0 && mOwnOpenFile) { + AudioFileClose(mAudioFile); + mAudioFile = 0; + } + if (!mClientOwnsIOBuffer) { + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = NULL; + mIOBufferList.mBuffers[0].mDataByteSize = 0; + } + delete[] mPacketDescs; mPacketDescs = NULL; mNumPacketDescs = 0; + delete[] mMagicCookie; mMagicCookie = NULL; + delete mWriteBufferList; mWriteBufferList = NULL; + mMode = kClosed; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::CloseConverter() +{ + if (mConverter) { +#if VERBOSE_CONVERTER + printf("CAAudioFile %p : CloseConverter\n", this); +#endif + AudioConverterDispose(mConverter); + mConverter = NULL; + } +} + +// ======================================================================================= + +// _______________________________________________________________________________________ +// +void CAAudioFile::Open(const FSRef &fsref) +{ + LOG_FUNCTION("CAAudioFile::Open", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + mFSRef = fsref; + XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file"); + mOwnOpenFile = true; + mMode = kReading; + GetExistingFileInfo(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Wrap(AudioFileID fileID, bool forWriting) +{ + LOG_FUNCTION("CAAudioFile::Wrap", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + + mAudioFile = fileID; + mOwnOpenFile = false; + mMode = forWriting ? kPreparingToWrite : kReading; + GetExistingFileInfo(); + if (forWriting) + FileFormatChanged(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout) +{ + LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this); + XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open"); + + mFileDataFormat = dataFormat; + if (layout) { + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + } + mMode = kPreparingToCreate; + FileFormatChanged(&parentDir, filename, filetype); +} + +// _______________________________________________________________________________________ +// +// called to create the file -- or update its format/channel layout/properties based on an encoder +// setting change +void CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype) +{ + LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this); + XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared"); + + UInt32 propertySize; + OSStatus err; + AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat; + +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Specified file data format"); +#endif + + // Find out the actual format the converter will produce. This is necessary in + // case the bitrate has forced a lower sample rate, which needs to be set correctly + // in the stream description passed to AudioFileCreate. + if (mConverter != NULL) { + propertySize = sizeof(AudioStreamBasicDescription); + Float64 origSampleRate = mFileDataFormat.mSampleRate; + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description"); + // do the same for the channel layout being output by the converter +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Converter output"); +#endif + if (fiszero(mFileDataFormat.mSampleRate)) + mFileDataFormat.mSampleRate = origSampleRate; + err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + AudioChannelLayout *layout = static_cast(malloc(propertySize)); + err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout); + if (err) { + free(layout); + XThrow(err, "couldn't get audio converter's output channel layout"); + } + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + free(layout); + } + } + + // create the output file + if (mMode == kPreparingToCreate) { + CAStreamBasicDescription newFileDataFormat = mFileDataFormat; + if (fiszero(newFileDataFormat.mSampleRate)) + newFileDataFormat.mSampleRate = 44100; // just make something up for now +#if VERBOSE_CONVERTER + newFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); +#endif + XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file"); + mMode = kPreparingToWrite; + mOwnOpenFile = true; + } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) { + // second check must be explicit since operator== on ASBD treats SR of zero as "don't care" + if (fiszero(mFileDataFormat.mSampleRate)) + mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate; +#if VERBOSE_CONVERTER + mFileDataFormat.PrintFormat(stdout, "", "Applied to new file"); +#endif + XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0"); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format"); + } + + UInt32 deferSizeUpdates = 1; + err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates); + + if (mConverter != NULL) { + // encoder + // get the magic cookie, if any, from the converter + delete[] mMagicCookie; mMagicCookie = NULL; + mMagicCookieSize = 0; + + err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL); + + // we can get a noErr result and also a propertySize == 0 + // -- if the file format does support magic cookies, but this file doesn't have one. + if (err == noErr && propertySize > 0) { + mMagicCookie = new Byte[propertySize]; + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie"); + mMagicCookieSize = propertySize; // the converter lies and tell us the wrong size + // now set the magic cookie on the output file + UInt32 willEatTheCookie = false; + // the converter wants to give us one; will the file take it? + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, + NULL, &willEatTheCookie); + if (err == noErr && willEatTheCookie) { +#if VERBOSE_CONVERTER + printf("Setting cookie on encoded file\n"); +#endif + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie"); + } + } + + // get maximum packet size + propertySize = sizeof(UInt32); + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size"); + + AllocateBuffers(true /* okToFail */); + } else { + InitFileMaxPacketSize(); + } + + if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) { + // don't bother tagging mono/stereo files + UInt32 isWritable; + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable); + if (!err && isWritable) { +#if VERBOSE_CHANNELMAP + printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, + mFileChannelLayout.Size(), &mFileChannelLayout.Layout()); + if (err) + CAXException::Warning("could not set the file's channel layout", err); + } else { +#if VERBOSE_CHANNELMAP + printf("file won't accept a channel layout (write)\n"); +#endif + } + } + + UpdateClientMaxPacketSize(); // also sets mFrame0Offset + mPacketMark = 0; + mFrameMark = 0; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::InitFileMaxPacketSize() +{ + LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this); + UInt32 propertySize = sizeof(UInt32); + OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize, + &propertySize, &mFileMaxPacketSize); + if (err) { + // workaround for 3361377: not all file formats' maximum packet sizes are supported + if (!mFileDataFormat.IsPCM()) + XThrowIfError(err, "get audio file's maximum packet size"); + mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame; + } + AllocateBuffers(true /* okToFail */); +} + + +// _______________________________________________________________________________________ +// +SInt64 CAAudioFile::FileDataOffset() +{ + if (mFileDataOffset < 0) { + UInt32 propertySize = sizeof(SInt64); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset"); + } + return mFileDataOffset; +} + +// _______________________________________________________________________________________ +// +SInt64 CAAudioFile::GetNumberFrames() const +{ + AudioFilePacketTableInfo pti; + UInt32 propertySize = sizeof(pti); + OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr) + return pti.mNumberValidFrames; + return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetNumberFrames(SInt64 nFrames) +{ + XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM"); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file"); +} + +// _______________________________________________________________________________________ +// +// call for existing file, NOT new one - from Open() or Wrap() +void CAAudioFile::GetExistingFileInfo() +{ + LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this); + UInt32 propertySize; + OSStatus err; + + // get mFileDataFormat + propertySize = sizeof(AudioStreamBasicDescription); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format"); + + // get mFileChannelLayout + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + AudioChannelLayout *layout = static_cast(malloc(propertySize)); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout); + if (err == noErr) { + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + } + free(layout); + XThrowIfError(err, "get audio file's channel layout"); + } + if (mMode != kReading) + return; + +#if 0 + // get mNumberPackets + propertySize = sizeof(mNumberPackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count"); +#if VERBOSE_IO + printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets); +#endif +#endif + + // get mMagicCookie + err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL); + if (err == noErr && propertySize > 0) { + mMagicCookie = new Byte[propertySize]; + mMagicCookieSize = propertySize; + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie"); + } + InitFileMaxPacketSize(); + mPacketMark = 0; + mFrameMark = 0; + + UpdateClientMaxPacketSize(); +} + +// ======================================================================================= + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout) +{ + LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this); + mFileChannelLayout = layout; +#if VERBOSE_CHANNELMAP + printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + if (mMode != kReading) + FileFormatChanged(); +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout) +{ + LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this); + XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file"); + + bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat); + + if (dataFormatChanging) { + CloseConverter(); + if (mWriteBufferList) { + delete mWriteBufferList; + mWriteBufferList = NULL; + } + mClientDataFormat = dataFormat; + } + + if (layout && layout->IsValid()) { + XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map"); + mClientChannelLayout = *layout; + } + + bool differentLayouts; + if (mClientChannelLayout.IsValid()) { + if (mFileChannelLayout.IsValid()) { + differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag(); +#if VERBOSE_CHANNELMAP + printf("two valid layouts, %s\n", differentLayouts ? "different" : "same"); +#endif + } else { + differentLayouts = false; +#if VERBOSE_CHANNELMAP + printf("valid client layout, unknown file layout\n"); +#endif + } + } else { + differentLayouts = false; +#if VERBOSE_CHANNELMAP + if (mFileChannelLayout.IsValid()) + printf("valid file layout, unknown client layout\n"); + else + printf("two invalid layouts\n"); +#endif + } + + if (mClientDataFormat != mFileDataFormat || differentLayouts) { + // We need an AudioConverter. + if (mMode == kReading) { + // file -> client (decode) +//mFileDataFormat.PrintFormat( stdout, "", "File: "); +//mClientDataFormat.PrintFormat(stdout, "", "Client: "); + + if (mConverter == NULL) + XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter), + "create audio converter"); + +#if VERBOSE_CONVERTER + printf("CAAudioFile %p -- created converter\n", this); + CAShow(mConverter); +#endif + // set the magic cookie, if any (for decode) + if (mMagicCookie) + SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM()); + // we get cookies from some AIFF's but the converter barfs on them, + // so we set canFail to true for PCM + + SetConverterChannelLayout(false, mFileChannelLayout); + SetConverterChannelLayout(true, mClientChannelLayout); + + // propagate leading/trailing frame counts + if (mFileDataFormat.mBitsPerChannel == 0) { + UInt32 propertySize; + OSStatus err; + AudioFilePacketTableInfo pti; + propertySize = sizeof(pti); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) { + AudioConverterPrimeInfo primeInfo; + primeInfo.leadingFrames = pti.mPrimingFrames; + primeInfo.trailingFrames = pti.mRemainderFrames; + /* ignore any error. better to play it at all than not. */ + /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo); + //XThrowIfError(err, "couldn't set prime info on converter"); + } + } + } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) { + // client -> file (encode) + if (mConverter == NULL) + XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter"); + mWriteBufferList = CABufferList::New("", mClientDataFormat); + SetConverterChannelLayout(false, mClientChannelLayout); + SetConverterChannelLayout(true, mFileChannelLayout); + if (mMode == kPreparingToWrite) + FileFormatChanged(); + } else + XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known"); + } + UpdateClientMaxPacketSize(); +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::SetConverterProperty( + AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void* inPropertyData, + bool inCanFail) +{ + OSStatus err = noErr; + //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID); + if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL) + ; + else { + err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) { + XThrowIfError(err, "set audio converter property"); + } + } + UpdateClientMaxPacketSize(); + if (mMode == kPreparingToWrite) + FileFormatChanged(); + return err; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout) +{ + LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this); + OSStatus err; + + if (layout.IsValid()) { +#if VERBOSE_CHANNELMAP + printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input", + CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag())); +#endif + if (output) { + err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout, + layout.Size(), &layout.Layout()); + XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout"); + } else { + err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout, + layout.Size(), &layout.Layout()); + XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout"); + } + if (mMode == kPreparingToWrite) + FileFormatChanged(); + } +} + +// _______________________________________________________________________________________ +// +CFArrayRef CAAudioFile::GetConverterConfig() +{ + CFArrayRef plist; + UInt32 propertySize = sizeof(plist); + XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings"); + return plist; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::UpdateClientMaxPacketSize() +{ + LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this); + mFrame0Offset = 0; + if (mConverter != NULL) { + AudioConverterPropertyID property = (mMode == kReading) ? + kAudioConverterPropertyMaximumOutputPacketSize : + kAudioConverterPropertyMaximumInputPacketSize; + + UInt32 propertySize = sizeof(UInt32); + XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize), + "get audio converter's maximum packet size"); + + if (mFileDataFormat.mBitsPerChannel == 0) { + AudioConverterPrimeInfo primeInfo; + propertySize = sizeof(primeInfo); + OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); + if (err == noErr) + mFrame0Offset = primeInfo.leadingFrames; +#if VERBOSE_CONVERTER + printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset); +#endif + } + } else { + mClientMaxPacketSize = mFileMaxPacketSize; + } +} + +// _______________________________________________________________________________________ +// Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs +// Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes +void CAAudioFile::AllocateBuffers(bool okToFail) +{ + LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this); + if (mFileMaxPacketSize == 0) { + if (okToFail) + return; + XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0"); + } + UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize); + // must be big enough for at least one maximum size packet + + if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) { + mIOBufferList.mNumberBuffers = 1; + mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame; + if (!mClientOwnsIOBuffer) { + //printf("reallocating I/O buffer\n"); + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes]; + } + mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes; + mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize; + } + + UInt32 propertySize = sizeof(UInt32); + UInt32 externallyFramed; + XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, + sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed), + "is format externally framed"); + if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) { + delete[] mPacketDescs; + mPacketDescs = NULL; + mNumPacketDescs = 0; + + if (externallyFramed) { + //printf("reallocating packet descs\n"); + mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets]; + mNumPacketDescs = mIOBufferSizePackets; + } + } +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::SetIOBuffer(void *buf) +{ + if (!mClientOwnsIOBuffer) + delete[] (Byte *)mIOBufferList.mBuffers[0].mData; + mIOBufferList.mBuffers[0].mData = buf; + + if (buf == NULL) { + mClientOwnsIOBuffer = false; + SetIOBufferSizeBytes(mIOBufferSizeBytes); + } else { + mClientOwnsIOBuffer = true; + AllocateBuffers(); + } +// printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer); +} + +// =============================================================================== + +/* +For Tiger: +added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket. +You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty. + + kAudioFilePropertyPacketToFrame = 'pkfr', + // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored. + kAudioFilePropertyFrameToPacket = 'frpk', + // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back. + +struct AudioFramePacketTranslation +{ + SInt64 mFrame; + SInt64 mPacket; + UInt32 mFrameOffsetInPacket; +}; +*/ + +SInt64 CAAudioFile::PacketToFrame(SInt64 packet) const +{ + AudioFramePacketTranslation trans; + UInt32 propertySize; + + switch (mFileDataFormat.mFramesPerPacket) { + case 1: + return packet; + case 0: + trans.mPacket = packet; + propertySize = sizeof(trans); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans), + "packet <-> frame translation unimplemented for format with variable frames/packet"); + return trans.mFrame; + } + return packet * mFileDataFormat.mFramesPerPacket; +} + +SInt64 CAAudioFile::FrameToPacket(SInt64 inFrame) const +{ + AudioFramePacketTranslation trans; + UInt32 propertySize; + + switch (mFileDataFormat.mFramesPerPacket) { + case 1: + return inFrame; + case 0: + trans.mFrame = inFrame; + propertySize = sizeof(trans); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans), + "packet <-> frame translation unimplemented for format with variable frames/packet"); + return trans.mPacket; + } + return inFrame / mFileDataFormat.mFramesPerPacket; +} + +// _______________________________________________________________________________________ +// + +SInt64 CAAudioFile::Tell() const // frameNumber +{ + return mFrameMark - mFrame0Offset; +} + +void CAAudioFile::SeekToPacket(SInt64 packetNumber) +{ +#if VERBOSE_IO + printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber); +#endif + XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file"); + if (mPacketMark == packetNumber) + return; // already there! don't reset converter + mPacketMark = packetNumber; + + mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset; + mFramesToSkipFollowingSeek = 0; + if (mConverter) + // must reset -- if we reached end of stream. converter will no longer work otherwise + AudioConverterReset(mConverter); +} + +/* + Example: AAC, 1024 frames/packet, 2112 frame offset + + 2112 + | + Absolute frames: 0 1024 2048 | 3072 + +---------+---------+--|------+---------+---------+ + Packets: | 0 | 1 | | 2 | 3 | 4 | + +---------+---------+--|------+---------+---------+ + Client frames: -2112 -1088 -64 | 960 SeekToFrame, TellFrame + | + 0 + + * Offset between absolute and client frames is mFrame0Offset. + *** mFrameMark is in client frames *** + + Examples: + clientFrame 0 960 1000 1024 + absoluteFrame 2112 3072 3112 3136 + packet 0 0 0 1 + tempFrameMark* -2112 -2112 -2112 -1088 + mFramesToSkipFollowingSeek 2112 3072 3112 2112 +*/ +void CAAudioFile::Seek(SInt64 clientFrame) +{ + if (clientFrame == mFrameMark) + return; // already there! don't reset converter + + //SInt64 absoluteFrame = clientFrame + mFrame0Offset; + XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file"); + +#if VERBOSE_IO + SInt64 prevFrameMark = mFrameMark; +#endif + + SInt64 packet; + packet = FrameToPacket(clientFrame); + if (packet < 0) + packet = 0; + SeekToPacket(packet); + // this will have backed up mFrameMark to match the beginning of the packet + mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0)); + mFrameMark = clientFrame; + +#if VERBOSE_IO + printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek); +#endif +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData) + // May read fewer packets than requested if: + // buffer is not big enough + // file does not contain that many more packets + // Note that eofErr is not fatal, just results in 0 packets returned + // ioData's buffer sizes may be shortened +{ + XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0"); + if (mIOBufferList.mBuffers[0].mData == NULL) { +#if DEBUG + printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n"); +#endif + AllocateBuffers(); + } + UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize; + UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize; + // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check + UInt32 nPackets = std::min(ioNumPackets, maxNumPackets); + + mMaxPacketsToRead = ~0UL; + + if (mClientDataFormat.mFramesPerPacket == 1) { // PCM or equivalent + while (mFramesToSkipFollowingSeek > 0) { + UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets); + UInt32 framesPerPacket; + if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0) + mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket; + + if (mConverter == NULL) { + XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file"); + } else { +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)"); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + } + if (skipFrames == 0) { // hit EOF + ioNumPackets = 0; + return; + } + mFrameMark += skipFrames; +#if VERBOSE_IO + printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames); +#endif + + mFramesToSkipFollowingSeek -= skipFrames; + + // restore mDataByteSize + for (int i = ioData->mNumberBuffers; --i >= 0 ; ) + ioData->mBuffers[i].mDataByteSize = bufferSizeBytes; + } + } + + if (mFileDataFormat.mFramesPerPacket > 0) + // don't read more packets than we are being asked to produce + mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1; + if (mConverter == NULL) { + XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file"); + } else { +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)"); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + } + if (mClientDataFormat.mFramesPerPacket == 1) + mFrameMark += nPackets; + + ioNumPackets = nPackets; +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) +{ + CAAudioFile *This = static_cast(inUserData); + +#if 0 + SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark; + if (remainingPacketsInFile <= 0) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + if (outDataPacketDescription) + *outDataPacketDescription = This->mPacketDescs; +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: EOF\n"); +#endif + return noErr; // not eofErr; EOF is signified by 0 packets/0 bytes + } +#endif + + // determine how much to read + AudioBufferList *readBuffer; + UInt32 readPackets; + if (inAudioConverter != NULL) { + // getting called from converter, need to use our I/O buffer + readBuffer = &This->mIOBufferList; + readPackets = This->mIOBufferSizePackets; + } else { + // getting called directly from ReadPackets, use supplied buffer + if (This->mFileMaxPacketSize == 0) + return kExtAudioFileError_MaxPacketSizeUnknown; + readBuffer = ioData; + readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize); + // don't attempt to read more packets than will fit in the buffer + } + // don't try to read past EOF +// if (readPackets > remainingPacketsInFile) +// readPackets = remainingPacketsInFile; + // don't read more packets than necessary to produce the requested amount of converted data + if (readPackets > This->mMaxPacketsToRead) { +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets); +#endif + readPackets = This->mMaxPacketsToRead; + } + + // read + UInt32 bytesRead; + OSStatus err; + + StartTiming(This, read); + StartTiming(This, readinconv); + err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData); +#if CAAUDIOFILE_PROFILE + if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter); +#endif + ElapsedTime(This, read, This->mTicksInIO); + + if (err) { + DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err); + return err; + } + +#if VERBOSE_IO + printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err); +#if VERBOSE_IO >= 2 + if (This->mPacketDescs) { + for (UInt32 i = 0; i < readPackets; ++i) { + printf(" read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize); + } + } + printf(" read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4); +#endif +#endif + if (readPackets == 0) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + return noErr; + } + + if (outDataPacketDescription) + *outDataPacketDescription = This->mPacketDescs; + ioData->mBuffers[0].mDataByteSize = bytesRead; + ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData; + + This->mPacketMark += readPackets; + if (This->mClientDataFormat.mFramesPerPacket != 1) { // for PCM client formats we update in Read + // but for non-PCM client format (weird case) we must update here/now + if (This->mFileDataFormat.mFramesPerPacket > 0) + This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket; + else { + for (UInt32 i = 0; i < readPackets; ++i) + This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket; + } + } + *ioNumberDataPackets = readPackets; + return noErr; +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data) +{ + if (mIOBufferList.mBuffers[0].mData == NULL) { +#if DEBUG + printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n"); +#endif + AllocateBuffers(); + } + + if (mMode == kPreparingToWrite) + mMode = kWriting; + else + XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file"); + if (mConverter != NULL) { + mWritePackets = numPackets; + mWriteBufferList->SetFrom(data); + WritePacketsFromCallback(WriteInputProc, this); + } else { + StartTiming(this, write); + XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize, + NULL, mPacketMark, &numPackets, data->mBuffers[0].mData), + "write audio file"); + ElapsedTime(this, write, mTicksInIO); +#if VERBOSE_IO + printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize); +#endif + //mNumberPackets = + mPacketMark += numPackets; + if (mFileDataFormat.mFramesPerPacket > 0) + mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket; + // else: shouldn't happen since we're only called when there's no converter + } +} + +// _______________________________________________________________________________________ +// +void CAAudioFile::FlushEncoder() +{ + if (mConverter != NULL) { + mFinishingEncoding = true; + WritePacketsFromCallback(WriteInputProc, this); + mFinishingEncoding = false; + + // get priming info from converter, set it on the file + if (mFileDataFormat.mBitsPerChannel == 0) { + UInt32 propertySize; + OSStatus err; + AudioConverterPrimeInfo primeInfo; + propertySize = sizeof(primeInfo); + + err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo); + if (err == noErr) { + AudioFilePacketTableInfo pti; + propertySize = sizeof(pti); + err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti); + if (err == noErr) { +//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); + UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames; + pti.mPrimingFrames = primeInfo.leadingFrames; + pti.mRemainderFrames = primeInfo.trailingFrames; + pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames; +//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames); + XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file"); + } + } + } + } +} + +// _______________________________________________________________________________________ +// +OSStatus CAAudioFile::WriteInputProc( AudioConverterRef /*inAudioConverter*/, + UInt32 * ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription ** outDataPacketDescription, + void* inUserData) +{ + CAAudioFile *This = static_cast(inUserData); + if (This->mFinishingEncoding) { + *ioNumberDataPackets = 0; + ioData->mBuffers[0].mDataByteSize = 0; + ioData->mBuffers[0].mData = NULL; + if (outDataPacketDescription) + *outDataPacketDescription = NULL; + return noErr; + } + UInt32 numPackets = This->mWritePackets; + if (numPackets == 0) { + return kNoMoreInputRightNow; + } + This->mWriteBufferList->ToAudioBufferList(ioData); + This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame); + *ioNumberDataPackets = numPackets; + if (outDataPacketDescription) + *outDataPacketDescription = NULL; + This->mWritePackets -= numPackets; + return noErr; +} + +// _______________________________________________________________________________________ +// +#if VERBOSE_IO +static void hexdump(const void *addr, long len) +{ + const Byte *p = (Byte *)addr; + UInt32 offset = 0; + + if (len > 0x400) len = 0x400; + + while (len > 0) { + int n = len > 16 ? 16 : len; + printf("%08lX: ", offset); + for (int i = 0; i < 16; ++i) + if (i < n) + printf("%02X ", p[i]); + else printf(" "); + for (int i = 0; i < 16; ++i) + if (i < n) + putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.'); + else putchar(' '); + putchar('\n'); + p += 16; + len -= 16; + offset += 16; + } +} +#endif + +// _______________________________________________________________________________________ +// +void CAAudioFile::WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData) +{ + while (true) { + // keep writing until we exhaust the input (temporary stop), or produce no output (EOF) + UInt32 numEncodedPackets = mIOBufferSizePackets; + mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes; +#if CAAUDIOFILE_PROFILE + mInConverter = true; +#endif + StartTiming(this, fill); + OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData, + &numEncodedPackets, &mIOBufferList, mPacketDescs); + ElapsedTime(this, fill, mTicksInConverter); +#if CAAUDIOFILE_PROFILE + mInConverter = false; +#endif + XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)"); + if (numEncodedPackets == 0) + break; + Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData; +#if VERBOSE_IO + printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize); + if (mPacketDescs) { + for (UInt32 i = 0; i < numEncodedPackets; ++i) { + printf(" write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); +#if VERBOSE_IO >= 2 + hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize); +#endif + } + } +#endif + StartTiming(this, write); + XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file"); + ElapsedTime(this, write, mTicksInIO); + mPacketMark += numEncodedPackets; + //mNumberPackets += numEncodedPackets; + if (mFileDataFormat.mFramesPerPacket > 0) + mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket; + else { + for (UInt32 i = 0; i < numEncodedPackets; ++i) + mFrameMark += mPacketDescs[i].mVariableFramesInPacket; + } + if (err == kNoMoreInputRightNow) + break; + } +} + +#endif // !CAAF_USE_EXTAUDIOFILE diff --git a/libs/appleutility/CAAudioFile.h b/libs/appleutility/CAAudioFile.h new file mode 100644 index 0000000000..2cfb4f3031 --- /dev/null +++ b/libs/appleutility/CAAudioFile.h @@ -0,0 +1,439 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAudioFile.h + +=============================================================================*/ + +#ifndef __CAAudioFile_h__ +#define __CAAudioFile_h__ + +#include + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +#include "CAStreamBasicDescription.h" +#include "CABufferList.h" +#include "CAAudioChannelLayout.h" +#include "CAXException.h" +#include "CAMath.h" + +#ifndef CAAF_USE_EXTAUDIOFILE +// option: use AudioToolbox/ExtAudioFile.h? Only available on Tiger. + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3 + // we are building software that must be deployable on Panther or earlier + #define CAAF_USE_EXTAUDIOFILE 0 + #else + // else we require Tiger and can use the API + #define CAAF_USE_EXTAUDIOFILE 1 + #endif +#endif + +#ifndef MAC_OS_X_VERSION_10_4 + // we have pre-Tiger headers; add our own declarations + typedef UInt32 AudioFileTypeID; + enum { + kExtAudioFileError_InvalidProperty = -66561, + kExtAudioFileError_InvalidPropertySize = -66562, + kExtAudioFileError_NonPCMClientFormat = -66563, + kExtAudioFileError_InvalidChannelMap = -66564, // number of channels doesn't match format + kExtAudioFileError_InvalidOperationOrder = -66565, + kExtAudioFileError_InvalidDataFormat = -66566, + kExtAudioFileError_MaxPacketSizeUnknown = -66567, + kExtAudioFileError_InvalidSeek = -66568, // writing, or offset out of bounds + kExtAudioFileError_AsyncWriteTooLarge = -66569, + kExtAudioFileError_AsyncWriteBufferOverflow = -66570 // an async write could not be completed in time + }; +#else + #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include + #else + #include "ExtendedAudioFile.h" + #endif +#endif + +// _______________________________________________________________________________________ +// Wrapper class for an AudioFile, supporting encode/decode to/from a PCM client format +class CAAudioFile { +public: + // implementation-independent helpers + void Open(const char *filePath) { + FSRef fsref; + XThrowIfError(FSPathMakeRef((UInt8 *)filePath, &fsref, NULL), "locate audio file"); + Open(fsref); + } + + bool HasConverter() const { return GetConverter() != NULL; } + + double GetDurationSeconds() { + double sr = GetFileDataFormat().mSampleRate; + return fnonzero(sr) ? GetNumberFrames() / sr : 0.; + } + // will be 0 if the file's frames/packet is 0 (variable) + // or the file's sample rate is 0 (unknown) + +#if CAAF_USE_EXTAUDIOFILE +public: + CAAudioFile() : mExtAF(NULL) { } + virtual ~CAAudioFile() { if (mExtAF) Close(); } + + void Open(const FSRef &fsref) { + // open an existing file + XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed"); + } + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) { + XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed"); + } + + void Wrap(AudioFileID fileID, bool forWriting) { + // use this to wrap an AudioFileID opened externally + XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed"); + } + + void Close() { + XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed"); + mExtAF = NULL; + } + + const CAStreamBasicDescription &GetFileDataFormat() { + UInt32 size = sizeof(mFileDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format"); + return mFileDataFormat; + } + + const CAAudioChannelLayout & GetFileChannelLayout() { + return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout); + } + + void SetFileChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout"); + mFileChannelLayout = layout; + } + + const CAStreamBasicDescription &GetClientDataFormat() { + UInt32 size = sizeof(mClientDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format"); + return mClientDataFormat; + } + + const CAAudioChannelLayout & GetClientChannelLayout() { + return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout); + } + + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format"); + if (layout) + SetClientChannelLayout(*layout); + } + + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout"); + } + + AudioConverterRef GetConverter() const { + UInt32 size = sizeof(AudioConverterRef); + AudioConverterRef converter; + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter"); + return converter; + } + + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false) + { + OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) + XThrowIfError(err, "Couldn't set audio converter property"); + if (!err) { + // must tell the file that we have changed the converter; a NULL converter config is sufficient + CFPropertyListRef config = NULL; + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed"); + } + return err; + } + + SInt64 GetNumberFrames() { + SInt64 length; + UInt32 size = sizeof(SInt64); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length"); + return length; + } + + void SetNumberFrames(SInt64 length) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length"); + } + + void Seek(SInt64 pos) { + XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file"); + } + + SInt64 Tell() { + SInt64 pos; + XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark"); + return pos; + } + + void Read(UInt32 &ioFrames, AudioBufferList *ioData) { + XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file"); + } + + void Write(UInt32 inFrames, const AudioBufferList *inData) { + XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file"); + } + + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size"); + } + +private: + const CAAudioChannelLayout & FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) { + UInt32 size; + XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout"); + AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size); + OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout); + if (err) { + free(layout); + XThrowIfError(err, "Couldn't get channel layout"); + } + layoutObj = layout; + free(layout); + return layoutObj; + } + + +private: + ExtAudioFileRef mExtAF; + + CAStreamBasicDescription mFileDataFormat; + CAAudioChannelLayout mFileChannelLayout; + + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mClientChannelLayout; +#endif + +#if !CAAF_USE_EXTAUDIOFILE + CAAudioFile(); + virtual ~CAAudioFile(); + + // --- second-stage initializers --- + // Use exactly one of the following: + // - Open + // - PrepareNew followed by Create + // - Wrap + + void Open(const FSRef &fsref); + // open an existing file + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL); + + void Wrap(AudioFileID fileID, bool forWriting); + // use this to wrap an AudioFileID opened externally + + // --- + + void Close(); + // In case you want to close the file before the destructor executes + + // --- Data formats --- + + // Allow specifying the file's channel layout. Must be called before SetClientFormat. + // When writing, the specified channel layout is written to the file (if the file format supports + // the channel layout). When reading, the specified layout overrides the one read from the file, + // if any. + void SetFileChannelLayout(const CAAudioChannelLayout &layout); + + // This specifies the data format which the client will use for reading/writing the file, + // which may be different from the file's format. An AudioConverter is created if necessary. + // The client format must be linear PCM. + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL); + void SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); } + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); } + + // Wrapping the underlying converter, if there is one + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void * inPropertyData, + bool inCanFail = false); + void SetConverterConfig(CFArrayRef config) { + SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); } + CFArrayRef GetConverterConfig(); + + // --- I/O --- + // All I/O is sequential, but you can seek to an arbitrary position when reading. + // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's. + // However, ReadPackets/WritePackets use packet counts in the client data format. + + void Read(UInt32 &ioNumFrames, AudioBufferList *ioData); + void Write(UInt32 numFrames, const AudioBufferList *data); + + // These can fail for files without a constant mFramesPerPacket + void Seek(SInt64 frameNumber); + SInt64 Tell() const; // frameNumber + + // --- Accessors --- + // note: client parameters only valid if SetClientFormat has been called + AudioFileID GetAudioFileID() const { return mAudioFile; } + const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; } + const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; } + const CAAudioChannelLayout & GetFileChannelLayout() const { return mFileChannelLayout; } + const CAAudioChannelLayout & GetClientChannelLayout() const { return mClientChannelLayout; } + AudioConverterRef GetConverter() const { return mConverter; } + + UInt32 GetFileMaxPacketSize() const { return mFileMaxPacketSize; } + UInt32 GetClientMaxPacketSize() const { return mClientMaxPacketSize; } + SInt64 GetNumberPackets() const { + SInt64 npackets; + UInt32 propertySize = sizeof(npackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count"); + return npackets; + } + SInt64 GetNumberFrames() const; + // will be 0 if the file's frames/packet is 0 (variable) + void SetNumberFrames(SInt64 length); // should only be set on a PCM file + + // --- Tunable performance parameters --- + void SetUseCache(bool b) { mUseCache = b; } + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; } + UInt32 GetIOBufferSizeBytes() { return mIOBufferSizeBytes; } + void * GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; } + void SetIOBuffer(void *buf); + + // -- Profiling --- +#if CAAUDIOFILE_PROFILE + void EnableProfiling(bool b) { mProfiling = b; } + UInt64 TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; } + UInt64 TicksInIO() const { return mTicksInIO; } +#endif + +// _______________________________________________________________________________________ +private: + SInt64 FileDataOffset(); + void SeekToPacket(SInt64 packetNumber); + SInt64 TellPacket() const { return mPacketMark; } // will be imprecise if SeekToFrame was called + + void SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout); + void WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData); + // will use I/O buffer size + void InitFileMaxPacketSize(); + void FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0); + + void GetExistingFileInfo(); + void FlushEncoder(); + void CloseConverter(); + void UpdateClientMaxPacketSize(); + void AllocateBuffers(bool okToFail=false); + SInt64 PacketToFrame(SInt64 packet) const; + SInt64 FrameToPacket(SInt64 inFrame) const; + + static OSStatus ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); + + static OSStatus WriteInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); +// _______________________________________________________________________________________ +private: + + // the file + FSRef mFSRef; + AudioFileID mAudioFile; + bool mOwnOpenFile; + bool mUseCache; + bool mFinishingEncoding; + enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode; + +// SInt64 mNumberPackets; // in file's format + SInt64 mFileDataOffset; + SInt64 mPacketMark; // in file's format + SInt64 mFrameMark; // this may be offset from the start of the file + // by the codec's latency; i.e. our frame 0 could + // lie at frame 2112 of a decoded AAC file + SInt32 mFrame0Offset; + UInt32 mFramesToSkipFollowingSeek; + + // buffers + UInt32 mIOBufferSizeBytes; + UInt32 mIOBufferSizePackets; + AudioBufferList mIOBufferList; // only one buffer -- USE ACCESSOR so it can be lazily initialized + bool mClientOwnsIOBuffer; + AudioStreamPacketDescription *mPacketDescs; + UInt32 mNumPacketDescs; + + // formats/conversion + AudioConverterRef mConverter; + CAStreamBasicDescription mFileDataFormat; + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mFileChannelLayout; + CAAudioChannelLayout mClientChannelLayout; + UInt32 mFileMaxPacketSize; + UInt32 mClientMaxPacketSize; + + // cookie + Byte * mMagicCookie; + UInt32 mMagicCookieSize; + + // for ReadPackets + UInt32 mMaxPacketsToRead; + + // for WritePackets + UInt32 mWritePackets; + CABufferList * mWriteBufferList; + +#if CAAUDIOFILE_PROFILE + // performance + bool mProfiling; + UInt64 mTicksInConverter; + UInt64 mTicksInReadInConverter; + UInt64 mTicksInIO; + bool mInConverter; +#endif + +#endif // CAAF_USE_EXTAUDIOFILE +}; + +#endif // __CAAudioFile_h__ diff --git a/libs/appleutility/CABufferList.cpp b/libs/appleutility/CABufferList.cpp new file mode 100644 index 0000000000..81298f918a --- /dev/null +++ b/libs/appleutility/CABufferList.cpp @@ -0,0 +1,179 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CABufferList.cpp + +=============================================================================*/ + +#include "CABufferList.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +void CABufferList::AllocateBuffers(UInt32 nBytes) +{ + if (nBytes <= GetNumBytes()) return; + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // get page faults now, not later + + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + if (buf->mData != NULL && buf->mDataByteSize > 0) + // preserve existing buffer contents + memcpy(p, buf->mData, buf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + delete[] oldMemory; +} + +void CABufferList::AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inSrcList, CABufferList *inSetPtrList) +{ + if (mNumberBuffers != inSrcList->mNumberBuffers) return; + if (mNumberBuffers != inSetPtrList->mNumberBuffers) return; + if (nBytes <= GetNumBytes()) { + CopyAllFrom(inSrcList, inSetPtrList); + return; + } + inSetPtrList->VerifyNotTrashingOwnedBuffer(); + UInt32 fromByteSize = inSrcList->GetNumBytes(); + + if (mNumberBuffers > 1) + // align successive buffers for Altivec and to take alternating + // cache line hits by spacing them by odd multiples of 16 + nBytes = (nBytes + (0x10 - (nBytes & 0xF))) | 0x10; + UInt32 memorySize = nBytes * mNumberBuffers; + Byte *newMemory = new Byte[memorySize], *p = newMemory; + memset(newMemory, 0, memorySize); // make buffer "hot" + + AudioBuffer *buf = mBuffers; + AudioBuffer *ptrBuf = inSetPtrList->mBuffers; + AudioBuffer *srcBuf = inSrcList->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf, ++ptrBuf, ++srcBuf) { + if (srcBuf->mData != NULL && srcBuf->mDataByteSize > 0) + // preserve existing buffer contents + memmove(p, srcBuf->mData, srcBuf->mDataByteSize); + buf->mDataByteSize = nBytes; + buf->mData = p; + ptrBuf->mDataByteSize = srcBuf->mDataByteSize; + ptrBuf->mData = p; + p += nBytes; + } + Byte *oldMemory = mBufferMemory; + mBufferMemory = newMemory; + if (inSrcList != inSetPtrList) + inSrcList->BytesConsumed(fromByteSize); + delete[] oldMemory; +} + +void CABufferList::DeallocateBuffers() +{ + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = NULL; + buf->mDataByteSize = 0; + } + if (mBufferMemory != NULL) { + delete[] mBufferMemory; + mBufferMemory = NULL; + } + +} + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize) +{ + printf("AudioBufferList @ %p:\n", abl); + const AudioBuffer *buf = abl->mBuffers; + for (UInt32 i = 0; i < abl->mNumberBuffers; ++i, ++buf) { + printf(" [%2ld]: %2ldch, %5ld bytes @ %8p", + i, buf->mNumberChannels, buf->mDataByteSize, buf->mData); + if (framesToPrint) { + printf(":"); + Byte *p = (Byte *)buf->mData; + for (int j = framesToPrint * buf->mNumberChannels; --j >= 0; ) + switch (wordSize) { + case 0: + printf(" %6.3f", *(Float32 *)p); + p += sizeof(Float32); + break; + case 1: + case -1: + printf(" %02X", *p); + p += 1; + break; + case 2: + printf(" %04X", EndianU16_BtoN(*(UInt16 *)p)); + p += 2; + break; + case 3: + printf(" %06X", (p[0] << 16) | (p[1] << 8) | p[2]); + p += 3; + break; + case 4: + printf(" %08lX", EndianU32_BtoN(*(UInt32 *)p)); + p += 4; + break; + case -2: + printf(" %04X", EndianU16_LtoN(*(UInt16 *)p)); + p += 2; + break; + case -3: + printf(" %06X", (p[2] << 16) | (p[1] << 8) | p[0]); + p += 3; + break; + case -4: + printf(" %08lX", EndianU32_LtoN(*(UInt32 *)p)); + p += 4; + break; + } + } + printf("\n"); + } +} + diff --git a/libs/appleutility/CABufferList.h b/libs/appleutility/CABufferList.h new file mode 100644 index 0000000000..3b0cef9a52 --- /dev/null +++ b/libs/appleutility/CABufferList.h @@ -0,0 +1,300 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CABufferList.h + +=============================================================================*/ + +#ifndef __CABufferList_h__ +#define __CABufferList_h__ + +#include +#include "CAStreamBasicDescription.h" +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include +#endif + +extern "C" void CAShowAudioBufferList(const AudioBufferList *abl, int framesToPrint, int wordSize); + // wordSize: 0 = float32, else integer word size, negative if little-endian + +/* ____________________________________________________________________________ +// CABufferList - variable length buffer list + + This class is designed for use in non-simplistic cases. For AudioUnits, AUBufferList + is preferred. + + CABufferList can be used in one of two ways: + - as mutable pointers into non-owned memory + - as an immutable array of buffers (owns its own memory). + + All buffers are assumed to have the same format (number of channels, word size), so that + we can assume their mDataByteSizes are all the same. +____________________________________________________________________________ */ +class CABufferList { +public: + void * operator new(size_t /*size*/, int nBuffers) { + return ::operator new(sizeof(CABufferList) + (nBuffers-1) * sizeof(AudioBuffer)); + } + static CABufferList * New(const char *name, const CAStreamBasicDescription &format) + { + UInt32 numBuffers = format.NumberChannelStreams(), channelsPerBuffer = format.NumberInterleavedChannels(); + return new(numBuffers) CABufferList(name, numBuffers, channelsPerBuffer); + } + +protected: + CABufferList(const char *name, UInt32 numBuffers, UInt32 channelsPerBuffer) : + mName(name), + mBufferMemory(NULL) + { + check(numBuffers > 0 /*&& channelsPerBuffer > 0*/); + mNumberBuffers = numBuffers; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mNumberChannels = channelsPerBuffer; + buf->mDataByteSize = 0; + buf->mData = NULL; + } + } + +public: + ~CABufferList() + { + if (mBufferMemory) + delete[] mBufferMemory; + } + + const char * Name() { return mName; } + + const AudioBufferList & GetBufferList() const { return *(AudioBufferList *)&mNumberBuffers; } + + AudioBufferList & GetModifiableBufferList() + { + VerifyNotTrashingOwnedBuffer(); + return _GetBufferList(); + } + + UInt32 GetNumBytes() const + { + return mBuffers[0].mDataByteSize; + } + + void SetBytes(UInt32 nBytes, void *data) + { + VerifyNotTrashingOwnedBuffer(); + check(mNumberBuffers == 1); + mBuffers[0].mDataByteSize = nBytes; + mBuffers[0].mData = data; + } + + void CopyAllFrom(CABufferList *srcbl, CABufferList *ptrbl) + // copies bytes from srcbl + // make ptrbl reflect the length copied + // note that srcbl may be same as ptrbl! + { + // Note that this buffer *can* own memory and its pointers/lengths are not + // altered; only its buffer contents, which are copied from srcbl. + // The pointers/lengths in ptrbl are updated to reflect the addresses/lengths + // of the copied data, and srcbl's contents are consumed. + ptrbl->VerifyNotTrashingOwnedBuffer(); + UInt32 nBytes = srcbl->GetNumBytes(); + AudioBuffer *mybuf = mBuffers, *srcbuf = srcbl->mBuffers, + *ptrbuf = ptrbl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf, ++ptrbuf) { + memmove(mybuf->mData, srcbuf->mData, srcbuf->mDataByteSize); + ptrbuf->mData = mybuf->mData; + ptrbuf->mDataByteSize = srcbuf->mDataByteSize; + } + if (srcbl != ptrbl) + srcbl->BytesConsumed(nBytes); + } + + void AppendFrom(CABufferList *blp, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers, *srcbuf = blp->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + check(nBytes <= srcbuf->mDataByteSize); + memcpy((Byte *)mybuf->mData + mybuf->mDataByteSize, srcbuf->mData, nBytes); + mybuf->mDataByteSize += nBytes; + } + blp->BytesConsumed(nBytes); + } + + void PadWithZeroes(UInt32 desiredBufferSize) + // for cases where an algorithm (e.g. SRC) requires some + // padding to create silence following end-of-file + { + VerifyNotTrashingOwnedBuffer(); + if (GetNumBytes() > desiredBufferSize) return; + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData + buf->mDataByteSize, 0, desiredBufferSize - buf->mDataByteSize); + buf->mDataByteSize = desiredBufferSize; + } + } + + void SetToZeroes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + memset((Byte *)buf->mData, 0, nBytes); + buf->mDataByteSize = nBytes; + } + } + + void Reset() + { + DeallocateBuffers(); + } + + Boolean SameDataAs(const CABufferList* anotherBufferList) + { + // check to see if two buffer lists point to the same memory. + if (mNumberBuffers != anotherBufferList->mNumberBuffers) return false; + + for (UInt32 i = 0; i < mNumberBuffers; ++i) { + if (mBuffers[i].mData != anotherBufferList->mBuffers[i].mData) return false; + } + return true; + } + + void BytesConsumed(UInt32 nBytes) + // advance buffer pointers, decrease buffer sizes + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + check(nBytes <= buf->mDataByteSize); + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetFrom(const AudioBufferList *abl) + { + VerifyNotTrashingOwnedBuffer(); + memcpy(&_GetBufferList(), abl, (char *)&abl->mBuffers[abl->mNumberBuffers] - (char *)abl); + } + + void SetFrom(const CABufferList *blp) + { + SetFrom(&blp->GetBufferList()); + } + + void SetFrom(const AudioBufferList *abl, UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *mybuf = mBuffers; + const AudioBuffer *srcbuf = abl->mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++mybuf, ++srcbuf) { + mybuf->mNumberChannels = srcbuf->mNumberChannels; + mybuf->mDataByteSize = nBytes; + mybuf->mData = srcbuf->mData; + } + } + + void SetFrom(const CABufferList *blp, UInt32 nBytes) + { + SetFrom(&blp->GetBufferList(), nBytes); + } + + AudioBufferList * ToAudioBufferList(AudioBufferList *abl) const + { + memcpy(abl, &GetBufferList(), (char *)&abl->mBuffers[mNumberBuffers] - (char *)abl); + return abl; + } + + void AllocateBuffers(UInt32 nBytes); + void AllocateBuffersAndCopyFrom(UInt32 nBytes, CABufferList *inCopyFromList, CABufferList *inSetPtrList); + + void DeallocateBuffers(); + + void UseExternalBuffer(Byte *ptr, UInt32 nBytes); + + void AdvanceBufferPointers(UInt32 nBytes) + // this is for bufferlists that function simply as + // an array of pointers into another bufferlist, being advanced, + // as in RenderOutput implementations + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) { + buf->mData = (Byte *)buf->mData + nBytes; + buf->mDataByteSize -= nBytes; + } + } + + void SetNumBytes(UInt32 nBytes) + { + VerifyNotTrashingOwnedBuffer(); + AudioBuffer *buf = mBuffers; + for (UInt32 i = mNumberBuffers; i--; ++buf) + buf->mDataByteSize = nBytes; + } + + void Print(const char *label=NULL, int nframes=0, int wordSize=0) const + { + if (label == NULL) + label = mName; + printf("%s - ", label); + CAShowAudioBufferList(&GetBufferList(), nframes, wordSize); + if (mBufferMemory) + printf(" owned memory @ 0x%p:\n", mBufferMemory); + } + +protected: + AudioBufferList & _GetBufferList() { return *(AudioBufferList *)&mNumberBuffers; } // use with care + // if we make this public, then we lose ability to call VerifyNotTrashingOwnedBuffer + void VerifyNotTrashingOwnedBuffer() + { + // This needs to be called from places where we are modifying the buffer list. + // It's an error to modify the buffer pointers or lengths if we own the buffer memory. + check(mBufferMemory == NULL); + } + + const char * mName; // for debugging + Byte * mBufferMemory; + // the rest must exactly mirror the structure of AudioBufferList + UInt32 mNumberBuffers; + AudioBuffer mBuffers[1]; +}; + +#endif // __CABufferList_h__ diff --git a/libs/appleutility/CAXException.cpp b/libs/appleutility/CAXException.cpp new file mode 100644 index 0000000000..088575f041 --- /dev/null +++ b/libs/appleutility/CAXException.cpp @@ -0,0 +1,45 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAXException.cpp + +=============================================================================*/ + +#include "CAXException.h" + +CAXException::WarningHandler CAXException::sWarningHandler = NULL; diff --git a/libs/appleutility/CAXException.h b/libs/appleutility/CAXException.h new file mode 100644 index 0000000000..796119763d --- /dev/null +++ b/libs/appleutility/CAXException.h @@ -0,0 +1,158 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAXException.h + +=============================================================================*/ + +#ifndef __CAXException_h__ +#define __CAXException_h__ + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include +#else + #include + #include +#endif +#include "CADebugMacros.h" +#include +#include +#include + +// An extended exception class that includes the name of the failed operation +class CAXException { +public: + CAXException(const char *operation, OSStatus err) : + mError(err) + { + if (operation == NULL) + mOperation[0] = '\0'; + else if (strlen(operation) >= sizeof(mOperation)) { + memcpy(mOperation, operation, sizeof(mOperation) - 1); + mOperation[sizeof(mOperation) - 1] = '\0'; + } else + strcpy(mOperation, operation); + } + + char *FormatError(char *str) const + { + return FormatError(str, mError); + } + + char mOperation[256]; + const OSStatus mError; + + // ------------------------------------------------- + + typedef void (*WarningHandler)(const char *msg, OSStatus err); + + /*static void Throw(const char *operation, OSStatus err) + { + throw CAXException(operation, err); + }*/ + + static char *FormatError(char *str, OSStatus error) + { + // see if it appears to be a 4-char-code + *(UInt32 *)(str + 1) = EndianU32_NtoB(error); + if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) { + str[0] = str[5] = '\''; + str[6] = '\0'; + } else + // no, format it as an integer + sprintf(str, "%ld", error); + return str; + } + + static void Warning(const char *s, OSStatus error) + { + if (sWarningHandler) + (*sWarningHandler)(s, error); + } + + static void SetWarningHandler(WarningHandler f) { sWarningHandler = f; } +private: + static WarningHandler sWarningHandler; +}; + +#if DEBUG || CoreAudio_Debug + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + char __buf[12]; \ + DebugMessageN2("error %s: %4s\n", CAXException::FormatError(__buf, __err), operation);\ + STOP; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#else + #define XThrowIfError(error, operation) \ + do { \ + OSStatus __err = error; \ + if (__err) { \ + throw CAXException(operation, __err); \ + } \ + } while (0) + + #define XThrowIf(condition, error, operation) \ + do { \ + if (condition) { \ + OSStatus __err = error; \ + throw CAXException(operation, __err); \ + } \ + } while (0) + +#endif + +#define XThrow(error, operation) XThrowIf(true, error, operation) +#define XThrowIfErr(error) XThrowIfError(error, #error) + +#endif // __CAXException_h__ diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index c15897d8be..2e5253ec25 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -171,9 +171,27 @@ def CheckJackRecomputeLatencies(context): context.Result(result) return result +jack_video_frame_offset_test = """ +#include +int main(int argc, char** argv) +{ + jack_position_t pos; + + pos.valid & JackVideoFrameOffset; + return 0; +} +""" + +def CheckJackVideoFrameOffset(context): + context.Message('Checking for JackVideoFrameOffset in jack_position_bits_t enum...') + result = context.TryLink(jack_video_frame_offset_test, '.c') + context.Result(result) + return result + conf = Configure(ardour, custom_tests = { 'CheckJackClientOpen' : CheckJackClientOpen, - 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies + 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies, + 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset }) if conf.CheckJackClientOpen(): @@ -182,6 +200,14 @@ if conf.CheckJackClientOpen(): if conf.CheckJackRecomputeLatencies(): ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCIES") +if conf.CheckJackVideoFrameOffset(): + ardour.Append(CXXFLAGS="-DHAVE_JACK_VIDEO_SUPPORT") + +if conf.CheckFunc('jack_port_ensure_monitor'): + env.Append(CCFLAGS='-DHAVE_JACK_PORT_ENSURE_MONITOR') +else: + print '\nWARNING: You need at least svn revision 985 of jack for hardware monitoring to work correctly.\n' + # # Optional header files # diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 8588c9660d..81206f2bb0 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -61,8 +61,6 @@ class AudioDiskstream : public Diskstream AudioDiskstream (Session &, const XMLNode&); ~AudioDiskstream(); - const PBD::ID& id() const { return _id; } - float playback_buffer_load() const; float capture_buffer_load() const; diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index b1ffab0944..0cab328fba 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -75,7 +75,7 @@ class AudioFileSource : public AudioSource { int move_to_trash (const string trash_dir_name); - static bool is_empty (string path); + static bool is_empty (Session&, string path); void mark_streaming_write_completed (); void mark_take (string); @@ -104,16 +104,16 @@ class AudioFileSource : public AudioSource { /* constructor to be called for existing external-to-session files */ - AudioFileSource (std::string path, Flag flags); + AudioFileSource (Session&, std::string path, Flag flags); /* constructor to be called for new in-session files */ - AudioFileSource (std::string path, Flag flags, + AudioFileSource (Session&, std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format); /* constructor to be called for existing in-session files */ - AudioFileSource (const XMLNode&); + AudioFileSource (Session&, const XMLNode&); int init (string idstr, bool must_exist); @@ -121,7 +121,6 @@ class AudioFileSource : public AudioSource { string _path; Flag _flags; string _take_id; - bool allow_remove_if_empty; uint64_t timeline_position; static string peak_dir; diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h index 0426208ba1..6a52f1c16f 100644 --- a/libs/ardour/ardour/audioplaylist.h +++ b/libs/ardour/ardour/audioplaylist.h @@ -111,6 +111,8 @@ class AudioPlaylist : public ARDOUR::Playlist bool region_changed (Change, boost::shared_ptr); void crossfade_changed (Change); void add_crossfade (Crossfade&); + + void source_offset_changed (boost::shared_ptr region); }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index 9b97a88bc0..71a66e52a0 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -174,6 +174,7 @@ class AudioRegion : public Region void recompute_at_end (); void envelope_changed (Change); + void source_offset_changed (); mutable Curve _fade_in; FadeShape _fade_in_shape; diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 751213ee8e..6a0a20d4b8 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -47,8 +47,8 @@ const jack_nframes_t frames_per_peak = 256; class AudioSource : public Source { public: - AudioSource (string name); - AudioSource (const XMLNode&); + AudioSource (Session&, string name); + AudioSource (Session&, const XMLNode&); virtual ~AudioSource (); virtual jack_nframes_t available_peaks (double zoom) const; diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index a3b84289c1..22ab706f82 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -159,8 +159,6 @@ struct ControlEvent { XMLNode &get_state(void); int set_state (const XMLNode &s); - PBD::ID id() { return _id; } - void set_max_xval (double); double get_max_xval() const { return max_xval; } @@ -189,7 +187,7 @@ struct ControlEvent { static sigc::signal AutomationListCreated; protected: - PBD::ID _id; + struct State : public ARDOUR::StateManager::State { AutomationEventList events; diff --git a/libs/ardour/ardour/coreaudiosource.h b/libs/ardour/ardour/coreaudiosource.h index cf25c466ee..49009caf53 100644 --- a/libs/ardour/ardour/coreaudiosource.h +++ b/libs/ardour/ardour/coreaudiosource.h @@ -20,15 +20,16 @@ #ifndef __coreaudio_source_h__ #define __coreaudio_source_h__ +#include + #include -#include namespace ARDOUR { class CoreAudioSource : public AudioFileSource { public: - CoreAudioSource (const XMLNode&); - CoreAudioSource (const string& path_plus_channel, Flag); + CoreAudioSource (ARDOUR::Session&, const XMLNode&); + CoreAudioSource (ARDOUR::Session&, const string& path_plus_channel, Flag); ~CoreAudioSource (); float sample_rate() const; @@ -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/ardour/destructive_filesource.h b/libs/ardour/ardour/destructive_filesource.h index fb2a3be47b..fed84217e7 100644 --- a/libs/ardour/ardour/destructive_filesource.h +++ b/libs/ardour/ardour/destructive_filesource.h @@ -31,12 +31,12 @@ namespace ARDOUR { class DestructiveFileSource : public SndFileSource { public: - DestructiveFileSource (std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, + DestructiveFileSource (Session&, std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags = AudioFileSource::Flag (AudioFileSource::Writable)); - DestructiveFileSource (std::string path, Flag flags); + DestructiveFileSource (Session&, std::string path, Flag flags); - DestructiveFileSource (const XMLNode&); + DestructiveFileSource (Session&, const XMLNode&); ~DestructiveFileSource (); jack_nframes_t last_capture_start_frame() const; diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index 2bce6a424f..048e9df90f 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -92,7 +92,6 @@ class IO; bool destructive() const { return _flags & Destructive; } virtual void set_destructive (bool yn); - const PBD::ID& id() const { return _id; } bool hidden() const { return _flags & Hidden; } bool recordable() const { return _flags & Recordable; } bool reversed() const { return _actual_speed < 0.0f; } @@ -243,7 +242,6 @@ class IO; ARDOUR::Session& _session; ARDOUR::IO* _io; ChanCount _n_channels; - PBD::ID _id; Playlist* _playlist; mutable gint _record_enabled; diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index ef0ab6c465..6074376291 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -263,8 +263,6 @@ public: void start_pan_touch (uint32_t which); void end_pan_touch (uint32_t which); - const PBD::ID& id() const { return _id; } - void defer_pan_reset (); void allow_pan_reset (); @@ -292,7 +290,6 @@ public: string _name; Connection* _input_connection; Connection* _output_connection; - PBD::ID _id; bool no_panner_reset; bool _phase_invert; XMLNode* deferred_state; diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 1052b74bd4..96fb1b1bcf 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -121,10 +121,7 @@ class Location : public sigc::trackable, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } - private: - PBD::ID _id; string _name; jack_nframes_t _start; jack_nframes_t _end; @@ -150,7 +147,6 @@ class Locations : public StateManager, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } Location *get_location_by_id(PBD::ID); Location* auto_loop_location () const; @@ -204,8 +200,6 @@ class Locations : public StateManager, public PBD::StatefulDestructible Change restore_state (StateManager::State&); StateManager::State* state_factory (std::string why) const; - - PBD::ID _id; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h index 757e33f70e..a035bf683e 100644 --- a/libs/ardour/ardour/midi_source.h +++ b/libs/ardour/ardour/midi_source.h @@ -43,8 +43,8 @@ class MidiRingBuffer; class MidiSource : public Source { public: - MidiSource (string name); - MidiSource (const XMLNode&); + MidiSource (Session& session, string name); + MidiSource (Session& session, const XMLNode&); virtual ~MidiSource (); virtual jack_nframes_t read (MidiRingBuffer& dst, jack_nframes_t start, jack_nframes_t cnt, jack_nframes_t stamp_offset) const; diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index c04b59286f..4249007fff 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -81,7 +81,6 @@ class Playlist : public StateManager, public PBD::StatefulDestructible { EditMode get_edit_mode() const { return _edit_mode; } void set_edit_mode (EditMode); - PBD::ID id() { return _id; } /* Editing operations */ void add_region (boost::shared_ptr, jack_nframes_t position, float times = 1, bool with_save = true); @@ -279,8 +278,6 @@ class Playlist : public StateManager, public PBD::StatefulDestructible { void unset_freeze_child (Playlist*); void timestamp_layer_op (boost::shared_ptr); - - PBD::ID _id; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 2a99bdcc16..d6dcd55645 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -103,7 +103,7 @@ class Port : public sigc::trackable { void ensure_monitor_input (bool yn) { -#ifdef WITH_JACK_PORT_ENSURE_MONITOR +#ifdef HAVE_JACK_PORT_ENSURE_MONITOR jack_port_ensure_monitor (_port, yn); #else jack_port_request_monitor(_port, yn); diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index c3e93fc7ae..821927f8c2 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -37,7 +37,6 @@ class XMLNode; namespace ARDOUR { class Playlist; -class Source; enum RegionEditState { EditChangesNothing = 0, @@ -99,8 +98,6 @@ class Region : public PBD::StatefulDestructible, public StateManager, public boo virtual ~Region(); - const PBD::ID& id() const { return _id; } - /* Note: changing the name of a Region does not constitute an edit */ string name() const { return _name; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 09fd01baec..99616f9729 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -267,8 +267,7 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible bool dirty() const { return _state_of_the_state & Dirty; } sigc::signal DirtyChanged; - std::string sound_dir () const; - std::string tape_dir () const; + std::string sound_dir (bool with_path = true) const; std::string peak_dir () const; std::string dead_sound_dir () const; std::string automation_dir () const; @@ -281,7 +280,8 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible static string change_audio_path_by_name (string oldpath, string oldname, string newname, bool destructive); static string change_midi_path_by_name (string oldpath, string oldname, string newname, bool destructive); - static string peak_path_from_audio_path (string); + + string peak_path_from_audio_path (string) const; string audio_path_from_name (string, uint32_t nchans, uint32_t chan, bool destructive); string midi_path_from_name (string); @@ -1713,9 +1713,10 @@ class Session : public sigc::trackable, public PBD::StatefulDestructible uint32_t _total_free_4k_blocks; Glib::Mutex space_lock; + static const char* old_sound_dir_name; static const char* sound_dir_name; - static const char* tape_dir_name; static const char* dead_sound_dir_name; + static const char* interchange_dir_name; static const char* peak_dir_name; string discover_best_sound_dir (bool destructive = false); diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 5c3fdcfec9..98e78e3802 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -44,10 +44,10 @@ class SMFSource : public MidiSource { }; /** Constructor for existing external-to-session files */ - SMFSource (std::string path, Flag flags = Flag(0)); + SMFSource (Session& session, std::string path, Flag flags = Flag(0)); /* Constructor for existing in-session files */ - SMFSource (const XMLNode&); + SMFSource (Session& session, const XMLNode&); virtual ~SMFSource (); diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index ab3e61eb29..cb6bd2e920 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -31,11 +31,11 @@ class SndFileSource : public AudioFileSource { public: /* constructor to be called for existing external-to-session files */ - SndFileSource (std::string path, Flag flags); + SndFileSource (Session&, std::string path, Flag flags); /* constructor to be called for new in-session files */ - SndFileSource (std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, + SndFileSource (Session&, std::string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags = AudioFileSource::Flag (AudioFileSource::Writable| AudioFileSource::Removable| AudioFileSource::RemovableIfEmpty| @@ -43,7 +43,7 @@ class SndFileSource : public AudioFileSource { /* constructor to be called for existing in-session files */ - SndFileSource (const XMLNode&); + SndFileSource (Session&, const XMLNode&); ~SndFileSource (); diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index a18250fff2..591e7181a1 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -32,11 +32,14 @@ namespace ARDOUR { +class Session; + class Source : public PBD::StatefulDestructible, public sigc::trackable { public: - Source (std::string name, DataType type); - Source (const XMLNode&); + Source (Session&, std::string name, DataType type); + Source (Session&, const XMLNode&); + virtual ~Source (); std::string name() const { return _name; } @@ -44,8 +47,6 @@ class Source : public PBD::StatefulDestructible, public sigc::trackable DataType type() { return _type; } - const PBD::ID& id() const { return _id; } - time_t timestamp() const { return _timestamp; } void stamp (time_t when) { _timestamp = when; } @@ -65,13 +66,13 @@ class Source : public PBD::StatefulDestructible, public sigc::trackable protected: void update_length (jack_nframes_t pos, jack_nframes_t cnt); + Session& _session; string _name; DataType _type; time_t _timestamp; jack_nframes_t _length; private: - PBD::ID _id; }; } diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index 2b25752a0d..073532c6ab 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -13,14 +13,17 @@ class XMLNode; namespace ARDOUR { +class Session; + class SourceFactory { public: static sigc::signal > SourceCreated; - static boost::shared_ptr create (const XMLNode& node); + static boost::shared_ptr create (Session&, const XMLNode& node); - static boost::shared_ptr createReadable (DataType type, std::string idstr, AudioFileSource::Flag flags, bool announce = true); - static boost::shared_ptr createWritable (DataType type, std::string name, bool destructive, jack_nframes_t rate, bool announce = true); + // MIDI sources will have to be hacked in here somehow + static boost::shared_ptr createReadable (DataType type, Session&, std::string idstr, AudioFileSource::Flag flags, bool announce = true); + static boost::shared_ptr createWritable (DataType type, Session&, std::string name, bool destructive, jack_nframes_t rate, bool announce = true); }; } diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 9111aeba68..0b37579ecb 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -242,7 +242,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible XMLNode& get_state (void); int set_state (const XMLNode&); - PBD::ID id() { return _id; } void dump (std::ostream&) const; void clear (); @@ -320,8 +319,6 @@ class TempoMap : public StateManager, public PBD::StatefulDestructible void save_state (std::string why); - PBD::ID _id; - }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 7b77aad8f5..9572297ac5 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -1590,7 +1590,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca string region_name; _session.region_name (region_name, channels[0].write_source->name(), false); - cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; + // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; try { boost::shared_ptr rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name)); @@ -1948,7 +1948,7 @@ AudioDiskstream::use_new_write_source (uint32_t n) if (chan.write_source) { - if (AudioFileSource::is_empty (chan.write_source->path())) { + if (AudioFileSource::is_empty (_session, chan.write_source->path())) { chan.write_source->mark_for_remove (); chan.write_source.reset (); } else { @@ -2185,7 +2185,8 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) } try { - fs = boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, prop->value(), false, _session.frame_rate())); + fs = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate())); } catch (failed_constructor& err) { diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 7fc29f84b8..bd09b1e6b3 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -899,3 +899,4 @@ AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist) } } } + diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index ddb835c78f..b2a1fb6a0f 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -167,6 +167,9 @@ AudioEngine::stop () bool AudioEngine::get_sync_offset (jack_nframes_t& offset) const { + +#ifdef HAVE_JACK_VIDEO_SUPPORT + jack_position_t pos; (void) jack_transport_query (_jack, &pos); @@ -176,6 +179,8 @@ AudioEngine::get_sync_offset (jack_nframes_t& offset) const return true; } +#endif + return false; } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 963a2274df..adfd352d12 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -63,8 +63,8 @@ char AudioFileSource::bwf_country_code[3] = "US"; char AudioFileSource::bwf_organization_code[4] = "LAS"; char AudioFileSource::bwf_serial_number[13] = "000000000000"; -AudioFileSource::AudioFileSource (string idstr, Flag flags) - : AudioSource (idstr), _flags (flags) +AudioFileSource::AudioFileSource (Session& s, string idstr, Flag flags) + : AudioSource (s, idstr), _flags (flags) { /* constructor used for existing external to session files. file must exist already */ @@ -74,8 +74,8 @@ AudioFileSource::AudioFileSource (string idstr, Flag flags) } -AudioFileSource::AudioFileSource (std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format) - : AudioSource (path), _flags (flags) +AudioFileSource::AudioFileSource (Session& s, std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format) + : AudioSource (s, path), _flags (flags) { /* constructor used for new internal-to-session files. file cannot exist */ @@ -84,8 +84,8 @@ AudioFileSource::AudioFileSource (std::string path, Flag flags, SampleFormat sam } } -AudioFileSource::AudioFileSource (const XMLNode& node) - : AudioSource (node), _flags (Flag (Writable|CanRename)) +AudioFileSource::AudioFileSource (Session& s, const XMLNode& node) + : AudioSource (s, node), _flags (Flag (Writable|CanRename)) { /* constructor used for existing internal-to-session files. file must exist */ @@ -101,6 +101,7 @@ AudioFileSource::AudioFileSource (const XMLNode& node) AudioFileSource::~AudioFileSource () { if (removable()) { + cerr << "Removing file " << _path << " because its removable\n"; unlink (_path.c_str()); unlink (peakpath.c_str()); } @@ -109,7 +110,7 @@ AudioFileSource::~AudioFileSource () bool AudioFileSource::removable () const { - return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && is_empty (_path))); + return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0)); } int @@ -135,7 +136,7 @@ AudioFileSource::init (string pathstr, bool must_exist) string AudioFileSource::peak_path (string audio_path) { - return Session::peak_path_from_audio_path (audio_path); + return _session.peak_path_from_audio_path (audio_path); } string @@ -527,7 +528,7 @@ void AudioFileSource::set_allow_remove_if_empty (bool yn) { if (writable()) { - allow_remove_if_empty = yn; + _flags = Flag (_flags | RemovableIfEmpty); } } @@ -561,11 +562,12 @@ AudioFileSource::set_name (string newname, bool destructive) } bool -AudioFileSource::is_empty (string path) +AudioFileSource::is_empty (Session& s, string path) { bool ret = false; + boost::shared_ptr afs = boost::dynamic_pointer_cast ( - SourceFactory::createReadable (DataType::AUDIO, path, NoPeakFile, false)); + SourceFactory::createReadable (DataType::AUDIO, s, path, NoPeakFile, false)); if (afs) { ret = (afs->length() == 0); diff --git a/libs/ardour/audiofilter.cc b/libs/ardour/audiofilter.cc index 0a630f1e25..4c38ecec20 100644 --- a/libs/ardour/audiofilter.cc +++ b/libs/ardour/audiofilter.cc @@ -51,7 +51,7 @@ AudioFilter::make_new_sources (boost::shared_ptr region, SourceList try { nsrcs.push_back (boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, path, false, session.frame_rate()))); + SourceFactory::createWritable (DataType::AUDIO, session, path, false, session.frame_rate()))); } catch (failed_constructor& err) { diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 362ab305db..959177d0cf 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "i18n.h" #include @@ -71,6 +71,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, jack_nframes_t sta _fade_out (0.0, 2.0, 1.0, false), _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + _scale_amplitude = 1.0; set_default_fades (); @@ -88,6 +93,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, jack_nframes_t sta , _fade_out (0.0, 2.0, 1.0, false) , _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + _scale_amplitude = 1.0; set_default_fades (); @@ -180,6 +190,11 @@ AudioRegion::AudioRegion (boost::shared_ptr src, const XMLNode& nod , _fade_out (0.0, 2.0, 1.0, false) , _envelope (0.0, 2.0, 1.0, false) { + boost::shared_ptr afs = boost::dynamic_pointer_cast (src); + if (afs) { + afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed)); + } + set_default_fades (); if (set_state (node)) { @@ -1165,6 +1180,12 @@ AudioRegion::speed_mismatch (float sr) const return fsr != sr; } +void +AudioRegion::source_offset_changed () +{ + set_position (source()->natural_position() + start(), this); +} + boost::shared_ptr AudioRegion::audio_source (uint32_t n) const { diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index d74a38097c..a8fe2a7c73 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -50,8 +50,8 @@ int AudioSource::peak_request_pipe[2]; bool AudioSource::_build_missing_peakfiles = false; bool AudioSource::_build_peakfiles = false; -AudioSource::AudioSource (string name) - : Source (name, DataType::AUDIO) +AudioSource::AudioSource (Session& s, string name) + : Source (s, name, DataType::AUDIO) { if (pending_peak_sources_lock == 0) { pending_peak_sources_lock = new Glib::Mutex; @@ -63,8 +63,8 @@ AudioSource::AudioSource (string name) _write_data_count = 0; } -AudioSource::AudioSource (const XMLNode& node) - : Source (node) +AudioSource::AudioSource (Session& s, const XMLNode& node) + : Source (s, node) { if (pending_peak_sources_lock == 0) { pending_peak_sources_lock = new Glib::Mutex; diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc index 0d7e690a25..049b5aabbe 100644 --- a/libs/ardour/coreaudiosource.cc +++ b/libs/ardour/coreaudiosource.cc @@ -20,6 +20,9 @@ #include #include +#include +#include + #include "i18n.h" #include @@ -27,14 +30,14 @@ using namespace ARDOUR; using namespace PBD; -CoreAudioSource::CoreAudioSource (const XMLNode& node) - : AudioFileSource (node) +CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node) + : AudioFileSource (s, node) { init (_name); } -CoreAudioSource::CoreAudioSource (const string& idstr, Flag flags) - : AudioFileSource(idstr, flags) +CoreAudioSource::CoreAudioSource (Session& s, const string& idstr, Flag flags) + : AudioFileSource(s, idstr, flags) { init (idstr); } @@ -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; } - diff --git a/libs/ardour/destructive_filesource.cc b/libs/ardour/destructive_filesource.cc index bf16b40005..1e57d88d70 100644 --- a/libs/ardour/destructive_filesource.cc +++ b/libs/ardour/destructive_filesource.cc @@ -68,21 +68,21 @@ gain_t* DestructiveFileSource::out_coefficient = 0; gain_t* DestructiveFileSource::in_coefficient = 0; jack_nframes_t DestructiveFileSource::xfade_frames = 64; -DestructiveFileSource::DestructiveFileSource (string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags) - : SndFileSource (path, samp_format, hdr_format, rate, flags) +DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags) + : SndFileSource (s, path, samp_format, hdr_format, rate, flags) { init (); } -DestructiveFileSource::DestructiveFileSource (string path, Flag flags) - : SndFileSource (path, flags) +DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags) + : SndFileSource (s, path, flags) { init (); } -DestructiveFileSource::DestructiveFileSource (const XMLNode& node) - : SndFileSource (node) +DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node) + : SndFileSource (s, node) { init (); } diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 6f3b772ece..cc92529d88 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -375,7 +375,9 @@ ARDOUR::get_user_ardour_path () /* create it if necessary */ - mkdir (path.c_str (), 0755); + if (g_mkdir_with_parents (path.c_str (), 0755)) { + throw exception (); + } return path; } diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 01c7182e14..b10f76424b 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -140,7 +140,7 @@ Session::import_audiofile (import_status& status) try { newfiles[n] = boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, buf, false, frame_rate())); + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a30881b4da..57a89b2310 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -2143,8 +2144,8 @@ IO::load_automation (const string& path) fullpath += path; in.open (fullpath.c_str()); if (!in) { - error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg; - return -1; + error << string_compose(_("%1: cannot open automation event file \"%2\" (%2)"), _name, fullpath, strerror (errno)) << endmsg; + return -1; } } diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index 30b28c6abe..6b3ea6f220 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -81,13 +81,18 @@ Location::set_start (jack_nframes_t s) { if (is_mark()) { if (_start != s) { + _start = s; _end = s; + start_changed(this); /* EMIT SIGNAL */ + if ( is_start() ) { + Session::StartTimeChanged (); /* EMIT SIGNAL */ AudioFileSource::set_header_position_offset ( s ); } + if ( is_end() ) { Session::EndTimeChanged (); /* EMIT SIGNAL */ } diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index c4cfe3c71d..de2f6b1c5d 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -42,15 +42,15 @@ using namespace PBD; sigc::signal MidiSource::MidiSourceCreated; -MidiSource::MidiSource (string name) - : Source (name, DataType::MIDI) +MidiSource::MidiSource (Session& s, string name) + : Source (s, name, DataType::MIDI) { _read_data_count = 0; _write_data_count = 0; } -MidiSource::MidiSource (const XMLNode& node) - : Source (node) +MidiSource::MidiSource (Session& s, const XMLNode& node) + : Source (s, node) { _read_data_count = 0; _write_data_count = 0; diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 1ced0faf89..0c3b0a291f 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -1090,7 +1090,7 @@ Panner::save () const ofstream out (automation_path.c_str()); if (!out) { - error << string_compose (_("cannot open pan automation file \"%1\" for saving (%s)"), automation_path, strerror (errno)) + error << string_compose (_("cannot open pan automation file \"%1\" for saving (%2)"), automation_path, strerror (errno)) << endmsg; return -1; } diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 8d200b0ee4..9a82c3bbab 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -224,13 +224,13 @@ Plugin::save_preset (string name, string domain) free(lrdf_add_preset(source.c_str(), name.c_str(), unique_id(), &defaults)); string path = string_compose("%1/.%2", envvar, domain); - if (mkdir(path.c_str(), 0775) && errno != EEXIST) { + if (g_mkdir_with_parents (path.c_str(), 0775)) { warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; return false; } path += "/rdf"; - if (mkdir(path.c_str(), 0775) && errno != EEXIST) { + if (g_mkdir_with_parents (path.c_str(), 0775)) { warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; return false; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 672e0692cb..398df02179 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -277,17 +277,17 @@ Route::process_output_buffers (BufferSet& bufs, -------------------------------------------------------------------------------------------------- */ if (declick > 0) { - Amp::run (bufs, nframes, 0.0, 1.0, _phase_invert); + Amp::run (bufs, nframes, 0.0, 1.0, false); _pending_declick = 0; } else if (declick < 0) { - Amp::run (bufs, nframes, 1.0, 0.0, _phase_invert); + Amp::run (bufs, nframes, 1.0, 0.0, false); _pending_declick = 0; } else { /* no global declick */ if (solo_gain != dsg) { - Amp::run (bufs, nframes, solo_gain, dsg, _phase_invert); + Amp::run (bufs, nframes, solo_gain, dsg, false); solo_gain = dsg; } } @@ -302,7 +302,7 @@ Route::process_output_buffers (BufferSet& bufs, } if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -379,7 +379,7 @@ Route::process_output_buffers (BufferSet& bufs, if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -543,7 +543,7 @@ Route::process_output_buffers (BufferSet& bufs, } if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } @@ -588,7 +588,7 @@ Route::process_output_buffers (BufferSet& bufs, ----------------------------------------------------------------------*/ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) { - Amp::run (bufs, nframes, mute_gain, dmg, _phase_invert); + Amp::run (bufs, nframes, mute_gain, dmg, false); mute_gain = dmg; mute_declick_applied = true; } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ebdbea1fc4..b670afb1ba 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -87,10 +87,11 @@ using boost::shared_ptr; const char* Session::_template_suffix = X_(".template"); const char* Session::_statefile_suffix = X_(".ardour"); const char* Session::_pending_suffix = X_(".pending"); -const char* Session::sound_dir_name = X_("sounds"); -const char* Session::tape_dir_name = X_("tapes"); +const char* Session::old_sound_dir_name = X_("sounds"); +const char* Session::sound_dir_name = X_("audiofiles"); const char* Session::peak_dir_name = X_("peaks"); const char* Session::dead_sound_dir_name = X_("dead_sounds"); +const char* Session::interchange_dir_name = X_("interchange"); Session::compute_peak_t Session::compute_peak = 0; Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0; @@ -2836,17 +2837,11 @@ Session::source_by_id (const PBD::ID& id) } string -Session::peak_path_from_audio_path (string audio_path) +Session::peak_path_from_audio_path (string audio_path) const { - /* XXX hardly bombproof! fix me */ - string res; - res = Glib::path_get_dirname (audio_path); - res = Glib::path_get_dirname (res); - res += '/'; - res += peak_dir_name; - res += '/'; + res = peak_dir (); res += PBD::basename_nosuffix (audio_path); res += ".peak"; @@ -2992,11 +2987,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool spath = (*i).path; - if (destructive) { - spath += tape_dir_name; - } else { - spath += sound_dir_name; - } + spath += sound_dir (false); if (destructive) { if (nchan < 2) { @@ -3032,9 +3023,10 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } } - if (access (buf, F_OK) == 0) { + if (g_file_test (buf, G_FILE_TEST_EXISTS)) { existing++; - } + } + } if (existing == 0) { @@ -3053,11 +3045,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; - if (destructive) { - spath = tape_dir (); - } else { - spath = discover_best_sound_dir (); - } + spath = discover_best_sound_dir (); string::size_type pos = foo.find_last_of ('/'); @@ -3074,7 +3062,8 @@ boost::shared_ptr Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive) { string spath = audio_path_from_name (ds.name(), ds.n_channels().get(DataType::AUDIO), chan, destructive); - return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, spath, destructive, frame_rate())); + return boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate())); } // FIXME: _terrible_ code duplication @@ -3260,7 +3249,7 @@ Session::create_midi_source_for_session (MidiDiskstream& ds) { string spath = midi_path_from_name (ds.name()); - return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, spath, false, frame_rate())); + return boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::MIDI, *this, spath, false, frame_rate())); } @@ -3420,17 +3409,35 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr a, boost:: void Session::remove_empty_sounds () { - PathScanner scanner; - string dir; - - dir = sound_dir (); - vector* possible_audiofiles = scanner (dir, "\\.wav$", false, true); + vector* possible_audiofiles = scanner (sound_dir(), "\\.(wav|aiff|caf|w64)$", false, true); + Glib::Mutex::Lock lm (source_lock); + + regex_t compiled_tape_track_pattern; + int err; + + if ((err = regcomp (&compiled_tape_track_pattern, "/T[0-9][0-9][0-9][0-9]-", REG_EXTENDED|REG_NOSUB))) { + + char msg[256]; + + regerror (err, &compiled_tape_track_pattern, msg, sizeof (msg)); + + error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg; + return; + } + for (vector::iterator i = possible_audiofiles->begin(); i != possible_audiofiles->end(); ++i) { + + /* never remove files that appear to be a tape track */ - if (AudioFileSource::is_empty (*(*i))) { + if (regexec (&compiled_tape_track_pattern, (*i)->c_str(), 0, 0, 0) == 0) { + delete *i; + continue; + } + + if (AudioFileSource::is_empty (*this, *(*i))) { unlink ((*i)->c_str()); @@ -3610,7 +3617,7 @@ jack_nframes_t Session::available_capture_duration () { const double scale = 4096.0 / sizeof (Sample); - + if (_total_free_4k_blocks * scale > (double) max_frames) { return max_frames; } @@ -3914,7 +3921,8 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf } try { - fsource = boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, buf, false, frame_rate())); + fsource = boost::dynamic_pointer_cast ( + SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); } catch (failed_constructor& err) { @@ -3931,7 +3939,7 @@ Session::write_one_audio_track (AudioTrack& track, jack_nframes_t start, jack_nf to_do = len; /* create a set of reasonably-sized buffers */ - buffers.ensure_buffers(nchans, chunk_size); +buffers.ensure_buffers(nchans, chunk_size); buffers.set_count(nchans); while (to_do && !itt.cancel) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index eaaa723a0c..a8b2804ff0 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -309,7 +309,6 @@ Session::second_stage_init (bool new_session) if (!new_session) { if (load_state (_current_snapshot_name)) { - cerr << "load state failed\n"; return -1; } remove_empty_sounds (); @@ -371,7 +370,6 @@ Session::second_stage_init (bool new_session) _end_location_is_free = false; } - restore_history(_current_snapshot_name); return 0; } @@ -436,16 +434,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - - /* tape dir */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); AudioFileSource::set_search_path (fspath); SMFSource::set_search_path (fspath); // FIXME: should be different @@ -467,16 +456,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; - fspath += ':'; - - /* add tape dir to file search path */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; + fspath += sound_dir (false); fspath += ':'; remaining = remaining.substr (colon+1); @@ -492,15 +472,9 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } - fspath += sound_dir_name; + fspath += sound_dir (false); fspath += ':'; - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += tape_dir_name; - session_dirs.push_back (sp); } @@ -518,61 +492,40 @@ int Session::create (bool& new_session, string* mix_template, jack_nframes_t initial_length) { string dir; + + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); - if (mkdir (_path.c_str(), 0755) < 0) { - if (errno == EEXIST) { - new_session = false; - } else { - error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; - return -1; - } - } else { - new_session = true; + if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + return -1; } dir = peak_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = sound_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } - } - - dir = tape_dir (); - - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session tape dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = dead_sound_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } dir = automation_dir (); - if (mkdir (dir.c_str(), 0755) < 0) { - if (errno != EEXIST) { - error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } @@ -725,6 +678,8 @@ Session::save_state (string snapshot_name, bool pending) } + cerr << "actually writing state\n"; + if (!tree.write (xml_path)) { error << string_compose (_("state could not be saved to %1"), xml_path) << endmsg; @@ -2031,13 +1986,12 @@ Session::load_sources (const XMLNode& node) boost::shared_ptr Session::XMLSourceFactory (const XMLNode& node) { - if (node.name() != "Source") { return boost::shared_ptr(); } try { - return SourceFactory::create (node); + return SourceFactory::create (*this, node); } catch (failed_constructor& err) { @@ -2062,7 +2016,7 @@ Session::save_template (string template_name) if ((dp = opendir (dir.c_str()))) { closedir (dp); } else { - if (mkdir (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)<0) { + if (g_mkdir_with_parents (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } @@ -2142,11 +2096,9 @@ Session::ensure_sound_dir (string path, string& result) /* Ensure that the parent directory exists */ - if (mkdir (path.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; - return -1; - } + if (g_mkdir_with_parents (path.c_str(), 0775)) { + error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; + return -1; } /* Ensure that the sounds directory exists */ @@ -2155,33 +2107,27 @@ Session::ensure_sound_dir (string path, string& result) result += '/'; result += sound_dir_name; - if (mkdir (result.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; - return -1; - } + if (g_mkdir_with_parents (result.c_str(), 0775)) { + error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; + return -1; } dead = path; dead += '/'; dead += dead_sound_dir_name; - if (mkdir (dead.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dead.c_str(), 0775)) { + error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; + return -1; } peak = path; peak += '/'; peak += peak_dir_name; - if (mkdir (peak.c_str(), 0775)) { - if (errno != EEXIST) { - error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; - return -1; - } + if (g_mkdir_with_parents (peak.c_str(), 0775)) { + error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; + return -1; } /* callers expect this to be terminated ... */ @@ -2196,12 +2142,6 @@ Session::discover_best_sound_dir (bool destructive) vector::iterator i; string result; - /* destructive files all go into the same place */ - - if (destructive) { - return tape_dir(); - } - /* handle common case without system calls */ if (session_dirs.size() == 1) { @@ -2418,20 +2358,37 @@ Session::dead_sound_dir () const } string -Session::sound_dir () const +Session::sound_dir (bool with_path) const { - string res = _path; + /* support old session structure */ + + struct stat statbuf; + string old; + + if (with_path) { + old = _path; + } + + old += sound_dir_name; + old += '/'; + + if (stat (old.c_str(), &statbuf) == 0) { + return old; + } + + string res; + + if (with_path) { + res = _path; + } + + res += interchange_dir_name; + res += '/'; + res += legalize_for_path (_name); + res += '/'; res += sound_dir_name; res += '/'; - return res; -} -string -Session::tape_dir () const -{ - string res = _path; - res += tape_dir_name; - res += '/'; return res; } @@ -3427,8 +3384,7 @@ Session::save_history (string snapshot_name) XMLTree tree; string xml_path; string bak_path; - - + tree.set_root (&history.get_state()); if (snapshot_name.empty()) { @@ -3436,7 +3392,6 @@ Session::save_history (string snapshot_name) } xml_path = _path + snapshot_name + ".history"; - cerr << "Saving history to " << xml_path << endmsg; bak_path = xml_path + ".bak"; @@ -3447,6 +3402,8 @@ Session::save_history (string snapshot_name) return -1; } + cerr << "actually writing history\n"; + if (!tree.write (xml_path)) { error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index c74d3021cb..887a9fa6c4 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -51,7 +51,7 @@ Session::bbt_time (jack_nframes_t when, BBT_Time& bbt) void Session::sync_time_vars () { - _current_frame_rate = _base_frame_rate * (1.0 + (video_pullup/100.0) ); + _current_frame_rate = (jack_nframes_t) round (_base_frame_rate * (1.0 + (video_pullup/100.0))); _frames_per_hour = _current_frame_rate * 3600; _frames_per_smpte_frame = (double) _current_frame_rate / (double) smpte_frames_per_second; _smpte_frames_per_hour = (unsigned long) (smpte_frames_per_second * 3600.0); @@ -432,9 +432,11 @@ Session::jack_timebase_callback (jack_transport_state_t state, pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT); } +#ifdef HAVE_JACK_VIDEO_SUPPORT //poke audio video ratio so Ardour can track Video Sync pos->audio_frames_per_video_frame = frame_rate() / smpte_frames_per_second; pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio); +#endif #if 0 /* SMPTE info */ diff --git a/libs/ardour/session_timefx.cc b/libs/ardour/session_timefx.cc index 4ba8d42d9e..b5b143514c 100644 --- a/libs/ardour/session_timefx.cc +++ b/libs/ardour/session_timefx.cc @@ -82,8 +82,7 @@ Session::tempoize_region (TimeStretchRequest& tsr) } try { - sources.push_back (boost::dynamic_pointer_cast ( - SourceFactory::createWritable (DataType::AUDIO, path, false, frame_rate()))); + sources.push_back (boost::dynamic_pointer_cast (SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()))); } catch (failed_constructor& err) { error << string_compose (_("tempoize: error creating new audio file %1 (%2)"), path, strerror (errno)) << endmsg; diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index e1845a009f..0e3064503a 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -48,8 +48,8 @@ bool SMFSource::header_position_negative; uint64_t SMFSource::header_position_offset; */ -SMFSource::SMFSource (std::string path, Flag flags) - : MidiSource (region_name_from_path(path)) +SMFSource::SMFSource (Session& s, std::string path, Flag flags) + : MidiSource (s, region_name_from_path(path)) , _channel(0) , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now , _allow_remove_if_empty(true) @@ -72,8 +72,8 @@ SMFSource::SMFSource (std::string path, Flag flags) assert(_name.find("/") == string::npos); } -SMFSource::SMFSource (const XMLNode& node) - : MidiSource (node) +SMFSource::SMFSource (Session& s, const XMLNode& node) + : MidiSource (s, node) , _channel(0) , _flags (Flag (Writable|CanRename)) , _allow_remove_if_empty(true) diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index b6ded6d617..0e80dee714 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -34,8 +34,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -SndFileSource::SndFileSource (const XMLNode& node) - : AudioFileSource (node) +SndFileSource::SndFileSource (Session& s, const XMLNode& node) + : AudioFileSource (s, node) { init (_name); @@ -52,9 +52,9 @@ SndFileSource::SndFileSource (const XMLNode& node) } } -SndFileSource::SndFileSource (string idstr, Flag flags) +SndFileSource::SndFileSource (Session& s, string idstr, Flag flags) /* files created this way are never writable or removable */ - : AudioFileSource (idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) + : AudioFileSource (s, idstr, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy))) { init (idstr); @@ -71,8 +71,8 @@ SndFileSource::SndFileSource (string idstr, Flag flags) } } -SndFileSource::SndFileSource (string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags) - : AudioFileSource(idstr, flags, sfmt, hf) +SndFileSource::SndFileSource (Session& s, string idstr, SampleFormat sfmt, HeaderFormat hf, jack_nframes_t rate, Flag flags) + : AudioFileSource (s, idstr, flags, sfmt, hf) { int fmt = 0; @@ -226,7 +226,6 @@ SndFileSource::open () _length = _info.frames; - _broadcast_info = new SF_BROADCAST_INFO; memset (_broadcast_info, 0, sizeof (*_broadcast_info)); @@ -507,6 +506,9 @@ SndFileSource::set_header_timeline_position () delete _broadcast_info; _broadcast_info = 0; } + + + } jack_nframes_t diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index d218e2cf94..977aea6efd 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -42,11 +42,9 @@ using std::max; using namespace ARDOUR; -sigc::signal Source::SourceCreated; - - -Source::Source (string name, DataType type) - : _type(type) +Source::Source (Session& s, string name, DataType type) + : _session (s) + , _type(type) { assert(_name.find("/") == string::npos); @@ -55,8 +53,9 @@ Source::Source (string name, DataType type) _length = 0; } -Source::Source (const XMLNode& node) - : _type(DataType::AUDIO) +Source::Source (Session& s, const XMLNode& node) + : _session (s) + , _type(DataType::AUDIO) { _timestamp = 0; _length = 0; diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 61a7659a5e..29cd5166ff 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -37,7 +37,7 @@ sigc::signal > SourceFactory::SourceCreated; #ifdef HAVE_COREAUDIO boost::shared_ptr -SourceFactory::create (const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node) { DataType type = DataType::AUDIO; const XMLProperty* prop = node.property("type"); @@ -49,11 +49,18 @@ SourceFactory::create (const XMLNode& node) if (node.property (X_("destructive")) != 0) { - boost::shared_ptr ret (new DestructiveFileSource (node)); + boost::shared_ptr ret (new DestructiveFileSource (s, node)); SourceCreated (ret); return ret; } else { + + try { + boost::shared_ptr ret (new CoreAudioSource (s, node)); + SourceCreated (ret); + return ret; + + } catch (failed_constructor& err) { try { boost::shared_ptr ret (new CoreAudioSource (node)); @@ -83,7 +90,7 @@ SourceFactory::create (const XMLNode& node) #else boost::shared_ptr -SourceFactory::create (const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node) { DataType type = DataType::AUDIO; const XMLProperty* prop = node.property("type"); @@ -95,20 +102,20 @@ SourceFactory::create (const XMLNode& node) if (node.property (X_("destructive")) != 0) { - boost::shared_ptr ret (new DestructiveFileSource (node)); + boost::shared_ptr ret (new DestructiveFileSource (s, node)); SourceCreated (ret); return ret; } else { - boost::shared_ptr ret (new SndFileSource (node)); + boost::shared_ptr ret (new SndFileSource (s, node)); SourceCreated (ret); return ret; } } else if (type == DataType::MIDI) { - - boost::shared_ptr ret (new SMFSource (node)); + + boost::shared_ptr ret (new SMFSource (s, node)); SourceCreated (ret); return ret; @@ -121,35 +128,34 @@ SourceFactory::create (const XMLNode& node) #ifdef HAVE_COREAUDIO boost::shared_ptr -SourceFactory::createReadable (string idstr, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { if (flags & Destructive) { - boost::shared_ptr ret (new DestructiveFileSource (idstr, flags)); + boost::shared_ptr ret (new DestructiveFileSource (s, idstr, flags)); if (announce) { SourceCreated (ret); } return ret; - } - - try { - boost::shared_ptr ret (new CoreAudioSource (idstr, flags)); - if (announce) { - SourceCreated (ret); + + try { + boost::shared_ptr ret (new CoreAudioSource (s, idstr, flags)); + if (announce) { + SourceCreated (ret); + } + return ret; + + } catch (failed_constructor& err) { + boost::shared_ptr ret (new SndFileSource (s, idstr, flags)); + if (announce) { + SourceCreated (ret); + } + return ret; } - return ret; - } - catch (failed_constructor& err) { - boost::shared_ptr ret (new SndFileSource (idstr, flags)); - if (announce) { - SourceCreated (ret); - } - return ret; - } } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (node)); + boost::shared_ptr ret (new SMFSource (s, node)); SourceCreated (ret); return ret; @@ -161,10 +167,11 @@ SourceFactory::createReadable (string idstr, AudioFileSource::Flag flags, bool a #else boost::shared_ptr -SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { - boost::shared_ptr ret (new SndFileSource (idstr, flags)); + + boost::shared_ptr ret (new SndFileSource (s, idstr, flags)); if (announce) { SourceCreated (ret); } @@ -172,8 +179,10 @@ SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Fla } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (idstr, SMFSource::Flag(0))); // FIXME: flags? - SourceCreated (ret); + boost::shared_ptr ret (new SMFSource (s, idstr, SMFSource::Flag(0))); // FIXME: flags? + if (announce) { + SourceCreated (ret); + } return ret; } @@ -184,13 +193,14 @@ SourceFactory::createReadable (DataType type, string idstr, AudioFileSource::Fla #endif // HAVE_COREAUDIO boost::shared_ptr -SourceFactory::createWritable (DataType type, std::string path, bool destructive, jack_nframes_t rate, bool announce) +SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, jack_nframes_t rate, bool announce) { /* this might throw failed_constructor(), which is OK */ if (type == DataType::AUDIO) { if (destructive) { - boost::shared_ptr ret (new DestructiveFileSource (path, + + boost::shared_ptr ret (new DestructiveFileSource (s, path, Config->get_native_file_data_format(), Config->get_native_file_header_format(), rate)); @@ -200,7 +210,8 @@ SourceFactory::createWritable (DataType type, std::string path, bool destructive return ret; } else { - boost::shared_ptr ret (new SndFileSource (path, + + boost::shared_ptr ret (new SndFileSource (s, path, Config->get_native_file_data_format(), Config->get_native_file_header_format(), rate)); @@ -208,13 +219,15 @@ SourceFactory::createWritable (DataType type, std::string path, bool destructive SourceCreated (ret); } return ret; + } + } else if (type == DataType::MIDI) { - boost::shared_ptr ret (new SMFSource (path)); + boost::shared_ptr ret (new SMFSource (s, path)); SourceCreated (ret); return ret; - + } return boost::shared_ptr (); diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 02884f062b..c2ff4f9a3a 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -919,6 +919,33 @@ jack_nframes_t TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num) { + + BBT_Time the_beat; + uint32_t ticks_one_half_subdivisions_worth; + uint32_t ticks_one_subdivisions_worth; + + bbt_time(fr, the_beat); + + ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num; + ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2; + + if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) { + uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth); + if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) { + the_beat.beats++; + the_beat.ticks += difference; + the_beat.ticks -= (uint32_t)Meter::ticks_per_beat; + } else { + the_beat.ticks += difference; + } + } else { + the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth; + } + + return frame_time (the_beat); + + /* XXX just keeping this for reference + TempoMap::BBTPointList::iterator i; TempoMap::BBTPointList *more_zoomed_bbt_points; jack_nframes_t frame_one_beats_worth; @@ -970,6 +997,9 @@ TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num) delete more_zoomed_bbt_points; return fr ; + + */ + } jack_nframes_t diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 8d9d799a44..ce4f4accd3 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -287,7 +287,7 @@ compute_equal_power_fades (jack_nframes_t nframes, float* in, float* out) in[0] = 0.0f; - for (int i = 1; i < nframes - 1; ++i) { + for (jack_nframes_t i = 1; i < nframes - 1; ++i) { in[i] = in[i-1] + step; } diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index dbb7547052..5dd32f2d51 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -174,9 +174,9 @@ VSTPlugin::get_state() if (stat (path.c_str(), &sbuf)) { if (errno == ENOENT) { - if (mkdir (path.c_str(), 0600)) { + if (g_mkdir_with_parents (path.c_str(), 0600)) { error << string_compose (_("cannot create VST chunk directory: %1"), - strerror (errno)) + strerror (errno)) << endmsg; return *root; } diff --git a/libs/gtkmm2ext/dndtreeview.cc b/libs/gtkmm2ext/dndtreeview.cc index d7ee5a7b27..83955861cf 100644 --- a/libs/gtkmm2ext/dndtreeview.cc +++ b/libs/gtkmm2ext/dndtreeview.cc @@ -48,6 +48,8 @@ DnDTreeView::serialize_pointers (RefPtr model, TreeSelection::ListHan uint32_t cnt = selection->size(); uint32_t sz = (sizeof (void*) * cnt) + sizeof (SerializedObjectPointers); + cerr << "lets plan to serialize " << cnt << " from selection\n"; + char* buf = new char[sz]; SerializedObjectPointers* sr = new (buf) SerializedObjectPointers; @@ -59,10 +61,12 @@ DnDTreeView::serialize_pointers (RefPtr model, TreeSelection::ListHan cnt = 0; for (TreeSelection::ListHandle_Path::iterator x = selection->begin(); x != selection->end(); ++x, ++cnt) { + cerr << "getting next item\n"; TreeModel::Row row = *(model->get_iter (*x)); row.get_value (data_column, sr->ptr[cnt]); } - + + cerr << "returning an SR with size = " << sr->size << endl; return sr; } @@ -79,6 +83,7 @@ DnDTreeView::on_drag_data_get(const RefPtr& context, SelectionData& SerializedObjectPointers* sr = serialize_pointers (get_model(), &selection, selection_data.get_target()); selection_data.set (8, (guchar*)sr, sr->size); + cerr << "selection data set to contain " << sr->size << endl; } } diff --git a/libs/pbd/pbd/stateful.h b/libs/pbd/pbd/stateful.h index 5adddfc1c0..5fbac11e5c 100644 --- a/libs/pbd/pbd/stateful.h +++ b/libs/pbd/pbd/stateful.h @@ -42,7 +42,7 @@ class Stateful { virtual void add_instant_xml (XMLNode&, const std::string& dir); XMLNode *instant_xml (const std::string& str, const std::string& dir); - PBD::ID id() { return _id; } + const PBD::ID& id() const { return _id; } protected: XMLNode *_extra_xml; -- cgit v1.2.3