summaryrefslogtreecommitdiff
path: root/libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2015-10-17 20:46:58 -0400
committerRobin Gareus <robin@gareus.org>2015-10-18 23:03:13 +0200
commit66704eefcbe132eac0415434340f788808c40302 (patch)
tree2d4b596265f2fd801244862cef8bb79a63eea289 /libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp
parentf7e3117c3b3f09cc10cb10434660accf4ef49fc8 (diff)
alternative new version of the AppleUtility library
Diffstat (limited to 'libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp')
-rw-r--r--libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp b/libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp
new file mode 100644
index 0000000000..1a00f96251
--- /dev/null
+++ b/libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp
@@ -0,0 +1,1966 @@
+/*
+ File: AudioFileObject.cpp
+ Abstract: AudioFileObject.h
+ Version: 1.1
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
+ 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 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.
+
+ Copyright (C) 2014 Apple Inc. All Rights Reserved.
+
+*/
+#include "AudioFileObject.h"
+#include "CADebugMacros.h"
+#include <algorithm>
+#include <sys/stat.h>
+
+#define kAudioFileNoCacheMask 0x20
+
+const SInt64 kScanToEnd = 0x7fffFFFFffffFFFFLL;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+AudioFileObject::~AudioFileObject()
+{
+ delete mDataSource;
+ DeletePacketTable();
+ SetURL(NULL);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoCreate(
+ CFURLRef inFileRef,
+ const AudioStreamBasicDescription *inFormat,
+ UInt32 inFlags)
+{
+ // common prologue
+ if (!IsDataFormatValid(inFormat))
+ return kAudioFileUnsupportedDataFormatError;
+
+ if (!IsDataFormatSupported(inFormat))
+ return kAudioFileUnsupportedDataFormatError;
+
+ SetPermissions(kAudioFileReadWritePermission);
+
+ SetAlignDataWithFillerChunks(!(inFlags & 2 /* kAudioFileFlags_DontPageAlignAudioData */ ));
+
+ // call virtual method for particular format.
+ return Create(inFileRef, inFormat);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::Create(
+ CFURLRef inFileRef,
+ const AudioStreamBasicDescription *inFormat)
+{
+ int fileD;
+ OSStatus err = CreateDataFile (inFileRef, fileD);
+ FailIf (err != noErr, Bail, "CreateDataFile failed");
+
+ SetURL (inFileRef);
+
+ err = OpenFile(kAudioFileReadWritePermission, fileD);
+ FailIf (err != noErr, Bail, "OpenFile failed");
+
+ err = SetDataFormat(inFormat);
+ FailIf (err != noErr, Bail, "SetDataFormat failed");
+
+ mIsInitialized = false;
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoOpen(
+ CFURLRef inFileRef,
+ SInt8 inPermissions,
+ int inFD)
+{
+ OSStatus err = noErr;
+ SetPermissions(inPermissions);
+
+ err = Open(inFileRef, inPermissions, inFD);
+ FailIf (err != noErr, Bail, "Open failed");
+
+ err = ValidateFormatAndData();
+ FailIf (err != noErr, Bail, "ValidateFormatAndData failed");
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::Open(
+ CFURLRef inFileRef,
+ SInt8 inPermissions,
+ int inFD)
+{
+ if (!(inPermissions & kAudioFileReadPermission))
+ return kAudioFilePermissionsError; // file must have read permissions
+
+ SetURL(inFileRef);
+
+ OSStatus err = OpenFile(inPermissions, inFD);
+ FailIf (err != noErr, Bail, "OpenFile failed");
+
+ err = OpenFromDataSource();
+ FailIf (err != noErr, Bail, "OpenFromDataSource failed");
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ValidateFormatAndData()
+{
+
+
+ AudioStreamBasicDescription asbd = GetDataFormat();
+
+ if (!IsDataFormatValid(&asbd))
+ return kAudioFileInvalidFileError;
+
+ if (asbd.mFormatID == kAudioFormatLinearPCM)
+ {
+ SInt64 maxPackets = GetNumBytes() / asbd.mBytesPerPacket;
+ if (GetNumPackets() > maxPackets)
+ return kAudioFileInvalidFileError;
+ }
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoOpenWithCallbacks(
+ void * inRefCon,
+ AudioFile_ReadProc inReadFunc,
+ AudioFile_WriteProc inWriteFunc,
+ AudioFile_GetSizeProc inGetSizeFunc,
+ AudioFile_SetSizeProc inSetSizeFunc)
+{
+ SInt8 perms = (inSetSizeFunc || inWriteFunc) ? kAudioFileReadWritePermission : kAudioFileReadPermission;
+ SetPermissions(perms);
+
+ DataSource* dataSource = new Seekable_DataSource(inRefCon, inReadFunc, inWriteFunc, inGetSizeFunc, inSetSizeFunc);
+ SetDataSource(dataSource);
+ return OpenFromDataSource();
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::OpenFromDataSource(void)
+{
+ return noErr;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoInitializeWithCallbacks(
+ void * inRefCon,
+ AudioFile_ReadProc inReadFunc,
+ AudioFile_WriteProc inWriteFunc,
+ AudioFile_GetSizeProc inGetSizeFunc,
+ AudioFile_SetSizeProc inSetSizeFunc,
+ UInt32 inFileType,
+ const AudioStreamBasicDescription *inFormat,
+ UInt32 inFlags)
+{
+ DataSource* dataSource = new Seekable_DataSource(inRefCon, inReadFunc, inWriteFunc, inGetSizeFunc, inSetSizeFunc);
+ if (!dataSource->CanWrite()) return -54/*permErr*/;
+ dataSource->SetSize(0);
+ SetDataSource(dataSource);
+ SetPermissions(kAudioFileReadWritePermission);
+
+ SetAlignDataWithFillerChunks(!(inFlags & 2 /* kAudioFileFlags_DontPageAlignAudioData */ ));
+
+ OSStatus err = SetDataFormat(inFormat);
+ if (err) return err;
+
+ return InitializeDataSource(inFormat, inFlags);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoInitialize(
+ CFURLRef inFileRef,
+ const AudioStreamBasicDescription *inFormat,
+ UInt32 inFlags)
+{
+ SetURL (inFileRef);
+ SetPermissions(kAudioFileReadWritePermission);
+
+ SetAlignDataWithFillerChunks(!(inFlags & 2 /* kAudioFileFlags_DontPageAlignAudioData */ ));
+
+ return Initialize(inFileRef, inFormat, inFlags);
+}
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::Initialize(
+ CFURLRef inFileRef,
+ const AudioStreamBasicDescription *inFormat,
+ UInt32 inFlags)
+{
+ OSStatus err = noErr;
+
+ UInt8 fPath[FILENAME_MAX];
+ if (!CFURLGetFileSystemRepresentation (inFileRef, true, fPath, FILENAME_MAX))
+ return kAudio_FileNotFoundError;
+
+#if TARGET_OS_WIN32
+ int filePerms = 0;
+ int flags = O_TRUNC | O_RDWR | O_BINARY;
+#else
+ mode_t filePerms = 0;
+ int flags = O_TRUNC | O_RDWR;
+#endif
+
+ int fileD = open((const char*)fPath, flags, filePerms);
+ if (fileD < 0)
+ return AudioFileTranslateErrno(errno);
+
+ err = OpenFile(kAudioFileReadWritePermission, fileD);
+ FailIf (err != noErr, Bail, "OpenFile failed");
+
+ // don't need to do this as open has an option to truncate the file
+// GetDataSource()->SetSize(0);
+
+ err = SetDataFormat(inFormat);
+ FailIf (err != noErr, Bail, "SetDataFormat failed");
+
+ InitializeDataSource(inFormat, inFlags);
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::InitializeDataSource(const AudioStreamBasicDescription *inFormat, UInt32 /*inFlags*/)
+{
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::DoClose()
+{
+ OSStatus err = UpdateSizeIfNeeded();
+ if (err) return err;
+
+ return Close();
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::Close()
+{
+ try {
+ delete mDataSource;
+ mDataSource = 0;
+ } catch (OSStatus err) {
+ return err;
+ } catch (...) {
+ return kAudioFileUnspecifiedError;
+ }
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+OSStatus AudioFileObject::Optimize()
+{
+ // default is that nothing needs to be done. This happens to be true for Raw, SD2 and NeXT/Sun types.
+ SetIsOptimized(true);
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+OSStatus AudioFileObject::DoOptimize()
+{
+ if (!CanWrite()) return kAudioFilePermissionsError;
+
+ OSStatus err = UpdateSizeIfNeeded();
+ if (err) return err;
+
+ if (IsOptimized()) return noErr;
+
+ err = Optimize();
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::UpdateNumBytes(SInt64 inNumBytes)
+{
+ OSStatus err = noErr;
+ if (inNumBytes != GetNumBytes()) {
+ SetNumBytes(inNumBytes);
+
+ // #warning " this will not work for vbr formats"
+ SetNumPackets(GetNumBytes() / mDataFormat.mBytesPerPacket);
+ SizeChanged();
+ }
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::UpdateNumPackets(SInt64 inNumPackets)
+{
+ OSStatus err = noErr;
+ if (inNumPackets != GetNumPackets()) {
+ // sync current state.
+ SetNeedsSizeUpdate(true);
+ UpdateSizeIfNeeded();
+ SetNumPackets(inNumPackets);
+
+ // #warning " this will not work for vbr formats"
+ SetNumBytes(GetNumPackets() * mDataFormat.mBytesPerFrame);
+ SizeChanged();
+ }
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::PacketToFrame(SInt64 inPacket, SInt64& outFirstFrameInPacket)
+{
+ if (mDataFormat.mFramesPerPacket == 0)
+ {
+ OSStatus err = ScanForPackets(inPacket+1); // the packet count must be one greater than the packet index
+ if (err) return err;
+
+ SInt64 packetTableSize = GetPacketTableSize();
+
+ if (mPacketTable && inPacket >= packetTableSize)
+ return kAudioFileEndOfFileError;
+
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidPacketOffsetError;
+
+ if (inPacket < 0 || inPacket >= packetTableSize)
+ return kAudioFileInvalidPacketOffsetError;
+
+ outFirstFrameInPacket = (*packetTable)[(size_t)inPacket].mFrameOffset;
+ }
+ else
+ {
+ outFirstFrameInPacket = inPacket * mDataFormat.mFramesPerPacket;
+ }
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::FrameToPacket(SInt64 inFrame, SInt64& outPacket, UInt32& outFrameOffsetInPacket)
+{
+ if (mDataFormat.mFramesPerPacket == 0)
+ {
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidPacketOffsetError;
+
+ // search packet table
+ AudioStreamPacketDescriptionExtended pext;
+ memset(&pext, 0, sizeof(pext));
+ pext.mFrameOffset = inFrame;
+ CompressedPacketTable::iterator iter = std::lower_bound(packetTable->begin(), packetTable->end(), pext);
+
+ if (iter == packetTable->end())
+ return kAudioFileInvalidPacketOffsetError;
+
+ if (iter > packetTable->begin()) --iter;
+
+ outPacket = iter - packetTable->begin();
+ outFrameOffsetInPacket = (UInt32)(inFrame - iter->mFrameOffset);
+ }
+ else
+ {
+ outPacket = inFrame / mDataFormat.mFramesPerPacket;
+ outFrameOffsetInPacket = (UInt32)(inFrame % mDataFormat.mFramesPerPacket);
+ }
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::PacketToByte(AudioBytePacketTranslation* abpt)
+{
+ if (abpt->mPacket < 0)
+ return kAudioFileInvalidPacketOffsetError;
+
+ if (mDataFormat.mBytesPerPacket == 0)
+ {
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidPacketOffsetError;
+
+ if (abpt->mPacket < GetPacketTableSize()) {
+ abpt->mByte = (*packetTable)[(int)abpt->mPacket].mStartOffset;
+ abpt->mFlags = 0;
+ } else {
+ SInt64 numPackets = packetTable->size();
+ if (numPackets < 8)
+ return 'more' /*kAudioFileStreamError_DataUnavailable*/ ;
+
+ const AudioStreamPacketDescriptionExtended lastPacket = (*packetTable)[numPackets - 1];
+ SInt64 bytesReadSoFar = lastPacket.mStartOffset + lastPacket.mDataByteSize;
+ double averageBytesPerPacket = (double)(bytesReadSoFar - GetDataOffset()) / (double)numPackets;
+ abpt->mByte = (SInt64)floor((double)abpt->mPacket * averageBytesPerPacket);
+ abpt->mFlags = kBytePacketTranslationFlag_IsEstimate;
+ }
+ }
+ else
+ {
+ abpt->mByte = abpt->mPacket * mDataFormat.mBytesPerPacket;
+ abpt->mFlags = 0;
+ }
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+inline bool byte_less_than (const AudioStreamPacketDescriptionExtended& a, const AudioStreamPacketDescriptionExtended& b)
+{
+ return a.mStartOffset < b.mStartOffset;
+}
+
+OSStatus AudioFileObject::ByteToPacket(AudioBytePacketTranslation* abpt)
+{
+ if (abpt->mByte < 0)
+ return kAudioFileInvalidPacketOffsetError;
+
+ if (mDataFormat.mBytesPerPacket == 0)
+ {
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidPacketOffsetError;
+ // search packet table
+ AudioStreamPacketDescriptionExtended pext;
+ memset(&pext, 0, sizeof(pext));
+ pext.mStartOffset = abpt->mByte;
+ CompressedPacketTable::iterator iter = std::lower_bound(packetTable->begin(), packetTable->end(), pext, byte_less_than);
+
+ if (iter == packetTable->end()) {
+ SInt64 numPackets = packetTable->size();
+ if (numPackets < 8)
+ return 'more' /*kAudioFileStreamError_DataUnavailable*/ ;
+
+ const AudioStreamPacketDescriptionExtended lastPacket = (*packetTable)[numPackets - 1];
+ SInt64 bytesReadSoFar = lastPacket.mStartOffset + lastPacket.mDataByteSize;
+ double averageBytesPerPacket = (double)(bytesReadSoFar - GetDataOffset()) / (double)numPackets;
+
+ double fpacket = (double)abpt->mByte / averageBytesPerPacket;
+ abpt->mPacket = (SInt64)floor(fpacket);
+ abpt->mByteOffsetInPacket = (UInt32)floor((fpacket - (double)abpt->mPacket) * averageBytesPerPacket);
+ abpt->mFlags = kBytePacketTranslationFlag_IsEstimate;
+
+ } else {
+ if (iter > packetTable->begin()) --iter;
+ abpt->mPacket = iter - packetTable->begin();
+ abpt->mByteOffsetInPacket = (UInt32)(abpt->mByte - iter->mStartOffset);
+ abpt->mFlags = 0;
+ }
+ }
+ else
+ {
+ abpt->mPacket = abpt->mByte / mDataFormat.mBytesPerPacket;
+ abpt->mByteOffsetInPacket = (UInt32)(abpt->mByte % mDataFormat.mBytesPerPacket);
+ abpt->mFlags = 0;
+ }
+ return noErr;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ReadBytes(
+ Boolean inUseCache,
+ SInt64 inStartingByte,
+ UInt32 *ioNumBytes,
+ void *outBuffer)
+{
+ OSStatus err = noErr;
+ UInt16 mode = SEEK_SET;
+ SInt64 fileOffset = mDataOffset + inStartingByte;
+ bool readingPastEnd = false;
+
+ FailWithAction((ioNumBytes == NULL) || (outBuffer == NULL), err = kAudio_ParamError,
+ Bail, "invalid num bytes parameter");
+
+ //printf("inStartingByte %lld GetNumBytes %lld\n", inStartingByte, GetNumBytes());
+
+ if (inStartingByte >= GetNumBytes())
+ {
+ *ioNumBytes = 0;
+ return kAudioFileEndOfFileError;
+ }
+
+ if ((fileOffset + *ioNumBytes) > (GetNumBytes() + mDataOffset))
+ {
+ *ioNumBytes = (UInt32)(GetNumBytes() + mDataOffset - fileOffset);
+ readingPastEnd = true;
+ }
+ //printf("fileOffset %lld mDataOffset %lld readingPastEnd %d\n", fileOffset, mDataOffset, readingPastEnd);
+
+ if (!inUseCache)
+ mode |= kAudioFileNoCacheMask;
+
+ err = GetDataSource()->ReadBytes(mode, fileOffset, *ioNumBytes, outBuffer, ioNumBytes);
+
+ if (readingPastEnd && err == noErr)
+ err = kAudioFileEndOfFileError;
+
+Bail:
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::WriteBytes(
+ Boolean inUseCache,
+ SInt64 inStartingByte,
+ UInt32 *ioNumBytes,
+ const void *inBuffer)
+{
+ OSStatus err = noErr;
+ UInt16 mode = SEEK_SET;
+ bool extendingTheAudioData;
+
+ if (!CanWrite()) return kAudioFilePermissionsError;
+
+ FailWithAction((ioNumBytes == NULL) || (inBuffer == NULL), err = kAudioFileUnspecifiedError, Bail, "invalid parameters");
+
+ // Do not try to write to a postion greater than 32 bits for some file types
+ // see if starting byte + ioNumBytes is greater than 32 bits
+ // if so, see if file type supports this and bail if not
+ err = IsValidFilePosition(inStartingByte + *ioNumBytes);
+ FailIf(err != noErr, Bail, "invalid file position");
+
+ extendingTheAudioData = inStartingByte + *ioNumBytes > GetNumBytes();
+
+ // if file is not optimized, then do not write data that would overwrite chunks following the sound data chunk
+ FailWithAction( extendingTheAudioData && !IsOptimized(),
+ err = kAudioFileNotOptimizedError, Bail, "Can't write more data until the file is optimized");
+
+ if (!inUseCache)
+ mode |= kAudioFileNoCacheMask;
+
+ err = GetDataSource()->WriteBytes(mode, mDataOffset + inStartingByte, *ioNumBytes,
+ inBuffer, ioNumBytes);
+
+ FailIf(err != noErr, Bail, "couldn't write new data");
+
+ if (extendingTheAudioData) {
+ SInt64 nuEOF; // Get the total bytes of audio data
+ SInt64 nuByteTotal;
+
+ err = GetDataSource()->GetSize(nuEOF);
+ FailIf(err != noErr, Bail, "GetSize failed");
+
+ nuByteTotal = nuEOF - mDataOffset;
+ err = UpdateNumBytes(nuByteTotal);
+ }
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ReadPackets(
+ Boolean inUseCache,
+ UInt32 *outNumBytes,
+ AudioStreamPacketDescription *outPacketDescriptions,
+ SInt64 inStartingPacket,
+ UInt32 *ioNumPackets,
+ void *outBuffer)
+{
+ // This only works with CBR. To suppport VBR you must override.
+ OSStatus err = noErr;
+
+ FailWithAction(outBuffer == NULL, err = kAudio_ParamError, Bail, "NULL buffer");
+
+ FailWithAction((ioNumPackets == NULL) || (*ioNumPackets < 1), err = kAudio_ParamError, Bail, "invalid num packets parameter");
+
+ {
+ UInt32 byteCount = *ioNumPackets * mDataFormat.mBytesPerPacket;
+ SInt64 startingByte = inStartingPacket * mDataFormat.mBytesPerPacket;
+
+ err = ReadBytes (inUseCache, startingByte, &byteCount, outBuffer);
+ if ((err == noErr) || (err == kAudioFileEndOfFileError))
+ {
+ if (byteCount != (*ioNumPackets * mDataFormat.mBytesPerPacket))
+ {
+ *ioNumPackets = byteCount / mDataFormat.mBytesPerPacket;
+ byteCount = *ioNumPackets * mDataFormat.mBytesPerPacket;
+ }
+
+ if (outNumBytes)
+ *outNumBytes = byteCount;
+
+ if (err == kAudioFileEndOfFileError)
+ err = noErr;
+ }
+ }
+Bail:
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ReadPacketData(
+ Boolean inUseCache,
+ UInt32 *ioNumBytes,
+ AudioStreamPacketDescription *outPacketDescriptions,
+ SInt64 inStartingPacket,
+ UInt32 *ioNumPackets,
+ void *outBuffer)
+{
+ OSStatus err = noErr;
+ FailWithAction(ioNumPackets == NULL || *ioNumPackets < 1, err = kAudio_ParamError, Bail, "invalid ioNumPackets parameter");
+ FailWithAction(ioNumBytes == NULL || *ioNumBytes < 1, err = kAudio_ParamError, Bail, "invalid ioNumBytes parameter");
+ if (mDataFormat.mBytesPerPacket) {
+ // CBR
+ FailWithAction(outBuffer == NULL, err = kAudio_ParamError, Bail, "NULL buffer");
+ UInt32 maxPackets = *ioNumBytes / mDataFormat.mBytesPerPacket;
+ if (*ioNumPackets > maxPackets) *ioNumPackets = maxPackets;
+
+ if (outBuffer) {
+ UInt32 byteCount = *ioNumPackets * mDataFormat.mBytesPerPacket;
+ SInt64 startingByte = inStartingPacket * mDataFormat.mBytesPerPacket;
+ err = ReadBytes (inUseCache, startingByte, &byteCount, outBuffer);
+ if (err == noErr || err == kAudioFileEndOfFileError) {
+ if (byteCount != (*ioNumPackets * mDataFormat.mBytesPerPacket)) {
+ *ioNumPackets = byteCount / mDataFormat.mBytesPerPacket;
+ byteCount = *ioNumPackets * mDataFormat.mBytesPerPacket;
+ }
+ *ioNumBytes = byteCount;
+ }
+ }
+ } else {
+ FailWithAction(outPacketDescriptions == NULL, err = kAudio_ParamError, Bail, "invalid outPacketDescriptions parameter");
+ err = ReadPacketDataVBR(inUseCache, ioNumBytes, outPacketDescriptions, inStartingPacket, ioNumPackets, outBuffer);
+ }
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ReadPacketDataVBR(
+ Boolean inUseCache,
+ UInt32 *ioNumBytes,
+ AudioStreamPacketDescription *outPacketDescriptions,
+ SInt64 inStartingPacket,
+ UInt32 *ioNumPackets,
+ void *outBuffer)
+{
+ OSStatus err = ScanForPackets(inStartingPacket+1); // need to scan packets up to start
+ if (err && err != kAudioFileEndOfFileError)
+ return err;
+
+ SInt64 dataOffset = GetDataOffset();
+
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidFileError;
+
+ SInt64 packetTableSize = GetPacketTableSize();
+
+ if (inStartingPacket >= packetTableSize) {
+ *ioNumBytes = 0;
+ *ioNumPackets = 0;
+ return kAudioFileEndOfFileError;
+ }
+
+ if (inStartingPacket + *ioNumPackets <= packetTableSize) {
+ err = ReadPacketDataVBR_InTable(inUseCache, ioNumBytes, outPacketDescriptions, inStartingPacket, ioNumPackets, outBuffer);
+ } else {
+
+ AudioStreamPacketDescription firstPacket = (*packetTable)[inStartingPacket];
+ SInt64 firstPacketOffset = firstPacket.mStartOffset;
+ UInt32 bytesRead = *ioNumBytes;
+ SInt64 fileSize = 0;
+ GetDataSource()->GetSize(fileSize);
+ SInt64 remainingBytesInFile = fileSize - firstPacketOffset - dataOffset;
+ if (bytesRead > remainingBytesInFile)
+ bytesRead = (UInt32)remainingBytesInFile;
+
+ err = ReadBytes (inUseCache, firstPacketOffset, &bytesRead, outBuffer);
+ if (err && err != kAudioFileEndOfFileError) {
+ *ioNumBytes = 0;
+ *ioNumPackets = 0;
+ return err;
+ }
+
+ Buffer_DataSource bufSrc(bytesRead, outBuffer, dataOffset + firstPacketOffset);
+
+ OSStatus scanErr = ScanForPackets(kScanToEnd, &bufSrc, false);
+ if (scanErr && scanErr != kAudioFileEndOfFileError)
+ return scanErr;
+ packetTableSize = packetTable->size();
+
+ UInt32 numPacketsRead = 0;
+ UInt32 endOfData = 0;
+ SInt64 packetNumber = inStartingPacket;
+ for (; numPacketsRead < *ioNumPackets && packetNumber < packetTableSize; ++numPacketsRead, ++packetNumber) {
+ AudioStreamPacketDescription curPacket = (*packetTable)[numPacketsRead + inStartingPacket];
+ SInt64 curPacketOffset = curPacket.mStartOffset - firstPacketOffset;
+ SInt64 endOfPacket = curPacketOffset + curPacket.mDataByteSize;
+ if (endOfPacket > bytesRead) break;
+ endOfData = (UInt32)endOfPacket;
+ outPacketDescriptions[numPacketsRead] = curPacket;
+ outPacketDescriptions[numPacketsRead].mStartOffset = curPacketOffset;
+ }
+
+ *ioNumBytes = endOfData;
+ *ioNumPackets = numPacketsRead;
+ }
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+OSStatus AudioFileObject::HowManyPacketsCanBeReadIntoBuffer(UInt32* ioNumBytes, SInt64 inStartingPacket, UInt32 *ioNumPackets)
+{
+ CompressedPacketTable* packetTable = GetPacketTable();
+ SInt64 packetTableSize = GetPacketTableSize();
+
+ if (inStartingPacket + *ioNumPackets > (SInt64)packetTableSize) {
+ *ioNumPackets = (UInt32)(packetTableSize - inStartingPacket);
+ }
+
+ AudioStreamPacketDescription firstPacket = (*packetTable)[inStartingPacket];
+
+ if (*ioNumBytes < firstPacket.mDataByteSize) {
+ *ioNumBytes = 0;
+ *ioNumPackets = 0;
+ return kAudio_ParamError;
+ }
+
+ SInt64 lastPacketIndex = inStartingPacket + *ioNumPackets - 1;
+ if (lastPacketIndex >= packetTableSize)
+ lastPacketIndex = packetTableSize - 1;
+
+ AudioStreamPacketDescription lastPacket = (*packetTable)[lastPacketIndex];
+
+ SInt64 readBytes = lastPacket.mStartOffset + lastPacket.mDataByteSize - firstPacket.mStartOffset;
+ if (readBytes <= *ioNumBytes) {
+ *ioNumBytes = (UInt32)readBytes;
+ return noErr;
+ }
+
+ SInt64 lowBound = inStartingPacket;
+ SInt64 highBound = lastPacketIndex + 1;
+ SInt64 okIndex = lowBound;
+ while (highBound >= lowBound) {
+ SInt64 tryBound = (lowBound + highBound) >> 1;
+ if (tryBound > lastPacketIndex)
+ break;
+ AudioStreamPacketDescription tryPacket = (*packetTable)[tryBound];
+
+ SInt64 readBytes = tryPacket.mStartOffset + tryPacket.mDataByteSize - firstPacket.mStartOffset;
+
+ if (readBytes > (SInt64)*ioNumBytes) {
+ highBound = tryBound - 1;
+ } else if (readBytes < (SInt64)*ioNumBytes) {
+ okIndex = tryBound;
+ lowBound = tryBound + 1;
+ } else {
+ okIndex = tryBound;
+ break;
+ }
+ }
+
+ SInt64 numPackets = okIndex - inStartingPacket + 1;
+ if (numPackets > *ioNumPackets) {
+ numPackets = *ioNumPackets;
+ okIndex = inStartingPacket + numPackets - 1;
+ }
+
+ AudioStreamPacketDescription packet = (*packetTable)[okIndex];
+ *ioNumBytes = (UInt32)(packet.mStartOffset + packet.mDataByteSize - firstPacket.mStartOffset);
+ *ioNumPackets = (UInt32)numPackets;
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::ReadPacketDataVBR_InTable(
+ Boolean inUseCache,
+ UInt32 *ioNumBytes,
+ AudioStreamPacketDescription *outPacketDescriptions,
+ SInt64 inStartingPacket,
+ UInt32 *ioNumPackets,
+ void *outBuffer)
+{
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (!packetTable)
+ return kAudioFileInvalidFileError;
+
+ OSStatus err = HowManyPacketsCanBeReadIntoBuffer(ioNumBytes, inStartingPacket, ioNumPackets);
+ if (err) return err;
+
+ AudioStreamPacketDescription firstPacket = (*packetTable)[inStartingPacket];
+ SInt64 firstPacketOffset = firstPacket.mStartOffset;
+ UInt32 bytesRead = *ioNumBytes;
+
+ if (outBuffer) {
+ err = ReadBytes (inUseCache, firstPacketOffset, &bytesRead, outBuffer);
+ if (err && err != kAudioFileEndOfFileError) {
+ *ioNumBytes = 0;
+ *ioNumPackets = 0;
+ return err;
+ }
+ *ioNumBytes = bytesRead;
+ }
+
+ // fill out packet descriptions
+ if (outPacketDescriptions) {
+ for (UInt32 i = 0; i < *ioNumPackets; i++) {
+ AudioStreamPacketDescription curPacket = (*packetTable)[i + inStartingPacket];
+ outPacketDescriptions[i] = curPacket;
+ outPacketDescriptions[i].mStartOffset = curPacket.mStartOffset - firstPacketOffset;
+ }
+ }
+
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::WritePackets(
+ Boolean inUseCache,
+ UInt32 inNumBytes,
+ const AudioStreamPacketDescription *inPacketDescriptions,
+ SInt64 inStartingPacket,
+ UInt32 *ioNumPackets,
+ const void *inBuffer)
+{
+ // This only works with CBR. To suppport VBR you must override.
+ OSStatus err = noErr;
+
+ FailWithAction(inStartingPacket > GetNumPackets(), err = kAudioFileInvalidPacketOffsetError, Bail, "write past end");
+ FailWithAction((ioNumPackets == NULL) || (inBuffer == NULL), err = kAudioFileUnspecifiedError, Bail, "invalid parameter");
+
+ {
+ UInt32 byteCount = *ioNumPackets * mDataFormat.mBytesPerPacket;
+ SInt64 startingByte = inStartingPacket * mDataFormat.mBytesPerPacket;
+
+ err = WriteBytes(inUseCache, startingByte, &byteCount, inBuffer);
+ FailIf (err != noErr, Bail, "Write Bytes Failed");
+
+ if (byteCount != (*ioNumPackets * mDataFormat.mBytesPerPacket))
+ *ioNumPackets = byteCount / mDataFormat.mBytesPerPacket;
+ }
+Bail:
+ return (err);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetBitRate( UInt32 *outBitRate)
+{
+
+ if (!outBitRate) return kAudioFileUnspecifiedError;
+
+ UInt32 bytesPerPacket = GetDataFormat().mBytesPerPacket;
+ UInt32 framesPerPacket = GetDataFormat().mFramesPerPacket;
+ Float64 sampleRate = GetDataFormat().mSampleRate;
+ const Float64 bitsPerByte = 8.;
+
+ if (bytesPerPacket && framesPerPacket) {
+ *outBitRate = (UInt32)(bitsPerByte * (Float64)bytesPerPacket * sampleRate / (Float64)framesPerPacket);
+ } else {
+ SInt64 numPackets = GetNumPackets();
+ SInt64 numBytes = GetNumBytes();
+ SInt64 numFrames = 0;
+
+ if (framesPerPacket) {
+ numFrames = numPackets * framesPerPacket;
+ } else {
+ // count frames
+ CompressedPacketTable* packetTable = GetPacketTable();
+ if (packetTable) {
+ if (packetTable->size() != numPackets) {
+ return kAudioFileInvalidFileError;
+ }
+#if !TARGET_OS_WIN32
+ for (ssize_t i = 0; i < numPackets; i++)
+#else
+ for (int i = 0; i < numPackets; i++)
+#endif
+ {
+ numFrames += (*packetTable)[i].mVariableFramesInPacket;
+ }
+ } else {
+ return kAudioFileUnsupportedPropertyError;
+ }
+ }
+
+ if (numFrames == 0 || (sampleRate == 0.)) {
+ *outBitRate = 0;
+ return noErr;
+ }
+ Float64 duration = (Float64)numFrames / sampleRate;
+ *outBitRate = (UInt32)(bitsPerByte * (Float64)numBytes / duration);
+ }
+
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetMagicCookieDataSize(
+ UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ if (outDataSize) *outDataSize = 0;
+ if (isWritable) *isWritable = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetMagicCookieData(
+ UInt32 *ioDataSize,
+ void *ioPropertyData)
+{
+ *ioDataSize = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetMagicCookieData( UInt32 /*inDataSize*/,
+ const void *inPropertyData)
+{
+ return kAudioFileInvalidChunkError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetMarkerListSize(
+ UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ if (outDataSize) *outDataSize = 0;
+ if (isWritable) *isWritable = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetMarkerList(
+ UInt32 *ioDataSize,
+ AudioFileMarkerList* /*ioPropertyData*/)
+{
+ *ioDataSize = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetMarkerList( UInt32 /*inDataSize*/,
+ const AudioFileMarkerList* /*inPropertyData*/)
+{
+ return kAudioFileUnsupportedPropertyError;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetRegionListSize(
+ UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ if (outDataSize) *outDataSize = 0;
+ if (isWritable) *isWritable = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetRegionList(
+ UInt32 *ioDataSize,
+ AudioFileRegionList *ioPropertyData)
+{
+ *ioDataSize = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetRegionList( UInt32 /*inDataSize*/,
+ const AudioFileRegionList* /*inPropertyData*/)
+{
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetChannelLayoutSize(
+ UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ if (outDataSize) *outDataSize = 0;
+ if (isWritable) *isWritable = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetChannelLayout(
+ UInt32 *ioDataSize,
+ AudioChannelLayout* /*ioPropertyData*/)
+{
+ *ioDataSize = 0;
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetChannelLayout( UInt32 /*inDataSize*/,
+ const AudioChannelLayout* /*inPropertyData*/)
+{
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetInfoDictionarySize( UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ if (outDataSize) *outDataSize = sizeof(CFDictionaryRef);
+ if (isWritable) *isWritable = 0;
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetInfoDictionary(CACFDictionary *infoDict)
+{
+ Float64 fl;
+ if (GetEstimatedDuration(&fl) == noErr)
+ return AddDurationToInfoDictionary(infoDict, fl);
+
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetInfoDictionary(CACFDictionary *infoDict)
+{
+ return kAudioFileUnsupportedPropertyError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+OSStatus AudioFileObject::GetEstimatedDuration(Float64* duration)
+{
+ // calculate duration
+ AudioStreamBasicDescription ASBD = GetDataFormat();
+
+ *duration = (ASBD.mFramesPerPacket != 0) ? (GetNumPackets() * ASBD.mFramesPerPacket) / ASBD.mSampleRate : 0.0;
+
+ /*
+ For now, assume that any ASBD that has zero in the frames per packet field has been subclassed for this
+ method. i.e. A CAF file has a frame count in one of it's chunks.
+
+ MP3 has been subclassed because it guesstimates a duration so the entire file does not
+ need to be parsed in order to calculate the total frames.
+ */
+
+ return noErr;
+
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetPropertyInfo (
+ AudioFilePropertyID inPropertyID,
+ UInt32 *outDataSize,
+ UInt32 *isWritable)
+{
+ OSStatus err = noErr;
+ UInt32 writable = 0;
+
+ switch (inPropertyID)
+ {
+ case kAudioFilePropertyDeferSizeUpdates :
+ if (outDataSize) *outDataSize = sizeof(UInt32);
+ writable = 1;
+ break;
+
+ case kAudioFilePropertyFileFormat:
+ if (outDataSize) *outDataSize = sizeof(UInt32);
+ writable = 0;
+ break;
+
+ case kAudioFilePropertyDataFormat:
+ if (outDataSize) *outDataSize = sizeof(AudioStreamBasicDescription);
+ writable = 1;
+ break;
+
+ case kAudioFilePropertyFormatList:
+ err = GetFormatListInfo(*outDataSize, writable);
+ break;
+
+ case kAudioFilePropertyPacketSizeUpperBound:
+ case kAudioFilePropertyIsOptimized:
+ case kAudioFilePropertyMaximumPacketSize:
+ if (outDataSize) *outDataSize = sizeof(UInt32);
+ writable = 0;
+ break;
+
+ case kAudioFilePropertyAudioDataByteCount:
+ case kAudioFilePropertyAudioDataPacketCount:
+ writable = 1;
+ if (outDataSize) *outDataSize = sizeof(SInt64);
+ break;
+
+ case kAudioFilePropertyDataOffset:
+ writable = 0;
+ if (outDataSize) *outDataSize = sizeof(SInt64);
+ break;
+
+ case kAudioFilePropertyBitRate:
+ writable = 0;
+ if (outDataSize) *outDataSize = sizeof(UInt32);
+ break;
+
+ case kAudioFilePropertyMagicCookieData:
+ err = GetMagicCookieDataSize(outDataSize, &writable);
+ break;
+
+ case kAudioFilePropertyMarkerList :
+ err = GetMarkerListSize(outDataSize, &writable);
+ break;
+
+ case kAudioFilePropertyRegionList :
+ err = GetRegionListSize(outDataSize, &writable);
+ break;
+
+ case kAudioFilePropertyChannelLayout :
+ err = GetChannelLayoutSize(outDataSize, &writable);
+ break;
+
+ case kAudioFilePropertyPacketToFrame :
+ case kAudioFilePropertyFrameToPacket :
+ if (outDataSize) *outDataSize = sizeof(AudioFramePacketTranslation);
+ writable = 0;
+ break;
+
+ case kAudioFilePropertyPacketToByte :
+ case kAudioFilePropertyByteToPacket :
+ if (outDataSize) *outDataSize = sizeof(AudioBytePacketTranslation);
+ writable = 0;
+ break;
+
+ case kAudioFilePropertyInfoDictionary :
+ err = GetInfoDictionarySize(outDataSize, &writable);
+ break;
+
+ case kTEMPAudioFilePropertySoundCheckDictionary :
+ err = GetSoundCheckDictionarySize(outDataSize, &writable);
+ break;
+
+ case kTEMPAudioFilePropertyGenerateLoudnessInfo :
+ err = GetLoudnessInfoSize(outDataSize, &writable);
+ writable = 0;
+ break;
+
+ case kTEMPAudioFilePropertyLoudnessInfo :
+ err = GetLoudnessInfoSize(outDataSize, &writable);
+ break;
+
+ case kAudioFilePropertyEstimatedDuration :
+ if (outDataSize) *outDataSize = sizeof(Float64);
+ writable = 0;
+ break;
+
+ case 'LYRC':
+ if (outDataSize) *outDataSize = sizeof(CFStringRef);
+ if (isWritable) *isWritable = 0;
+ break;
+
+ case 'eof?':
+ if (outDataSize) *outDataSize = sizeof(UInt32);
+ if (isWritable) *isWritable = 0;
+ break;
+
+ case 'sbtd' /*kAudioFilePropertySourceBitDepth*/ :
+ if (outDataSize) *outDataSize = sizeof(SInt32);
+ if (isWritable) *isWritable = CanWrite();
+ break;
+
+ default:
+ writable = 0;
+ err = kAudioFileUnsupportedPropertyError;
+ break;
+ }
+
+ if (isWritable)
+ *isWritable = writable;
+ return (err);
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetProperty(
+ AudioFilePropertyID inPropertyID,
+ UInt32 *ioDataSize,
+ void *ioPropertyData)
+{
+ OSStatus err = noErr;
+ UInt32 neededSize;
+ UInt32 writable;
+
+ switch (inPropertyID)
+ {
+ case kAudioFilePropertyFileFormat:
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ *(UInt32 *) ioPropertyData = GetFileType();
+ break;
+
+ case kAudioFilePropertyFormatList:
+ err = GetFormatList(*ioDataSize, (AudioFormatListItem*)ioPropertyData);
+ break;
+
+ case kAudioFilePropertyDataFormat:
+ FailWithAction(*ioDataSize != sizeof(AudioStreamBasicDescription),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ memcpy(ioPropertyData, &mDataFormat, sizeof(AudioStreamBasicDescription));
+ break;
+ case kAudioFilePropertyDataOffset:
+ FailWithAction(*ioDataSize != sizeof(SInt64),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(SInt64 *) ioPropertyData = mDataOffset;
+ break;
+ case kAudioFilePropertyIsOptimized:
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(UInt32 *) ioPropertyData = mIsOptimized;
+ break;
+
+ case kAudioFilePropertyAudioDataByteCount:
+ FailWithAction(*ioDataSize != sizeof(SInt64),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(SInt64 *)ioPropertyData = GetNumBytes();
+ break;
+
+ case kAudioFilePropertyAudioDataPacketCount:
+ FailWithAction(*ioDataSize != sizeof(SInt64),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(SInt64 *)ioPropertyData = GetNumPackets();
+ break;
+
+ case kAudioFilePropertyPacketSizeUpperBound:
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(UInt32 *)ioPropertyData = GetPacketSizeUpperBound();
+ break;
+
+ case kAudioFilePropertyMaximumPacketSize:
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ *(UInt32 *)ioPropertyData = FindMaximumPacketSize();
+ break;
+
+
+ case kAudioFilePropertyBitRate:
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ err = GetBitRate((UInt32*)ioPropertyData);
+ break;
+
+ case kAudioFilePropertyMagicCookieData:
+
+ err = GetMagicCookieData(ioDataSize, ioPropertyData);
+ break;
+
+ case kAudioFilePropertyMarkerList :
+ err = GetMarkerList(ioDataSize, static_cast<AudioFileMarkerList*>(ioPropertyData));
+ break;
+
+ case kAudioFilePropertyRegionList :
+ memset(ioPropertyData, 0, *ioDataSize);
+ err = GetRegionList(ioDataSize, static_cast<AudioFileRegionList*>(ioPropertyData));
+ break;
+
+ case kAudioFilePropertyChannelLayout :
+ err = GetChannelLayoutSize(&neededSize, &writable);
+ FailIf(err, Bail, "GetChannelLayoutSize failed");
+ FailWithAction(*ioDataSize != neededSize, err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ err = GetChannelLayout(ioDataSize, static_cast<AudioChannelLayout*>(ioPropertyData));
+ break;
+
+ case kAudioFilePropertyDeferSizeUpdates :
+ FailWithAction(*ioDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ *(UInt32 *) ioPropertyData = DeferSizeUpdates();
+ break;
+
+ case kAudioFilePropertyPacketToFrame :
+ {
+ FailWithAction(*ioDataSize != sizeof(AudioFramePacketTranslation),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ AudioFramePacketTranslation* afpt = (AudioFramePacketTranslation*)ioPropertyData;
+ err = PacketToFrame(afpt->mPacket, afpt->mFrame);
+ break;
+ }
+ case kAudioFilePropertyFrameToPacket :
+ {
+ FailWithAction(*ioDataSize != sizeof(AudioFramePacketTranslation),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ AudioFramePacketTranslation* afpt = (AudioFramePacketTranslation*)ioPropertyData;
+ err = FrameToPacket(afpt->mFrame, afpt->mPacket, afpt->mFrameOffsetInPacket);
+ break;
+ }
+
+ case kAudioFilePropertyPacketToByte :
+ {
+ FailWithAction(*ioDataSize != sizeof(AudioBytePacketTranslation),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ AudioBytePacketTranslation* abpt = (AudioBytePacketTranslation*)ioPropertyData;
+ err = PacketToByte(abpt);
+ break;
+ }
+ case kAudioFilePropertyByteToPacket :
+ {
+ FailWithAction(*ioDataSize != sizeof(AudioBytePacketTranslation),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ AudioBytePacketTranslation* abpt = (AudioBytePacketTranslation*)ioPropertyData;
+ err = ByteToPacket(abpt);
+ break;
+ }
+
+ case kAudioFilePropertyInfoDictionary :
+ {
+ FailWithAction(*ioDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ CACFDictionary afInfoDictionary(true);
+
+ err = GetInfoDictionary(&afInfoDictionary);
+
+ if (!err)
+ {
+ *(CFMutableDictionaryRef *)ioPropertyData = afInfoDictionary.CopyCFMutableDictionary();
+ }
+ break;
+ }
+
+ case kTEMPAudioFilePropertySoundCheckDictionary :
+ {
+ FailWithAction(*ioDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ CACFDictionary afInfoDictionary(true);
+
+ err = GetSoundCheckDictionary(&afInfoDictionary);
+ if (err) {
+ OSStatus err2 = GetSoundCheckDictionaryFromLoudnessInfo(&afInfoDictionary);
+ if (err2 == noErr) err = noErr; // else report original error from GetSoundCheckDictionary.
+ }
+
+ if (!err)
+ {
+ *(CFMutableDictionaryRef *)ioPropertyData = afInfoDictionary.CopyCFMutableDictionary();
+ }
+ break;
+ }
+
+ case kTEMPAudioFilePropertyLoudnessInfo :
+ {
+ FailWithAction(*ioDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ CACFDictionary afInfoDictionary(true);
+
+ err = GetLoudnessInfo(&afInfoDictionary);
+ if (err) {
+ OSStatus err2 = GetLoudnessInfoFromSoundCheckDictionary(&afInfoDictionary);
+ if (err2 == noErr) err = noErr; // else report original error from GetLoudnessInfo.
+ }
+
+ if (!err)
+ {
+ *(CFMutableDictionaryRef *)ioPropertyData = afInfoDictionary.CopyCFMutableDictionary();
+ }
+ break;
+ }
+
+ case kTEMPAudioFilePropertyGenerateLoudnessInfo :
+ {
+ FailWithAction(*ioDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ CACFDictionary afInfoDictionary(true);
+
+ err = GenerateLoudnessInfo(&afInfoDictionary);
+
+ if (!err)
+ {
+ *(CFMutableDictionaryRef *)ioPropertyData = afInfoDictionary.CopyCFMutableDictionary();
+ }
+ break;
+ }
+
+ case kAudioFilePropertyEstimatedDuration :
+ {
+ FailWithAction(*ioDataSize != sizeof(Float64),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ err = GetEstimatedDuration((Float64*)ioPropertyData);
+ break;
+ }
+
+ case 'LYRC' :
+ if (*ioDataSize < sizeof(CFStringRef))
+ return kAudioFileBadPropertySizeError;
+
+ *ioDataSize = sizeof(CFStringRef);
+ err = GetLyrics((CFStringRef*) ioPropertyData);
+ break;
+
+ case 'eof?' :
+ {
+ if (*ioDataSize != sizeof(UInt32))
+ return kAudioFileBadPropertySizeError;
+
+ SInt64 pos;
+ err = GetDataSource()->GetPos(pos);
+ if (err) break;
+
+ SInt64 endOfData = GetDataOffset() + GetNumBytes();
+ *(UInt32*)ioPropertyData = pos >= endOfData;
+
+ break;
+ }
+ case 'sbtd' /*kAudioFilePropertySourceBitDepth*/ :
+ {
+ if (*ioDataSize != sizeof(SInt32))
+ return kAudioFileBadPropertySizeError;
+
+ SInt32 outValue = 0;
+ err = GetSourceBitDepth(outValue);
+ if ((err || outValue == 0) && GetDataFormat().mFormatID == kAudioFormatLinearPCM) {
+ // if there was no stored source bit depth, and this file is LPCM, then report this file's bit depth.
+ err = noErr;
+ outValue = GetDataFormat().mBitsPerChannel;
+ if (GetDataFormat().mFormatFlags & kAudioFormatFlagIsFloat)
+ outValue = -outValue;
+ } else if (err)
+ break;
+
+ *(SInt32 *) ioPropertyData = outValue;
+
+ break;
+ }
+ default:
+ err = kAudioFileUnsupportedPropertyError;
+ break;
+ }
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetProperty(
+ AudioFilePropertyID inPropertyID,
+ UInt32 inDataSize,
+ const void *inPropertyData)
+{
+ OSStatus err = noErr;
+
+ switch (inPropertyID)
+ {
+ case kAudioFilePropertyDataFormat:
+ FailWithAction(inDataSize != sizeof(AudioStreamBasicDescription),
+ err = kAudioFileBadPropertySizeError, Bail, "Incorrect data size");
+ err = UpdateDataFormat((AudioStreamBasicDescription *) inPropertyData);
+ break;
+ case kAudioFilePropertyFormatList:
+ err = SetFormatList(inDataSize, (AudioFormatListItem*)inPropertyData);
+ break;
+
+ case kAudioFilePropertyAudioDataByteCount: {
+ FailWithAction(inDataSize != sizeof(SInt64), err = kAudioFileBadPropertySizeError, Bail, "Incorrect data size");
+ SInt64 numBytes = *(SInt64 *) inPropertyData;
+ if (numBytes > GetNumBytes()) {
+ // can't use this to increase data size.
+ return kAudioFileOperationNotSupportedError;
+ }
+ UInt32 saveDefer = DeferSizeUpdates();
+ SetDeferSizeUpdates(0); // force an update.
+ err = UpdateNumBytes(numBytes);
+ SetDeferSizeUpdates(saveDefer);
+ } break;
+
+ case kAudioFilePropertyAudioDataPacketCount: {
+ SInt64 numPackets = *(SInt64 *) inPropertyData;
+ if (numPackets > GetNumPackets()) {
+ // can't use this to increase data size.
+ return kAudioFileOperationNotSupportedError;
+ }
+ err = UpdateNumPackets(numPackets);
+ } break;
+
+ case kAudioFilePropertyMagicCookieData:
+ err = SetMagicCookieData(inDataSize, inPropertyData);
+ break;
+
+
+ case kAudioFilePropertyMarkerList :
+ err = SetMarkerList(inDataSize, static_cast<const AudioFileMarkerList*>(inPropertyData));
+ break;
+
+ case kAudioFilePropertyRegionList :
+ err = SetRegionList(inDataSize, static_cast<const AudioFileRegionList*>(inPropertyData));
+ break;
+
+ case kAudioFilePropertyChannelLayout :
+ err = SetChannelLayout(inDataSize, static_cast<const AudioChannelLayout*>(inPropertyData));
+ break;
+
+ case kAudioFilePropertyDeferSizeUpdates :
+ FailWithAction(inDataSize != sizeof(UInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+ SetDeferSizeUpdates(*(UInt32 *) inPropertyData);
+ break;
+
+ case kAudioFilePropertyInfoDictionary :
+ {
+ FailWithAction(inDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ // pass the SetInfoDictionary a CACFDictionary object made with the provided CFDictionaryRef
+ // Let the caller release their own CFObject so pass false for th erelease parameter
+ CACFDictionary afInfoDictionary(*(CFDictionaryRef *)inPropertyData, false);
+ err = SetInfoDictionary(&afInfoDictionary);
+
+ break;
+ }
+
+ case kTEMPAudioFilePropertySoundCheckDictionary :
+ {
+ FailWithAction(inDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ // pass the SetInfoDictionary a CACFDictionary object made with the provided CFDictionaryRef
+ // Let the caller release their own CFObject so pass false for the release parameter
+ CACFDictionary afInfoDictionary(*(CFDictionaryRef *)inPropertyData, false);
+ err = SetSoundCheckDictionary(&afInfoDictionary);
+
+ break;
+ }
+
+ case kTEMPAudioFilePropertyLoudnessInfo :
+ {
+ FailWithAction(inDataSize != sizeof(CFDictionaryRef),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ // pass the SetInfoDictionary a CACFDictionary object made with the provided CFDictionaryRef
+ // Let the caller release their own CFObject so pass false for the release parameter
+ CACFDictionary afInfoDictionary(*(CFDictionaryRef *)inPropertyData, false);
+ err = SetLoudnessInfo(&afInfoDictionary);
+
+ break;
+ }
+
+ case 'sbtd' /*kAudioFilePropertySourceBitDepth*/ :
+ {
+ FailWithAction(inDataSize != sizeof(SInt32),
+ err = kAudioFileBadPropertySizeError, Bail, "inDataSize is wrong");
+
+ SInt32 inValue = *(SInt32 *)inPropertyData;
+ err = SetSourceBitDepth(inValue);
+
+ break;
+ }
+
+ default:
+ err = kAudioFileUnsupportedPropertyError;
+ break;
+ }
+
+Bail:
+ return err;
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetDataFormat(const AudioStreamBasicDescription* inStreamFormat)
+{
+ OSStatus err = noErr;
+
+ if (!IsDataFormatValid(inStreamFormat))
+ return kAudioFileUnsupportedDataFormatError;
+
+ if (!IsDataFormatSupported(inStreamFormat))
+ return kAudioFileUnsupportedDataFormatError;
+
+ UInt32 prevBytesPerPacket = mDataFormat.mBytesPerPacket;
+
+ mDataFormat = *inStreamFormat;
+
+ // if CBR and bytes per packet changes, we need to change the number of packets we think we have.
+ if (mDataFormat.mBytesPerPacket && mDataFormat.mBytesPerPacket != prevBytesPerPacket)
+ {
+ SInt64 numPackets = GetNumBytes() / mDataFormat.mBytesPerPacket;
+ SetNumPackets(numPackets);
+ SetMaximumPacketSize(mDataFormat.mBytesPerPacket);
+
+ if (!mFirstSetFormat)
+ SizeChanged();
+ }
+
+ mFirstSetFormat = false;
+
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetFormatListInfo( UInt32 &outDataSize,
+ UInt32 &outWritable)
+{
+ // default implementation is to just return the data format
+ outDataSize = sizeof(AudioFormatListItem);
+ outWritable = false;
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::GetFormatList( UInt32 &ioDataSize,
+ AudioFormatListItem *ioPropertyData)
+{
+ // default implementation is to just return the data format
+ if (ioDataSize < sizeof(AudioFormatListItem))
+ return kAudioFileBadPropertySizeError;
+
+ AudioFormatListItem afli;
+ afli.mASBD = mDataFormat;
+ AudioChannelLayoutTag layoutTag = /*kAudioChannelLayoutTag_Unknown*/ 0xFFFF0000 | mDataFormat.mChannelsPerFrame;
+ UInt32 layoutSize, isWritable;
+ OSStatus err = GetChannelLayoutSize(&layoutSize, &isWritable);
+ if (err == noErr)
+ {
+ CAAutoFree<AudioChannelLayout> layout;
+ layout.allocBytes(layoutSize);
+ err = GetChannelLayout(&layoutSize, layout());
+ if (err == noErr) {
+ layoutTag = layout->mChannelLayoutTag;
+ }
+ }
+ afli.mChannelLayoutTag = layoutTag;
+
+ memcpy(ioPropertyData, &afli, sizeof(AudioFormatListItem));
+
+ ioDataSize = sizeof(AudioFormatListItem);
+ return noErr;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::SetFormatList( UInt32 inDataSize,
+ const AudioFormatListItem *inPropertyData)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+OSStatus AudioFileObject::UpdateDataFormat(const AudioStreamBasicDescription* inStreamFormat)
+{
+ return SetDataFormat(inStreamFormat);
+}
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Boolean AudioFileObject::IsDataFormatValid(AudioStreamBasicDescription const* inDesc)
+{
+ if (inDesc->mSampleRate < 0.)
+ return false;
+
+ if (inDesc->mSampleRate > 3e6)
+ return false;
+
+ if (inDesc->mChannelsPerFrame < 1 || inDesc->mChannelsPerFrame > 0x000FFFFF)
+ return false;
+
+ if (inDesc->mFormatID == kAudioFormatLinearPCM)
+ {
+ if (inDesc->mBitsPerChannel < 1 || inDesc->mBitsPerChannel > 64)
+ return false;
+
+ if (inDesc->mFramesPerPacket != 1)
+ return false;
+
+ if (inDesc->mBytesPerPacket == 0)
+ return false;
+
+ if (inDesc->mBytesPerFrame != inDesc->mBytesPerPacket)
+ return false;
+
+ // [3605260] we assume here that a packet is an integer number of frames.
+ UInt32 minimumBytesPerPacket = (inDesc->mBitsPerChannel * inDesc->mChannelsPerFrame + 7) / 8;
+ if (inDesc->mBytesPerPacket < minimumBytesPerPacket)
+ return false;
+ }
+ return true;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+void AudioFileObject::SetDataSource(DataSource* inDataSource)
+{
+ if (mDataSource != inDataSource) {
+ delete mDataSource;
+ mDataSource = inDataSource;
+ }
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+void AudioFileObject::SetURL (CFURLRef inURL)
+{
+ if (mFileRef == inURL) return;
+ if (inURL) CFRetain (inURL);
+ if (mFileRef) CFRelease(mFileRef);
+ mFileRef = inURL;
+}
+
+OSStatus AudioFileObject::OpenFile(SInt8 inPermissions, int inFD)
+{
+ OSStatus err = noErr;
+
+ SetDataSource(new Cached_DataSource(new UnixFile_DataSource(inFD, inPermissions, true)));
+
+ mFileD = inFD;
+ SetPermissions (inPermissions);
+
+ return err;
+}
+
+OSStatus AudioFileObject::CreateDataFile (CFURLRef inFileRef, int &outFileD)
+{
+ UInt8 fPath[FILENAME_MAX];
+ if (!CFURLGetFileSystemRepresentation (inFileRef, true, fPath, FILENAME_MAX))
+ return kAudio_FileNotFoundError;
+
+ struct stat stbuf;
+ if (stat ((const char*)fPath, &stbuf) == 0)
+ return kAudioFilePermissionsError;
+
+#if TARGET_OS_WIN32
+ int filePerms = S_IREAD | S_IWRITE;
+ int flags = O_CREAT | O_EXCL | O_RDWR | O_BINARY;
+#else
+ mode_t filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ int flags = O_CREAT | O_EXCL | O_RDWR;
+#endif
+ outFileD = open((const char*)fPath, flags, filePerms);
+ if (outFileD < 0)
+ return AudioFileTranslateErrno(errno);
+
+ return noErr;
+}
+
+OSStatus AudioFileObject::AddDurationToInfoDictionary(CACFDictionary *infoDict, Float64 &inDuration)
+{
+#if !TARGET_OS_WIN32
+ CFLocaleRef currentLocale = CFLocaleGetSystem();
+ CFNumberFormatterRef numberFormatter = NULL;
+ numberFormatter = CFNumberFormatterCreate(kCFAllocatorDefault, currentLocale, kCFNumberFormatterDecimalStyle);
+ CFStringRef cfStr = CFNumberFormatterCreateStringWithValue( kCFAllocatorDefault, numberFormatter, kCFNumberFloat64Type, &inDuration);
+ if (cfStr)
+ {
+ if (CFStringGetLength(cfStr) != 0)
+ infoDict->AddString(CFSTR(kAFInfoDictionary_ApproximateDurationInSeconds), cfStr);
+ CFRelease(cfStr);
+ }
+ CFRelease(numberFormatter);
+#endif
+ return noErr;
+}
+
+OSStatus AudioFileObject::SizeChanged()
+{
+ OSStatus err = noErr;
+ if (mPermissions & kAudioFileWritePermission)
+ {
+ if (DeferSizeUpdates())
+ SetNeedsSizeUpdate(true);
+ else
+ err = UpdateSize();
+ }
+ return err;
+}
+
+OSStatus AudioFileObject::UpdateSizeIfNeeded()
+{
+ if (GetNeedsSizeUpdate())
+ {
+ OSStatus err = UpdateSize();
+ if (err) return err;
+ SetNeedsSizeUpdate(false);
+ }
+ return noErr;
+}
+
+OSStatus AudioFileObject::CountUserData( UInt32 /*inUserDataID*/,
+ UInt32* /*outNumberItems*/)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+OSStatus AudioFileObject::GetUserDataSize( UInt32 /*inUserDataID*/,
+ UInt32 /*inIndex*/,
+ UInt32* /*outDataSize*/)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+OSStatus AudioFileObject::GetUserData( UInt32 /*inUserDataID*/,
+ UInt32 /*inIndex*/,
+ UInt32* /*ioDataSize*/,
+ void* /*ioUserData*/)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+OSStatus AudioFileObject::SetUserData( UInt32 /*inUserDataID*/,
+ UInt32 /*inIndex*/,
+ UInt32 /*inDataSize*/,
+ const void* /*inUserData*/)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+OSStatus AudioFileObject::RemoveUserData( UInt32 /*inUserDataID*/,
+ UInt32 /*inIndex*/)
+{
+ return kAudioFileOperationNotSupportedError;
+}
+
+OSStatus AudioFileObject::MoveData(SInt64 fromPos, SInt64 toPos, SInt64 size)
+{
+ if (fromPos == toPos)
+ return noErr;
+
+ OSStatus err = noErr;
+ CAAutoFree<char> audioData(kCopySoundDataBufferSize, true);
+
+ SInt64 bytesRemaining = size;
+ if (fromPos < toPos) {
+ while (bytesRemaining > 0)
+ {
+ // read from old file
+ UInt32 byteCount;
+ SInt64 count = (bytesRemaining < kCopySoundDataBufferSize) ? bytesRemaining : kCopySoundDataBufferSize;
+ err = GetDataSource()->ReadBytes(SEEK_SET, fromPos+(bytesRemaining-count), (UInt32)count, audioData(), &byteCount);
+ FailIf (err != noErr, Bail, "MoveData ReadBytes failed");
+
+ err = GetDataSource()->WriteBytes(SEEK_SET, toPos+(bytesRemaining-count), (UInt32)count, audioData(), &byteCount);
+ FailIf (err != noErr, Bail, "WriteBytes failed");
+
+ bytesRemaining -= count;
+ }
+ } else {
+ while (bytesRemaining > 0)
+ {
+ // read from old file
+ UInt32 byteCount;
+ SInt64 count = (bytesRemaining < kCopySoundDataBufferSize) ? bytesRemaining : kCopySoundDataBufferSize;
+ err = GetDataSource()->ReadBytes(SEEK_SET, fromPos+(size - bytesRemaining), (UInt32)count, audioData(), &byteCount);
+ FailIf (err != noErr, Bail, "MoveData ReadBytes failed");
+
+ err = GetDataSource()->WriteBytes(SEEK_SET, toPos+(size - bytesRemaining), (UInt32)count, audioData(), &byteCount);
+ FailIf (err != noErr, Bail, "WriteBytes failed");
+
+ bytesRemaining -= count;
+ }
+ }
+
+Bail:
+ return err;
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+