EXPERIMENTAL! NEEDS TESTING! remove "offset" from almost everything in the process...
[ardour2.git] / libs / appleutility / CAAudioFile.h
blobce22bfe466c85234ece0e95552fa0851b2bb98bf
1 /* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved.
3 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
4 ("Apple") in consideration of your agreement to the following terms, and your
5 use, installation, modification or redistribution of this Apple software
6 constitutes acceptance of these terms. If you do not agree with these terms,
7 please do not use, install, modify or redistribute this Apple software.
9 In consideration of your agreement to abide by the following terms, and subject
10 to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
11 copyrights in this original Apple software (the "Apple Software"), to use,
12 reproduce, modify and redistribute the Apple Software, with or without
13 modifications, in source and/or binary forms; provided that if you redistribute
14 the Apple Software in its entirety and without modifications, you must retain
15 this notice and the following text and disclaimers in all such redistributions of
16 the Apple Software. Neither the name, trademarks, service marks or logos of
17 Apple Computer, Inc. may be used to endorse or promote products derived from the
18 Apple Software without specific prior written permission from Apple. Except as
19 expressly stated in this notice, no other rights or licenses, express or implied,
20 are granted by Apple herein, including but not limited to any patent rights that
21 may be infringed by your derivative works or by other works in which the Apple
22 Software may be incorporated.
24 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
25 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
26 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
28 COMBINATION WITH YOUR PRODUCTS.
30 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
31 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
32 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
34 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
35 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
36 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /*=============================================================================
39 CAAudioFile.h
41 =============================================================================*/
43 #ifndef __CAAudioFile_h__
44 #define __CAAudioFile_h__
46 #include <iostream>
47 #include <AvailabilityMacros.h>
49 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
50 #include <AudioToolbox/AudioToolbox.h>
51 #else
52 #include <AudioToolbox.h>
53 #endif
55 #include "CAStreamBasicDescription.h"
56 #include "CABufferList.h"
57 #include "CAAudioChannelLayout.h"
58 #include "CAXException.h"
59 #include "CAMath.h"
61 #ifndef CAAF_USE_EXTAUDIOFILE
62 // option: use AudioToolbox/ExtAudioFile.h? Only available on Tiger.
63 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_3
64 // we are building software that must be deployable on Panther or earlier
65 #define CAAF_USE_EXTAUDIOFILE 0
66 #else
67 // else we require Tiger and can use the API
68 #define CAAF_USE_EXTAUDIOFILE 1
69 #endif
70 #endif
72 #ifndef MAC_OS_X_VERSION_10_4
73 // we have pre-Tiger headers; add our own declarations
74 typedef UInt32 AudioFileTypeID;
75 enum {
76 kExtAudioFileError_InvalidProperty = -66561,
77 kExtAudioFileError_InvalidPropertySize = -66562,
78 kExtAudioFileError_NonPCMClientFormat = -66563,
79 kExtAudioFileError_InvalidChannelMap = -66564, // number of channels doesn't match format
80 kExtAudioFileError_InvalidOperationOrder = -66565,
81 kExtAudioFileError_InvalidDataFormat = -66566,
82 kExtAudioFileError_MaxPacketSizeUnknown = -66567,
83 kExtAudioFileError_InvalidSeek = -66568, // writing, or offset out of bounds
84 kExtAudioFileError_AsyncWriteTooLarge = -66569,
85 kExtAudioFileError_AsyncWriteBufferOverflow = -66570 // an async write could not be completed in time
87 #else
88 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
89 #include <AudioToolbox/ExtendedAudioFile.h>
90 #else
91 #include "ExtendedAudioFile.h"
92 #endif
93 #endif
95 // _______________________________________________________________________________________
96 // Wrapper class for an AudioFile, supporting encode/decode to/from a PCM client format
97 class CAAudioFile {
98 public:
99 // implementation-independent helpers
100 void Open(const char *filePath) {
101 FSRef fsref;
102 std::cerr << "Opening " << filePath << std::endl;
103 XThrowIfError(FSPathMakeRef((UInt8 *)filePath, &fsref, NULL), "locate audio file");
104 Open(fsref);
107 bool HasConverter() const { return GetConverter() != NULL; }
109 double GetDurationSeconds() {
110 double sr = GetFileDataFormat().mSampleRate;
111 return fnonzero(sr) ? GetNumberFrames() / sr : 0.;
113 // will be 0 if the file's frames/packet is 0 (variable)
114 // or the file's sample rate is 0 (unknown)
116 #if CAAF_USE_EXTAUDIOFILE
117 #warning HERE WE ARE
118 public:
119 CAAudioFile() : mExtAF(NULL) { std::cerr << "Constructing CAAudioFile\n"; }
120 virtual ~CAAudioFile() { std::cerr << "Destroying CAAudiofile @ " << this << std::endl; if (mExtAF) Close(); }
122 void Open(const FSRef &fsref) {
123 // open an existing file
124 XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed");
127 void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) {
128 XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed");
131 void Wrap(AudioFileID fileID, bool forWriting) {
132 // use this to wrap an AudioFileID opened externally
133 XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed");
136 void Close() {
137 std::cerr << "\tdisposeo of ext audio file @ " << mExtAF << std::endl;
138 XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed");
139 mExtAF = NULL;
142 const CAStreamBasicDescription &GetFileDataFormat() {
143 UInt32 size = sizeof(mFileDataFormat);
144 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format");
145 return mFileDataFormat;
148 const CAAudioChannelLayout & GetFileChannelLayout() {
149 return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout);
152 void SetFileChannelLayout(const CAAudioChannelLayout &layout) {
153 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout");
154 mFileChannelLayout = layout;
157 const CAStreamBasicDescription &GetClientDataFormat() {
158 UInt32 size = sizeof(mClientDataFormat);
159 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format");
160 return mClientDataFormat;
163 const CAAudioChannelLayout & GetClientChannelLayout() {
164 return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout);
167 void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) {
168 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format");
169 if (layout)
170 SetClientChannelLayout(*layout);
173 void SetClientChannelLayout(const CAAudioChannelLayout &layout) {
174 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout");
177 AudioConverterRef GetConverter() const {
178 UInt32 size = sizeof(AudioConverterRef);
179 AudioConverterRef converter;
180 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter");
181 return converter;
184 OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID, UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false)
186 OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData);
187 if (!inCanFail)
188 XThrowIfError(err, "Couldn't set audio converter property");
189 if (!err) {
190 // must tell the file that we have changed the converter; a NULL converter config is sufficient
191 CFPropertyListRef config = NULL;
192 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed");
194 return err;
197 SInt64 GetNumberFrames() {
198 SInt64 length;
199 UInt32 size = sizeof(SInt64);
200 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length");
201 return length;
204 void SetNumberFrames(SInt64 length) {
205 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length");
208 void Seek(SInt64 pos) {
209 XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file");
212 SInt64 Tell() {
213 SInt64 pos;
214 XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark");
215 return pos;
218 void Read(UInt32 &ioFrames, AudioBufferList *ioData) {
219 XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file");
222 void Write(UInt32 inFrames, const AudioBufferList *inData) {
223 XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file");
226 void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) {
227 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size");
230 private:
231 const CAAudioChannelLayout & FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) {
232 UInt32 size;
233 XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout");
234 AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size);
235 OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout);
236 if (err) {
237 free(layout);
238 XThrowIfError(err, "Couldn't get channel layout");
240 layoutObj = layout;
241 free(layout);
242 return layoutObj;
246 private:
247 ExtAudioFileRef mExtAF;
249 CAStreamBasicDescription mFileDataFormat;
250 CAAudioChannelLayout mFileChannelLayout;
252 CAStreamBasicDescription mClientDataFormat;
253 CAAudioChannelLayout mClientChannelLayout;
254 #endif
256 #if !CAAF_USE_EXTAUDIOFILE
257 CAAudioFile();
258 virtual ~CAAudioFile();
260 // --- second-stage initializers ---
261 // Use exactly one of the following:
262 // - Open
263 // - PrepareNew followed by Create
264 // - Wrap
266 void Open(const FSRef &fsref);
267 // open an existing file
269 void CreateNew(const FSRef &inParentDir, CFStringRef inFileName, AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL);
271 void Wrap(AudioFileID fileID, bool forWriting);
272 // use this to wrap an AudioFileID opened externally
274 // ---
276 void Close();
277 // In case you want to close the file before the destructor executes
279 // --- Data formats ---
281 // Allow specifying the file's channel layout. Must be called before SetClientFormat.
282 // When writing, the specified channel layout is written to the file (if the file format supports
283 // the channel layout). When reading, the specified layout overrides the one read from the file,
284 // if any.
285 void SetFileChannelLayout(const CAAudioChannelLayout &layout);
287 // This specifies the data format which the client will use for reading/writing the file,
288 // which may be different from the file's format. An AudioConverter is created if necessary.
289 // The client format must be linear PCM.
290 void SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL);
291 void SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); }
292 void SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); }
294 // Wrapping the underlying converter, if there is one
295 OSStatus SetConverterProperty(AudioConverterPropertyID inPropertyID,
296 UInt32 inPropertyDataSize,
297 const void * inPropertyData,
298 bool inCanFail = false);
299 void SetConverterConfig(CFArrayRef config) {
300 SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); }
301 CFArrayRef GetConverterConfig();
303 // --- I/O ---
304 // All I/O is sequential, but you can seek to an arbitrary position when reading.
305 // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's.
306 // However, ReadPackets/WritePackets use packet counts in the client data format.
308 void Read(UInt32 &ioNumFrames, AudioBufferList *ioData);
309 void Write(UInt32 numFrames, const AudioBufferList *data);
311 // These can fail for files without a constant mFramesPerPacket
312 void Seek(SInt64 frameNumber);
313 SInt64 Tell() const; // frameNumber
315 // --- Accessors ---
316 // note: client parameters only valid if SetClientFormat has been called
317 AudioFileID GetAudioFileID() const { return mAudioFile; }
318 const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; }
319 const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; }
320 const CAAudioChannelLayout & GetFileChannelLayout() const { return mFileChannelLayout; }
321 const CAAudioChannelLayout & GetClientChannelLayout() const { return mClientChannelLayout; }
322 AudioConverterRef GetConverter() const { return mConverter; }
324 UInt32 GetFileMaxPacketSize() const { return mFileMaxPacketSize; }
325 UInt32 GetClientMaxPacketSize() const { return mClientMaxPacketSize; }
326 SInt64 GetNumberPackets() const {
327 SInt64 npackets;
328 UInt32 propertySize = sizeof(npackets);
329 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count");
330 return npackets;
332 SInt64 GetNumberFrames() const;
333 // will be 0 if the file's frames/packet is 0 (variable)
334 void SetNumberFrames(SInt64 length); // should only be set on a PCM file
336 // --- Tunable performance parameters ---
337 void SetUseCache(bool b) { mUseCache = b; }
338 void SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; }
339 UInt32 GetIOBufferSizeBytes() { return mIOBufferSizeBytes; }
340 void * GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; }
341 void SetIOBuffer(void *buf);
343 // -- Profiling ---
344 #if CAAUDIOFILE_PROFILE
345 void EnableProfiling(bool b) { mProfiling = b; }
346 UInt64 TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; }
347 UInt64 TicksInIO() const { return mTicksInIO; }
348 #endif
350 // _______________________________________________________________________________________
351 private:
352 SInt64 FileDataOffset();
353 void SeekToPacket(SInt64 packetNumber);
354 SInt64 TellPacket() const { return mPacketMark; } // will be imprecise if SeekToFrame was called
356 void SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout);
357 void WritePacketsFromCallback(
358 AudioConverterComplexInputDataProc inInputDataProc,
359 void * inInputDataProcUserData);
360 // will use I/O buffer size
361 void InitFileMaxPacketSize();
362 void FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0);
364 void GetExistingFileInfo();
365 void FlushEncoder();
366 void CloseConverter();
367 void UpdateClientMaxPacketSize();
368 void AllocateBuffers(bool okToFail=false);
369 SInt64 PacketToFrame(SInt64 packet) const;
370 SInt64 FrameToPacket(SInt64 inFrame) const;
372 static OSStatus ReadInputProc( AudioConverterRef inAudioConverter,
373 UInt32* ioNumberDataPackets,
374 AudioBufferList* ioData,
375 AudioStreamPacketDescription** outDataPacketDescription,
376 void* inUserData);
378 static OSStatus WriteInputProc( AudioConverterRef inAudioConverter,
379 UInt32* ioNumberDataPackets,
380 AudioBufferList* ioData,
381 AudioStreamPacketDescription** outDataPacketDescription,
382 void* inUserData);
383 // _______________________________________________________________________________________
384 private:
386 // the file
387 FSRef mFSRef;
388 AudioFileID mAudioFile;
389 bool mOwnOpenFile;
390 bool mUseCache;
391 bool mFinishingEncoding;
392 enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode;
394 // SInt64 mNumberPackets; // in file's format
395 SInt64 mFileDataOffset;
396 SInt64 mPacketMark; // in file's format
397 SInt64 mFrameMark; // this may be offset from the start of the file
398 // by the codec's latency; i.e. our frame 0 could
399 // lie at frame 2112 of a decoded AAC file
400 SInt32 mFrame0Offset;
401 UInt32 mFramesToSkipFollowingSeek;
403 // buffers
404 UInt32 mIOBufferSizeBytes;
405 UInt32 mIOBufferSizePackets;
406 AudioBufferList mIOBufferList; // only one buffer -- USE ACCESSOR so it can be lazily initialized
407 bool mClientOwnsIOBuffer;
408 AudioStreamPacketDescription *mPacketDescs;
409 UInt32 mNumPacketDescs;
411 // formats/conversion
412 AudioConverterRef mConverter;
413 CAStreamBasicDescription mFileDataFormat;
414 CAStreamBasicDescription mClientDataFormat;
415 CAAudioChannelLayout mFileChannelLayout;
416 CAAudioChannelLayout mClientChannelLayout;
417 UInt32 mFileMaxPacketSize;
418 UInt32 mClientMaxPacketSize;
420 // cookie
421 Byte * mMagicCookie;
422 UInt32 mMagicCookieSize;
424 // for ReadPackets
425 UInt32 mMaxPacketsToRead;
427 // for WritePackets
428 UInt32 mWritePackets;
429 CABufferList * mWriteBufferList;
431 #if CAAUDIOFILE_PROFILE
432 // performance
433 bool mProfiling;
434 UInt64 mTicksInConverter;
435 UInt64 mTicksInReadInConverter;
436 UInt64 mTicksInIO;
437 bool mInConverter;
438 #endif
440 #endif // CAAF_USE_EXTAUDIOFILE
443 #endif // __CAAudioFile_h__