diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2015-10-17 20:46:58 -0400 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2015-10-18 23:03:13 +0200 |
commit | 66704eefcbe132eac0415434340f788808c40302 (patch) | |
tree | 2d4b596265f2fd801244862cef8bb79a63eea289 /libs/appleutility/CoreAudio/AudioFile/AFPublic/AudioFileObject.cpp | |
parent | f7e3117c3b3f09cc10cb10434660accf4ef49fc8 (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.cpp | 1966 |
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; +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + |