speed up track resizing; fixup file DnD; stop use CAAudioFile for imported files
[ardour.git] / libs / appleutility / 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 #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(); }
121
122         void    Open(const FSRef &fsref) {
123                                 // open an existing file
124                 XThrowIfError(ExtAudioFileOpen(&fsref, &mExtAF), "ExtAudioFileOpen failed");
125         }
126         
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");
129         }
130
131         void    Wrap(AudioFileID fileID, bool forWriting) {
132                                 // use this to wrap an AudioFileID opened externally
133                 XThrowIfError(ExtAudioFileWrapAudioFileID(fileID, forWriting, &mExtAF), "ExtAudioFileWrapAudioFileID failed");
134         }
135         
136         void    Close() {
137                 std::cerr << "\tdisposeo of ext audio file @ " << mExtAF << std::endl;
138                 XThrowIfError(ExtAudioFileDispose(mExtAF), "ExtAudioFileClose failed");
139                 mExtAF = NULL;
140         }
141
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;
146         }
147         
148         const CAAudioChannelLayout &    GetFileChannelLayout() {
149                 return FetchChannelLayout(mFileChannelLayout, kExtAudioFileProperty_FileChannelLayout);
150         }
151         
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;
155         }
156
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;
161         }
162         
163         const CAAudioChannelLayout &    GetClientChannelLayout() {
164                 return FetchChannelLayout(mClientChannelLayout, kExtAudioFileProperty_ClientChannelLayout);
165         }
166         
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);
171         }
172         
173         void    SetClientChannelLayout(const CAAudioChannelLayout &layout) {
174                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_ClientChannelLayout, layout.Size(), &layout.Layout()), "Couldn't set client channel layout");
175         }
176         
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;
182         }
183
184         OSStatus        SetConverterProperty(AudioConverterPropertyID inPropertyID,     UInt32 inPropertyDataSize, const void *inPropertyData, bool inCanFail=false)
185         {
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");
193                 }
194                 return err;
195         }
196         
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;
202         }
203         
204         void            SetNumberFrames(SInt64 length) {
205                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_FileLengthFrames, sizeof(SInt64), &length), "Couldn't set file's length");
206         }
207         
208         void            Seek(SInt64 pos) {
209                 XThrowIfError(ExtAudioFileSeek(mExtAF, pos), "Couldn't seek in audio file");
210         }
211         
212         SInt64          Tell() {
213                 SInt64 pos;
214                 XThrowIfError(ExtAudioFileTell(mExtAF, &pos), "Couldn't get file's mark");
215                 return pos;
216         }
217         
218         void            Read(UInt32 &ioFrames, AudioBufferList *ioData) {
219                 XThrowIfError(ExtAudioFileRead(mExtAF, &ioFrames, ioData), "Couldn't read audio file");
220         }
221
222         void            Write(UInt32 inFrames, const AudioBufferList *inData) {
223                 XThrowIfError(ExtAudioFileWrite(mExtAF, inFrames, inData), "Couldn't write audio file");
224         }
225
226         void            SetIOBufferSizeBytes(UInt32 bufferSizeBytes) {
227                 XThrowIfError(ExtAudioFileSetProperty(mExtAF, kExtAudioFileProperty_IOBufferSizeBytes, sizeof(UInt32), &bufferSizeBytes), "Couldn't set audio file's I/O buffer size");
228         }
229
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");
239                 }
240                 layoutObj = layout;
241                 free(layout);
242                 return layoutObj;
243         }
244
245
246 private:
247         ExtAudioFileRef                         mExtAF;
248
249         CAStreamBasicDescription        mFileDataFormat;
250         CAAudioChannelLayout            mFileChannelLayout;
251
252         CAStreamBasicDescription        mClientDataFormat;
253         CAAudioChannelLayout            mClientChannelLayout;
254 #endif
255
256 #if !CAAF_USE_EXTAUDIOFILE
257         CAAudioFile();
258         virtual ~CAAudioFile();
259
260         // --- second-stage initializers ---
261         // Use exactly one of the following:
262         //              - Open
263         //              - PrepareNew followed by Create
264         //              - Wrap
265         
266         void    Open(const FSRef &fsref);
267                                 // open an existing file
268
269         void    CreateNew(const FSRef &inParentDir, CFStringRef inFileName,     AudioFileTypeID inFileType, const AudioStreamBasicDescription &inStreamDesc, const AudioChannelLayout *inChannelLayout=NULL);
270         
271         void    Wrap(AudioFileID fileID, bool forWriting);
272                                 // use this to wrap an AudioFileID opened externally
273
274         // ---
275
276         void    Close();
277                                 // In case you want to close the file before the destructor executes
278         
279         // --- Data formats ---
280
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);
286         
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); }
293         
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();
302         
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.
307
308         void    Read(UInt32 &ioNumFrames, AudioBufferList *ioData);
309         void    Write(UInt32 numFrames, const AudioBufferList *data);
310
311         // These can fail for files without a constant mFramesPerPacket
312         void    Seek(SInt64 frameNumber);
313         SInt64  Tell() const;   // frameNumber
314         
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; }
323
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;
331         }
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
335         
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);
342         
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
349         
350 // _______________________________________________________________________________________
351 private:
352         SInt64  FileDataOffset();
353         void    SeekToPacket(SInt64 packetNumber);
354         SInt64  TellPacket() const { return mPacketMark; }  // will be imprecise if SeekToFrame was called
355         
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);
363
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;
371
372         static OSStatus ReadInputProc(          AudioConverterRef                               inAudioConverter,
373                                                                                 UInt32*                                                 ioNumberDataPackets,
374                                                                                 AudioBufferList*                                ioData,
375                                                                                 AudioStreamPacketDescription**  outDataPacketDescription,
376                                                                                 void*                                                   inUserData);    
377
378         static OSStatus WriteInputProc(         AudioConverterRef                               inAudioConverter,
379                                                                                 UInt32*                                                 ioNumberDataPackets,
380                                                                                 AudioBufferList*                                ioData,
381                                                                                 AudioStreamPacketDescription**  outDataPacketDescription,
382                                                                                 void*                                                   inUserData);    
383 // _______________________________________________________________________________________
384 private:
385
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;
393         
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;
402         
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;
410         
411         // formats/conversion
412         AudioConverterRef                       mConverter;
413         CAStreamBasicDescription        mFileDataFormat;
414         CAStreamBasicDescription        mClientDataFormat;
415         CAAudioChannelLayout            mFileChannelLayout;
416         CAAudioChannelLayout            mClientChannelLayout;
417         UInt32                                          mFileMaxPacketSize;
418         UInt32                                          mClientMaxPacketSize;
419         
420         // cookie
421         Byte *                                          mMagicCookie;
422         UInt32                                          mMagicCookieSize;
423         
424         // for ReadPackets
425         UInt32                                          mMaxPacketsToRead;
426         
427         // for WritePackets
428         UInt32                                          mWritePackets;
429         CABufferList *                          mWriteBufferList;
430         
431 #if CAAUDIOFILE_PROFILE
432         // performance
433         bool                                            mProfiling;
434         UInt64                                          mTicksInConverter;
435         UInt64                                          mTicksInReadInConverter;
436         UInt64                                          mTicksInIO;
437         bool                                            mInConverter;
438 #endif
439
440 #endif // CAAF_USE_EXTAUDIOFILE
441 };
442
443 #endif // __CAAudioFile_h__