ardour/libs/appleutility/CoreAudio105/CAAudioFile.h

443 lines
18 KiB
C++

/* 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__