Editor zooming: refactor zoom-limiting code into editor::session_gui_extents.
[ardour.git] / libs / appleutility / CoreAudio105 / CAAudioFile.h
1 /*      Copyright:      � Copyright 2005 Apple Computer, Inc. All rights reserved.
2
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.
8
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.
23
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.
29
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.
37 */
38 /*=============================================================================
39         CAAudioFile.h
40
41 =============================================================================*/
42
43 #ifndef __CAAudioFile_h__
44 #define __CAAudioFile_h__
45
46 #include <iostream>
47 #include <AvailabilityMacros.h>
48
49 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
50         #include <AudioToolbox/AudioToolbox.h>
51 #else
52         #include <AudioToolbox.h>
53 #endif
54
55 #include "CAStreamBasicDescription.h"
56 #include "CABufferList.h"
57 #include "CAAudioChannelLayout.h"
58 #include "CAXException.h"
59 #include "CAMath.h"
60
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
71
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
86         };
87 #else
88         #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
89                 #include <AudioToolbox/ExtendedAudioFile.h>
90         #else
91                 #include "ExtendedAudioFile.h"
92         #endif
93 #endif
94
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);
105         }
106
107         bool                                                    HasConverter() const { return GetConverter() != NULL; }
108
109         double  GetDurationSeconds() {
110                 double sr = GetFileDataFormat().mSampleRate;
111                 return fnonzero(sr) ? GetNumberFrames() / sr : 0.;
112         }
113                                 // will be 0 if the file's frames/packet is 0 (variable)
114                                 // or the file's sample rate is 0 (unknown)
115
116 #if CAAF_USE_EXTAUDIOFILE
117 public:
118         CAAudioFile() : mExtAF(NULL) { std::cerr << "Constructing CAAudioFile\n"; }
119         virtual ~CAAudioFile() { std::cerr << "Destroying CAAudiofile @ " << this << std::endl; if (mExtAF) Close(); }
120
121         void    Open(const FSRef &fsref) {
122                                 // open an existing file
123                 XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed");
124         }
125
126         void    CreateNew(const FSRef &inParentDir, CFStringRef inFileName,     AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL) {
127                 XThrowIfError(ExtAudioFileCreateNew(&inParentDir, inFileName, inFileType, &inStreamDesc, inChannelLayout, &mExtAF), "ExtAudioFileCreateNew failed");
128         }
129
130         void    Wrap(AudioFileID fileID, bool forWriting) {
131                                 // use this to wrap an AudioFileID opened externally
132                 XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed");
133         }
134
135         void    Close() {
136                 std::cerr << "\tdisposeo of ext audio file @ " << mExtAF << std::endl;
137                 XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed");
138                 mExtAF = NULL;
139         }
140
141         const CAStreamBasicDescription &GetFileDataFormat() {
142                 UInt32 size = sizeof(mFileDataFormat);
143                 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileDataFormat, &size, &mFileDataFormat), "Couldn't get file's data format");
144                 return mFileDataFormat;
145         }
146
147         const CAAudioChannelLayout &    GetFileChannelLayout() {
148                 return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout);
149         }
150
151         void    SetFileChannelLayout(const CAAudioChannelLayout &layout) {
152                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set file's channel layout");
153                 mFileChannelLayout = layout;
154         }
155
156         const CAStreamBasicDescription &GetClientDataFormat() {
157                 UInt32 size = sizeof(mClientDataFormat);
158                 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, &size, &mClientDataFormat), "Couldn't get client data format");
159                 return mClientDataFormat;
160         }
161
162         const CAAudioChannelLayout &    GetClientChannelLayout() {
163                 return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout);
164         }
165
166         void    SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL) {
167                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientDataFormat, sizeof(dataFormat), &dataFormat), "Couldn't set client format");
168                 if (layout)
169                         SetClientChannelLayout(*layout);
170         }
171
172         void    SetClientChannelLayout(const CAAudioChannelLayout &layout) {
173                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout");
174         }
175
176         AudioConverterRef                               GetConverter() const {
177                 UInt32 size = sizeof(AudioConverterRef);
178                 AudioConverterRef converter;
179                 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_AudioConverter, &size, &converter), "Couldn't get file's AudioConverter");
180                 return converter;
181         }
182
183         OSStatus        SetConverterProperty(AudioConverterPropertyID inPropertyID,     UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false)
184         {
185                 OSStatus err = AudioConverterSetProperty(GetConverter(), inPropertyID, inPropertyDataSize, inPropertyData);
186                 if (!inCanFail)
187                         XThrowIfError(err, "Couldn't set audio converter property");
188                 if (!err) {
189                         // must tell the file that we have changed the converter; a NULL converter config is sufficient
190                         CFPropertyListRef config = NULL;
191                         XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ConverterConfig, sizeof(CFPropertyListRef), &config), "couldn't signal the file that the converter has changed");
192                 }
193                 return err;
194         }
195
196         SInt64          GetNumberFrames() {
197                 SInt64 length;
198                 UInt32 size = sizeof(SInt64);
199                 XThrowIfError(ExtAudioFileGetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, &size, &length), "Couldn't get file's length");
200                 return length;
201         }
202
203         void            SetNumberFrames(SInt64 length) {
204                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length");
205         }
206
207         void            Seek(SInt64 pos) {
208                 XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file");
209         }
210
211         SInt64          Tell() {
212                 SInt64 pos;
213                 XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark");
214                 return pos;
215         }
216
217         void            Read(UInt32 &ioFrames, AudioBufferList *ioData) {
218                 XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file");
219         }
220
221         void            Write(UInt32 inFrames, const AudioBufferList *inData) {
222                 XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file");
223         }
224
225         void            SetIOBufferSizeBytes(UInt32 bufferSizeBytes) {
226                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size");
227         }
228
229 private:
230         const CAAudioChannelLayout &    FetchChannelLayout(CAAudioChannelLayout &layoutObj, ExtAudioFilePropertyID propID) {
231                 UInt32 size;
232                 XThrowIfError(ExtAudioFileGetPropertyInfo(mExtAF, propID, &size, NULL), "Couldn't get info about channel layout");
233                 AudioChannelLayout *layout = (AudioChannelLayout *)malloc(size);
234                 OSStatus err = ExtAudioFileGetProperty(mExtAF, propID, &size, layout);
235                 if (err) {
236                         free(layout);
237                         XThrowIfError(err, "Couldn't get channel layout");
238                 }
239                 layoutObj = layout;
240                 free(layout);
241                 return layoutObj;
242         }
243
244
245 private:
246         ExtAudioFileRef                         mExtAF;
247
248         CAStreamBasicDescription        mFileDataFormat;
249         CAAudioChannelLayout            mFileChannelLayout;
250
251         CAStreamBasicDescription        mClientDataFormat;
252         CAAudioChannelLayout            mClientChannelLayout;
253 #endif
254
255 #if !CAAF_USE_EXTAUDIOFILE
256         CAAudioFile();
257         virtual ~CAAudioFile();
258
259         // --- second-stage initializers ---
260         // Use exactly one of the following:
261         //              - Open
262         //              - PrepareNew followed by Create
263         //              - Wrap
264
265         void    Open(const FSRef &fsref);
266                                 // open an existing file
267
268         void    CreateNew(const FSRef &inParentDir, CFStringRef inFileName,     AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL);
269
270         void    Wrap(AudioFileID fileID, bool forWriting);
271                                 // use this to wrap an AudioFileID opened externally
272
273         // ---
274
275         void    Close();
276                                 // In case you want to close the file before the destructor executes
277
278         // --- Data formats ---
279
280         // Allow specifying the file's channel layout. Must be called before SetClientFormat.
281         // When writing, the specified channel layout is written to the file (if the file format supports
282         // the channel layout). When reading, the specified layout overrides the one read from the file,
283         // if any.
284         void    SetFileChannelLayout(const CAAudioChannelLayout &layout);
285
286         // This specifies the data format which the client will use for reading/writing the file,
287         // which may be different from the file's format. An AudioConverter is created if necessary.
288         // The client format must be linear PCM.
289         void    SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout=NULL);
290         void    SetClientDataFormat(const CAStreamBasicDescription &dataFormat) { SetClientFormat(dataFormat, NULL); }
291         void    SetClientChannelLayout(const CAAudioChannelLayout &layout) { SetClientFormat(mClientDataFormat, &layout); }
292
293         // Wrapping the underlying converter, if there is one
294         OSStatus        SetConverterProperty(AudioConverterPropertyID   inPropertyID,
295                                                                         UInt32                                          inPropertyDataSize,
296                                                                         const void *                            inPropertyData,
297                                                                         bool                                            inCanFail = false);
298         void            SetConverterConfig(CFArrayRef config) {
299                                         SetConverterProperty(kAudioConverterPropertySettings, sizeof(config), &config); }
300         CFArrayRef  GetConverterConfig();
301
302         // --- I/O ---
303         // All I/O is sequential, but you can seek to an arbitrary position when reading.
304         // SeekToPacket and TellPacket's packet numbers are in the file's data format, not the client's.
305         // However, ReadPackets/WritePackets use packet counts in the client data format.
306
307         void    Read(UInt32 &ioNumFrames, AudioBufferList *ioData);
308         void    Write(UInt32 numFrames, const AudioBufferList *data);
309
310         // These can fail for files without a constant mFramesPerPacket
311         void    Seek(SInt64 frameNumber);
312         SInt64  Tell() const;   // frameNumber
313
314         // --- Accessors ---
315         // note: client parameters only valid if SetClientFormat has been called
316         AudioFileID                                             GetAudioFileID() const { return mAudioFile; }
317         const CAStreamBasicDescription &GetFileDataFormat() const { return mFileDataFormat; }
318         const CAStreamBasicDescription &GetClientDataFormat() const { return mClientDataFormat; }
319         const CAAudioChannelLayout &    GetFileChannelLayout() const { return mFileChannelLayout; }
320         const CAAudioChannelLayout &    GetClientChannelLayout() const { return mClientChannelLayout; }
321         AudioConverterRef                               GetConverter() const { return mConverter; }
322
323         UInt32  GetFileMaxPacketSize() const { return mFileMaxPacketSize; }
324         UInt32  GetClientMaxPacketSize() const { return mClientMaxPacketSize; }
325         SInt64  GetNumberPackets() const {
326                 SInt64 npackets;
327                 UInt32 propertySize = sizeof(npackets);
328                 XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &npackets), "get audio file's packet count");
329                 return npackets;
330         }
331         SInt64  GetNumberFrames() const;
332                                 // will be 0 if the file's frames/packet is 0 (variable)
333         void    SetNumberFrames(SInt64 length); // should only be set on a PCM file
334
335         // --- Tunable performance parameters ---
336         void    SetUseCache(bool b) { mUseCache = b; }
337         void    SetIOBufferSizeBytes(UInt32 bufferSizeBytes) { mIOBufferSizeBytes = bufferSizeBytes; }
338         UInt32  GetIOBufferSizeBytes() { return mIOBufferSizeBytes; }
339         void *  GetIOBuffer() { return mIOBufferList.mBuffers[0].mData; }
340         void    SetIOBuffer(void *buf);
341
342         // -- Profiling ---
343 #if CAAUDIOFILE_PROFILE
344         void    EnableProfiling(bool b) { mProfiling = b; }
345         UInt64  TicksInConverter() const { return (mTicksInConverter > 0) ? (mTicksInConverter - mTicksInReadInConverter) : 0; }
346         UInt64  TicksInIO() const { return mTicksInIO; }
347 #endif
348
349 // _______________________________________________________________________________________
350 private:
351         SInt64  FileDataOffset();
352         void    SeekToPacket(SInt64 packetNumber);
353         SInt64  TellPacket() const { return mPacketMark; }  // will be imprecise if SeekToFrame was called
354
355         void    SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout);
356         void    WritePacketsFromCallback(
357                                                                         AudioConverterComplexInputDataProc      inInputDataProc,
358                                                                         void *                                                          inInputDataProcUserData);
359                                 // will use I/O buffer size
360         void    InitFileMaxPacketSize();
361         void    FileFormatChanged(const FSRef *parentDir=0, CFStringRef filename=0, AudioFileTypeID filetype=0);
362
363         void    GetExistingFileInfo();
364         void    FlushEncoder();
365         void    CloseConverter();
366         void    UpdateClientMaxPacketSize();
367         void    AllocateBuffers(bool okToFail=false);
368         SInt64  PacketToFrame(SInt64 packet) const;
369         SInt64  FrameToPacket(SInt64 inFrame) const;
370
371         static OSStatus ReadInputProc(          AudioConverterRef                               inAudioConverter,
372                                                                                 UInt32*                                                 ioNumberDataPackets,
373                                                                                 AudioBufferList*                                ioData,
374                                                                                 AudioStreamPacketDescription**  outDataPacketDescription,
375                                                                                 void*                                                   inUserData);
376
377         static OSStatus WriteInputProc(         AudioConverterRef                               inAudioConverter,
378                                                                                 UInt32*                                                 ioNumberDataPackets,
379                                                                                 AudioBufferList*                                ioData,
380                                                                                 AudioStreamPacketDescription**  outDataPacketDescription,
381                                                                                 void*                                                   inUserData);
382 // _______________________________________________________________________________________
383 private:
384
385         // the file
386         FSRef                                           mFSRef;
387         AudioFileID                                     mAudioFile;
388         bool                                            mOwnOpenFile;
389         bool                                            mUseCache;
390         bool                                            mFinishingEncoding;
391         enum { kClosed, kReading, kPreparingToCreate, kPreparingToWrite, kWriting } mMode;
392
393 //      SInt64                                          mNumberPackets;         // in file's format
394         SInt64                                          mFileDataOffset;
395         SInt64                                          mPacketMark;            // in file's format
396         SInt64                                          mFrameMark;                     // this may be offset from the start of the file
397                                                                                                         // by the codec's latency; i.e. our frame 0 could
398                                                                                                         // lie at frame 2112 of a decoded AAC file
399         SInt32                                          mFrame0Offset;
400         UInt32                                          mFramesToSkipFollowingSeek;
401
402         // buffers
403         UInt32                                          mIOBufferSizeBytes;
404         UInt32                                          mIOBufferSizePackets;
405         AudioBufferList                         mIOBufferList;          // only one buffer -- USE ACCESSOR so it can be lazily initialized
406         bool                                            mClientOwnsIOBuffer;
407         AudioStreamPacketDescription *mPacketDescs;
408         UInt32                                          mNumPacketDescs;
409
410         // formats/conversion
411         AudioConverterRef                       mConverter;
412         CAStreamBasicDescription        mFileDataFormat;
413         CAStreamBasicDescription        mClientDataFormat;
414         CAAudioChannelLayout            mFileChannelLayout;
415         CAAudioChannelLayout            mClientChannelLayout;
416         UInt32                                          mFileMaxPacketSize;
417         UInt32                                          mClientMaxPacketSize;
418
419         // cookie
420         Byte *                                          mMagicCookie;
421         UInt32                                          mMagicCookieSize;
422
423         // for ReadPackets
424         UInt32                                          mMaxPacketsToRead;
425
426         // for WritePackets
427         UInt32                                          mWritePackets;
428         CABufferList *                          mWriteBufferList;
429
430 #if CAAUDIOFILE_PROFILE
431         // performance
432         bool                                            mProfiling;
433         UInt64                                          mTicksInConverter;
434         UInt64                                          mTicksInReadInConverter;
435         UInt64                                          mTicksInIO;
436         bool                                            mInConverter;
437 #endif
438
439 #endif // CAAF_USE_EXTAUDIOFILE
440 };
441
442 #endif // __CAAudioFile_h__