diff options
Diffstat (limited to 'libs/appleutility/CoreAudio105/CAAudioFile.h')
-rw-r--r-- | libs/appleutility/CoreAudio105/CAAudioFile.h | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/libs/appleutility/CoreAudio105/CAAudioFile.h b/libs/appleutility/CoreAudio105/CAAudioFile.h new file mode 100644 index 0000000000..8dd1d8690b --- /dev/null +++ b/libs/appleutility/CoreAudio105/CAAudioFile.h @@ -0,0 +1,442 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAudioFile.h + +=============================================================================*/ + +#ifndef __CAAudioFile_h__ +#define __CAAudioFile_h__ + +#include <iostream> +#include <AvailabilityMacros.h> + +#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioToolbox/AudioToolbox.h> +#else + #include <AudioToolbox.h> +#endif + +#include "CAStreamBasicDescription.h" +#include "CABufferList.h" +#include "CAAudioChannelLayout.h" +#include "CAXException.h" +#include "CAMath.h" + +#ifndef CAAF_USE_EXTAUDIOFILE +// option: use AudioToolbox/ExtAudioFile.h? Only available on Tiger. + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3 + // we are building software that must be deployable on Panther or earlier + #define CAAF_USE_EXTAUDIOFILE 0 + #else + // else we require Tiger and can use the API + #define CAAF_USE_EXTAUDIOFILE 1 + #endif +#endif + +#ifndef MAC_OS_X_VERSION_10_4 + // we have pre-Tiger headers; add our own declarations + typedef UInt32 AudioFileTypeID; + enum { + kExtAudioFileError_InvalidProperty = -66561, + kExtAudioFileError_InvalidPropertySize = -66562, + kExtAudioFileError_NonPCMClientFormat = -66563, + kExtAudioFileError_InvalidChannelMap = -66564, // number of channels doesn't match format + kExtAudioFileError_InvalidOperationOrder = -66565, + kExtAudioFileError_InvalidDataFormat = -66566, + kExtAudioFileError_MaxPacketSizeUnknown = -66567, + kExtAudioFileError_InvalidSeek = -66568, // writing, or offset out of bounds + kExtAudioFileError_AsyncWriteTooLarge = -66569, + kExtAudioFileError_AsyncWriteBufferOverflow = -66570 // an async write could not be completed in time + }; +#else + #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__) + #include <AudioToolbox/ExtendedAudioFile.h> + #else + #include "ExtendedAudioFile.h" + #endif +#endif + +// _______________________________________________________________________________________ +// Wrapper class for an AudioFile, supporting encode/decode to/from a PCM client format +class CAAudioFile { +public: + // implementation-independent helpers + void Open(const char *filePath) { + FSRef fsref; + std::cerr << "Opening " << filePath << std::endl; + XThrowIfError(FSPathMakeRef((UInt8 *)filePath, &fsref, NULL), "locate audio file"); + Open(fsref); + } + + bool HasConverter() const { return GetConverter() != NULL; } + + double GetDurationSeconds() { + double sr = GetFileDataFormat().mSampleRate; + return fnonzero(sr) ? GetNumberFrames() / sr : 0.; + } + // will be 0 if the file's frames/packet is 0 (variable) + // or the file's sample rate is 0 (unknown) + +#if CAAF_USE_EXTAUDIOFILE +public: + CAAudioFile() : mExtAF(NULL) { std::cerr << "Constructing CAAudioFile\n"; } + virtual ~CAAudioFile() { std::cerr << "Destroying CAAudiofile @ " << this << std::endl; if (mExtAF) Close(); } + + void Open(const FSRef &fsref) { + // open an existing file + XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed"); + } + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) { + XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed"); + } + + void Wrap(AudioFileID fileID, bool forWriting) { + // use this to wrap an AudioFileID opened externally + XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed"); + } + + void Close() { + std::cerr << "\tdisposeo of ext audio file @ " << mExtAF << std::endl; + XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed"); + mExtAF = NULL; + } + + const CAStreamBasicDescription &GetFileDataFormat() { + UInt32 size = sizeof(mFileDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format"); + return mFileDataFormat; + } + + const CAAudioChannelLayout & GetFileChannelLayout() { + return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout); + } + + void SetFileChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout"); + mFileChannelLayout = layout; + } + + const CAStreamBasicDescription &GetClientDataFormat() { + UInt32 size = sizeof(mClientDataFormat); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format"); + return mClientDataFormat; + } + + const CAAudioChannelLayout & GetClientChannelLayout() { + return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout); + } + + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format"); + if (layout) + SetClientChannelLayout(*layout); + } + + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout"); + } + + AudioConverterRef GetConverter() const { + UInt32 size = sizeof(AudioConverterRef); + AudioConverterRef converter; + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter"); + return converter; + } + + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false) + { + OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData); + if (!inCanFail) + XThrowIfError(err, "Couldn't set audio converter property"); + if (!err) { + // must tell the file that we have changed the converter; a NULL converter config is sufficient + CFPropertyListRef config = NULL; + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed"); + } + return err; + } + + SInt64 GetNumberFrames() { + SInt64 length; + UInt32 size = sizeof(SInt64); + XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length"); + return length; + } + + void SetNumberFrames(SInt64 length) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length"); + } + + void Seek(SInt64 pos) { + XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file"); + } + + SInt64 Tell() { + SInt64 pos; + XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark"); + return pos; + } + + void Read(UInt32 &ioFrames, AudioBufferList *ioData) { + XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file"); + } + + void Write(UInt32 inFrames, const AudioBufferList *inData) { + XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file"); + } + + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { + XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size"); + } + +private: + const CAAudioChannelLayout & FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) { + UInt32 size; + XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout"); + AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size); + OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout); + if (err) { + free(layout); + XThrowIfError(err, "Couldn't get channel layout"); + } + layoutObj = layout; + free(layout); + return layoutObj; + } + + +private: + ExtAudioFileRef mExtAF; + + CAStreamBasicDescription mFileDataFormat; + CAAudioChannelLayout mFileChannelLayout; + + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mClientChannelLayout; +#endif + +#if !CAAF_USE_EXTAUDIOFILE + CAAudioFile(); + virtual ~CAAudioFile(); + + // --- second-stage initializers --- + // Use exactly one of the following: + // - Open + // - PrepareNew followed by Create + // - Wrap + + void Open(const FSRef &fsref); + // open an existing file + + void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL); + + void Wrap(AudioFileID fileID, bool forWriting); + // use this to wrap an AudioFileID opened externally + + // --- + + void Close(); + // In case you want to close the file before the destructor executes + + // --- Data formats --- + + // Allow specifying the file's channel layout. Must be called before SetClientFormat. + // When writing, the specified channel layout is written to the file (if the file format supports + // the channel layout). When reading, the specified layout overrides the one read from the file, + // if any. + void SetFileChannelLayout(const CAAudioChannelLayout &layout); + + // This specifies the data format which the client will use for reading/writing the file, + // which may be different from the file's format. An AudioConverter is created if necessary. + // The client format must be linear PCM. + void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL); + void SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); } + void SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); } + + // Wrapping the underlying converter, if there is one + OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, + UInt32 inPropertyDataSize, + const void * inPropertyData, + bool inCanFail = false); + void SetConverterConfig(CFArrayRef config) { + SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); } + CFArrayRef GetConverterConfig(); + + // --- I/O --- + // All I/O is sequential, but you can seek to an arbitrary position when reading. + // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's. + // However, ReadPackets/WritePackets use packet counts in the client data format. + + void Read(UInt32 &ioNumFrames, AudioBufferList *ioData); + void Write(UInt32 numFrames, const AudioBufferList *data); + + // These can fail for files without a constant mFramesPerPacket + void Seek(SInt64 frameNumber); + SInt64 Tell() const; // frameNumber + + // --- Accessors --- + // note: client parameters only valid if SetClientFormat has been called + AudioFileID GetAudioFileID() const { return mAudioFile; } + const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; } + const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; } + const CAAudioChannelLayout & GetFileChannelLayout() const { return mFileChannelLayout; } + const CAAudioChannelLayout & GetClientChannelLayout() const { return mClientChannelLayout; } + AudioConverterRef GetConverter() const { return mConverter; } + + UInt32 GetFileMaxPacketSize() const { return mFileMaxPacketSize; } + UInt32 GetClientMaxPacketSize() const { return mClientMaxPacketSize; } + SInt64 GetNumberPackets() const { + SInt64 npackets; + UInt32 propertySize = sizeof(npackets); + XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count"); + return npackets; + } + SInt64 GetNumberFrames() const; + // will be 0 if the file's frames/packet is 0 (variable) + void SetNumberFrames(SInt64 length); // should only be set on a PCM file + + // --- Tunable performance parameters --- + void SetUseCache(bool b) { mUseCache = b; } + void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; } + UInt32 GetIOBufferSizeBytes() { return mIOBufferSizeBytes; } + void * GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; } + void SetIOBuffer(void *buf); + + // -- Profiling --- +#if CAAUDIOFILE_PROFILE + void EnableProfiling(bool b) { mProfiling = b; } + UInt64 TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; } + UInt64 TicksInIO() const { return mTicksInIO; } +#endif + +// _______________________________________________________________________________________ +private: + SInt64 FileDataOffset(); + void SeekToPacket(SInt64 packetNumber); + SInt64 TellPacket() const { return mPacketMark; } // will be imprecise if SeekToFrame was called + + void SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout); + void WritePacketsFromCallback( + AudioConverterComplexInputDataProc inInputDataProc, + void * inInputDataProcUserData); + // will use I/O buffer size + void InitFileMaxPacketSize(); + void FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0); + + void GetExistingFileInfo(); + void FlushEncoder(); + void CloseConverter(); + void UpdateClientMaxPacketSize(); + void AllocateBuffers(bool okToFail=false); + SInt64 PacketToFrame(SInt64 packet) const; + SInt64 FrameToPacket(SInt64 inFrame) const; + + static OSStatus ReadInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); + + static OSStatus WriteInputProc( AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData); +// _______________________________________________________________________________________ +private: + + // the file + FSRef mFSRef; + AudioFileID mAudioFile; + bool mOwnOpenFile; + bool mUseCache; + bool mFinishingEncoding; + enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode; + +// SInt64 mNumberPackets; // in file's format + SInt64 mFileDataOffset; + SInt64 mPacketMark; // in file's format + SInt64 mFrameMark; // this may be offset from the start of the file + // by the codec's latency; i.e. our frame 0 could + // lie at frame 2112 of a decoded AAC file + SInt32 mFrame0Offset; + UInt32 mFramesToSkipFollowingSeek; + + // buffers + UInt32 mIOBufferSizeBytes; + UInt32 mIOBufferSizePackets; + AudioBufferList mIOBufferList; // only one buffer -- USE ACCESSOR so it can be lazily initialized + bool mClientOwnsIOBuffer; + AudioStreamPacketDescription *mPacketDescs; + UInt32 mNumPacketDescs; + + // formats/conversion + AudioConverterRef mConverter; + CAStreamBasicDescription mFileDataFormat; + CAStreamBasicDescription mClientDataFormat; + CAAudioChannelLayout mFileChannelLayout; + CAAudioChannelLayout mClientChannelLayout; + UInt32 mFileMaxPacketSize; + UInt32 mClientMaxPacketSize; + + // cookie + Byte * mMagicCookie; + UInt32 mMagicCookieSize; + + // for ReadPackets + UInt32 mMaxPacketsToRead; + + // for WritePackets + UInt32 mWritePackets; + CABufferList * mWriteBufferList; + +#if CAAUDIOFILE_PROFILE + // performance + bool mProfiling; + UInt64 mTicksInConverter; + UInt64 mTicksInReadInConverter; + UInt64 mTicksInIO; + bool mInConverter; +#endif + +#endif // CAAF_USE_EXTAUDIOFILE +}; + +#endif // __CAAudioFile_h__ |