~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/appleutility/CAAudioFile.cpp

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.cpp
 
40
        
 
41
=============================================================================*/
 
42
 
 
43
#include "CAAudioFile.h"
 
44
 
 
45
#if !CAAF_USE_EXTAUDIOFILE
 
46
 
 
47
#include "CAXException.h"
 
48
#include <algorithm>
 
49
#include "CAHostTimeBase.h"
 
50
#include "CADebugMacros.h"
 
51
 
 
52
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
 
53
        #include <AudioToolbox/AudioToolbox.h>
 
54
#else
 
55
        #include <AudioFormat.h>
 
56
#endif
 
57
 
 
58
#if DEBUG
 
59
        //#define VERBOSE_IO 1
 
60
        //#define VERBOSE_CONVERTER 1
 
61
        //#define VERBOSE_CHANNELMAP 1
 
62
        //#define LOG_FUNCTION_ENTRIES 1
 
63
 
 
64
        #if VERBOSE_CHANNELMAP
 
65
                #include "CAChannelLayouts.h"   // this is in Source/Tests/AudioFileTools/Utility
 
66
        #endif
 
67
#endif
 
68
 
 
69
#if LOG_FUNCTION_ENTRIES
 
70
        class FunctionLogger {
 
71
        public:
 
72
                FunctionLogger(const char *name, const char *fmt=NULL, ...) : mName(name) {
 
73
                        Indent();
 
74
                        printf("-> %s ", name);
 
75
                        if (fmt) {
 
76
                                va_list args;
 
77
                                va_start(args, fmt);
 
78
                                vprintf(fmt, args);
 
79
                                va_end(args);
 
80
                        }
 
81
                        printf("\n");
 
82
                        ++sIndent;
 
83
                }
 
84
                ~FunctionLogger() {
 
85
                        --sIndent;
 
86
                        Indent();
 
87
                        printf("<- %s\n", mName);
 
88
                        if (sIndent == 0)
 
89
                                printf("\n");
 
90
                }
 
91
                
 
92
                static void     Indent() {
 
93
                        for (int i = sIndent; --i >= 0; ) {
 
94
                                putchar(' '); putchar(' ');
 
95
                        }
 
96
                }
 
97
                
 
98
                const char *mName;
 
99
                static int sIndent;
 
100
        };
 
101
        int FunctionLogger::sIndent = 0;
 
102
 
 
103
        #define LOG_FUNCTION(name, format, ...) FunctionLogger _flog(name, format, ## __VA_ARGS__);
 
104
#else
 
105
        #define LOG_FUNCTION(name, format, foo)
 
106
#endif
 
107
 
 
108
static const UInt32 kDefaultIOBufferSizeBytes = 0x10000;
 
109
 
 
110
#if CAAUDIOFILE_PROFILE
 
111
        #define StartTiming(af, starttime) UInt64 starttime = af->mProfiling ? CAHostTimeBase::GetTheCurrentTime() : 0
 
112
        #define ElapsedTime(af, starttime, counter) if (af->mProfiling) counter += (CAHostTimeBase::GetTheCurrentTime() - starttime)
 
113
#else
 
114
        #define StartTiming(af, starttime)
 
115
        #define ElapsedTime(af, starttime, counter)
 
116
#endif
 
117
 
 
118
#define kNoMoreInputRightNow 'nein'
 
119
 
 
120
// _______________________________________________________________________________________
 
121
//
 
122
CAAudioFile::CAAudioFile() :
 
123
        mAudioFile(0),
 
124
        mUseCache(false),
 
125
        mFinishingEncoding(false),
 
126
        mMode(kClosed),
 
127
        mFileDataOffset(-1),
 
128
        mFramesToSkipFollowingSeek(0),
 
129
        
 
130
        mClientOwnsIOBuffer(false),
 
131
        mPacketDescs(NULL),
 
132
        mNumPacketDescs(0),
 
133
        mConverter(NULL),
 
134
        mMagicCookie(NULL),
 
135
        mWriteBufferList(NULL)
 
136
#if CAAUDIOFILE_PROFILE
 
137
    ,
 
138
        mProfiling(false),
 
139
        mTicksInConverter(0),
 
140
        mTicksInReadInConverter(0),
 
141
        mTicksInIO(0),
 
142
        mInConverter(false)
 
143
#endif
 
144
{
 
145
        mIOBufferList.mBuffers[0].mData = NULL;
 
146
        mIOBufferList.mBuffers[0].mDataByteSize = 0;
 
147
        mClientMaxPacketSize = 0;
 
148
        mIOBufferSizeBytes = kDefaultIOBufferSizeBytes;
 
149
}
 
150
 
 
151
// _______________________________________________________________________________________
 
152
//
 
153
CAAudioFile::~CAAudioFile()
 
154
{
 
155
        Close();
 
156
}
 
157
 
 
158
// _______________________________________________________________________________________
 
159
//
 
160
void    CAAudioFile::Close()
 
161
{
 
162
        LOG_FUNCTION("CAAudioFile::Close", NULL, NULL);
 
163
        if (mMode == kClosed)
 
164
                return;
 
165
        if (mMode == kWriting)
 
166
                FlushEncoder();
 
167
        CloseConverter();
 
168
        if (mAudioFile != 0 && mOwnOpenFile) {
 
169
                AudioFileClose(mAudioFile);
 
170
                mAudioFile = 0;
 
171
        }
 
172
        if (!mClientOwnsIOBuffer) {
 
173
                delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
 
174
                mIOBufferList.mBuffers[0].mData = NULL;
 
175
                mIOBufferList.mBuffers[0].mDataByteSize = 0;
 
176
        }
 
177
        delete[] mPacketDescs;  mPacketDescs = NULL;    mNumPacketDescs = 0;
 
178
        delete[] mMagicCookie;  mMagicCookie = NULL;
 
179
        delete mWriteBufferList;        mWriteBufferList = NULL;
 
180
        mMode = kClosed;
 
181
}
 
182
 
 
183
// _______________________________________________________________________________________
 
184
//
 
185
void    CAAudioFile::CloseConverter()
 
186
{
 
187
        if (mConverter) {
 
188
#if VERBOSE_CONVERTER
 
189
                printf("CAAudioFile %p : CloseConverter\n", this);
 
190
#endif
 
191
                AudioConverterDispose(mConverter);
 
192
                mConverter = NULL;
 
193
        }
 
194
}
 
195
 
 
196
// =======================================================================================
 
197
 
 
198
// _______________________________________________________________________________________
 
199
//
 
200
void    CAAudioFile::Open(const FSRef &fsref)
 
201
{
 
202
        LOG_FUNCTION("CAAudioFile::Open", "%p", this);
 
203
        XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
 
204
        mFSRef = fsref;
 
205
        XThrowIfError(AudioFileOpen(&mFSRef, fsRdPerm, 0, &mAudioFile), "open audio file");
 
206
        mOwnOpenFile = true;
 
207
        mMode = kReading;
 
208
        GetExistingFileInfo();
 
209
}
 
210
 
 
211
// _______________________________________________________________________________________
 
212
//
 
213
void    CAAudioFile::Wrap(AudioFileID fileID, bool forWriting)
 
214
{
 
215
        LOG_FUNCTION("CAAudioFile::Wrap", "%p", this);
 
216
        XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
 
217
 
 
218
        mAudioFile = fileID;
 
219
        mOwnOpenFile = false;
 
220
        mMode = forWriting ? kPreparingToWrite : kReading;
 
221
        GetExistingFileInfo();
 
222
        if (forWriting)
 
223
                FileFormatChanged();
 
224
}
 
225
 
 
226
// _______________________________________________________________________________________
 
227
//
 
228
void    CAAudioFile::CreateNew(const FSRef &parentDir, CFStringRef filename, AudioFileTypeID filetype, const AudioStreamBasicDescription &dataFormat, const AudioChannelLayout *layout)
 
229
{
 
230
        LOG_FUNCTION("CAAudioFile::CreateNew", "%p", this);
 
231
        XThrowIf(mMode != kClosed, kExtAudioFileError_InvalidOperationOrder, "file already open");
 
232
        
 
233
        mFileDataFormat = dataFormat;
 
234
        if (layout) {
 
235
                mFileChannelLayout = layout;
 
236
#if VERBOSE_CHANNELMAP
 
237
                printf("PrepareNew passed channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
238
#endif
 
239
        }
 
240
        mMode = kPreparingToCreate;
 
241
        FileFormatChanged(&parentDir, filename, filetype);
 
242
}
 
243
 
 
244
// _______________________________________________________________________________________
 
245
//
 
246
// called to create the file -- or update its format/channel layout/properties based on an encoder 
 
247
// setting change
 
248
void    CAAudioFile::FileFormatChanged(const FSRef *parentDir, CFStringRef filename, AudioFileTypeID filetype)
 
249
{
 
250
        LOG_FUNCTION("CAAudioFile::FileFormatChanged", "%p", this);
 
251
        XThrowIf(mMode != kPreparingToCreate && mMode != kPreparingToWrite, kExtAudioFileError_InvalidOperationOrder, "new file not prepared");
 
252
        
 
253
        UInt32 propertySize;
 
254
        OSStatus err;
 
255
        AudioStreamBasicDescription saveFileDataFormat = mFileDataFormat;
 
256
        
 
257
#if VERBOSE_CONVERTER
 
258
        mFileDataFormat.PrintFormat(stdout, "", "Specified file data format");
 
259
#endif
 
260
        
 
261
        // Find out the actual format the converter will produce. This is necessary in
 
262
        // case the bitrate has forced a lower sample rate, which needs to be set correctly
 
263
        // in the stream description passed to AudioFileCreate.
 
264
        if (mConverter != NULL) {
 
265
                propertySize = sizeof(AudioStreamBasicDescription);
 
266
                Float64 origSampleRate = mFileDataFormat.mSampleRate;
 
267
                XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCurrentOutputStreamDescription, &propertySize, &mFileDataFormat), "get audio converter's output stream description");
 
268
                // do the same for the channel layout being output by the converter
 
269
#if VERBOSE_CONVERTER
 
270
                mFileDataFormat.PrintFormat(stdout, "", "Converter output");
 
271
#endif
 
272
                if (fiszero(mFileDataFormat.mSampleRate))
 
273
                        mFileDataFormat.mSampleRate = origSampleRate;
 
274
                err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterOutputChannelLayout, &propertySize, NULL);
 
275
                if (err == noErr && propertySize > 0) {
 
276
                        AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
 
277
                        err = AudioConverterGetProperty(mConverter, kAudioConverterOutputChannelLayout, &propertySize, layout);
 
278
                        if (err) {
 
279
                                free(layout);
 
280
                                XThrow(err, "couldn't get audio converter's output channel layout");
 
281
                        }
 
282
                        mFileChannelLayout = layout;
 
283
#if VERBOSE_CHANNELMAP
 
284
                        printf("got new file's channel layout from converter: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
285
#endif
 
286
                        free(layout);
 
287
                }
 
288
        }
 
289
        
 
290
        // create the output file
 
291
        if (mMode == kPreparingToCreate) {
 
292
                CAStreamBasicDescription newFileDataFormat = mFileDataFormat;
 
293
                if (fiszero(newFileDataFormat.mSampleRate))
 
294
                        newFileDataFormat.mSampleRate = 44100;  // just make something up for now
 
295
#if VERBOSE_CONVERTER
 
296
                newFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
 
297
#endif
 
298
                XThrowIfError(AudioFileCreate(parentDir, filename, filetype, &newFileDataFormat, 0, &mFSRef, &mAudioFile), "create audio file");
 
299
                mMode = kPreparingToWrite;
 
300
                mOwnOpenFile = true;
 
301
        } else if (saveFileDataFormat != mFileDataFormat || fnotequal(saveFileDataFormat.mSampleRate, mFileDataFormat.mSampleRate)) {
 
302
                // second check must be explicit since operator== on ASBD treats SR of zero as "don't care"
 
303
                if (fiszero(mFileDataFormat.mSampleRate))
 
304
                        mFileDataFormat.mSampleRate = mClientDataFormat.mSampleRate;
 
305
#if VERBOSE_CONVERTER
 
306
                mFileDataFormat.PrintFormat(stdout, "", "Applied to new file");
 
307
#endif
 
308
                XThrowIf(fiszero(mFileDataFormat.mSampleRate), kExtAudioFileError_InvalidDataFormat, "file's sample rate is 0");
 
309
                XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyDataFormat, sizeof(AudioStreamBasicDescription), &mFileDataFormat), "couldn't update file's data format");
 
310
        }
 
311
 
 
312
        UInt32 deferSizeUpdates = 1;
 
313
        err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyDeferSizeUpdates, sizeof(UInt32), &deferSizeUpdates);
 
314
 
 
315
        if (mConverter != NULL) {
 
316
                // encoder
 
317
                // get the magic cookie, if any, from the converter             
 
318
                delete[] mMagicCookie;  mMagicCookie = NULL;
 
319
                mMagicCookieSize = 0;
 
320
 
 
321
                err = AudioConverterGetPropertyInfo(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, NULL);
 
322
                
 
323
                // we can get a noErr result and also a propertySize == 0
 
324
                // -- if the file format does support magic cookies, but this file doesn't have one.
 
325
                if (err == noErr && propertySize > 0) {
 
326
                        mMagicCookie = new Byte[propertySize];
 
327
                        XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterCompressionMagicCookie, &propertySize, mMagicCookie), "get audio converter's magic cookie");
 
328
                        mMagicCookieSize = propertySize;        // the converter lies and tell us the wrong size
 
329
                        // now set the magic cookie on the output file
 
330
                        UInt32 willEatTheCookie = false;
 
331
                        // the converter wants to give us one; will the file take it?
 
332
                        err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData,
 
333
                                        NULL, &willEatTheCookie);
 
334
                        if (err == noErr && willEatTheCookie) {
 
335
#if VERBOSE_CONVERTER
 
336
                                printf("Setting cookie on encoded file\n");
 
337
#endif
 
338
                                XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, mMagicCookieSize, mMagicCookie), "set audio file's magic cookie");
 
339
                        }
 
340
                }
 
341
                
 
342
                // get maximum packet size
 
343
                propertySize = sizeof(UInt32);
 
344
                XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertyMaximumOutputPacketSize, &propertySize, &mFileMaxPacketSize), "get audio converter's maximum output packet size");
 
345
 
 
346
                AllocateBuffers(true /* okToFail */);
 
347
        } else {
 
348
                InitFileMaxPacketSize();
 
349
        }
 
350
        
 
351
        if (mFileChannelLayout.IsValid() && mFileChannelLayout.NumberChannels() > 2) {
 
352
                // don't bother tagging mono/stereo files
 
353
                UInt32 isWritable;
 
354
                err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, NULL, &isWritable);
 
355
                if (!err && isWritable) {
 
356
#if VERBOSE_CHANNELMAP
 
357
                        printf("writing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
358
#endif
 
359
                        err = AudioFileSetProperty(mAudioFile, kAudioFilePropertyChannelLayout, 
 
360
                                mFileChannelLayout.Size(), &mFileChannelLayout.Layout());
 
361
                        if (err)
 
362
                                CAXException::Warning("could not set the file's channel layout", err);
 
363
                } else {
 
364
#if VERBOSE_CHANNELMAP
 
365
                        printf("file won't accept a channel layout (write)\n");
 
366
#endif
 
367
                }
 
368
        }
 
369
        
 
370
        UpdateClientMaxPacketSize();    // also sets mFrame0Offset
 
371
        mPacketMark = 0;
 
372
        mFrameMark = 0;
 
373
}
 
374
 
 
375
// _______________________________________________________________________________________
 
376
//
 
377
void    CAAudioFile::InitFileMaxPacketSize()
 
378
{
 
379
        LOG_FUNCTION("CAAudioFile::InitFileMaxPacketSize", "%p", this);
 
380
        UInt32 propertySize = sizeof(UInt32);
 
381
        OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyMaximumPacketSize, 
 
382
                &propertySize, &mFileMaxPacketSize);
 
383
        if (err) {
 
384
                // workaround for 3361377: not all file formats' maximum packet sizes are supported
 
385
                if (!mFileDataFormat.IsPCM())
 
386
                        XThrowIfError(err, "get audio file's maximum packet size");
 
387
                mFileMaxPacketSize = mFileDataFormat.mBytesPerFrame;
 
388
        }
 
389
        AllocateBuffers(true /* okToFail */);
 
390
}
 
391
 
 
392
 
 
393
// _______________________________________________________________________________________
 
394
//
 
395
SInt64  CAAudioFile::FileDataOffset()
 
396
{
 
397
        if (mFileDataOffset < 0) {
 
398
                UInt32 propertySize = sizeof(SInt64);
 
399
                XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataOffset, &propertySize, &mFileDataOffset), "couldn't get file's data offset");
 
400
        }
 
401
        return mFileDataOffset;
 
402
}
 
403
 
 
404
// _______________________________________________________________________________________
 
405
//
 
406
SInt64  CAAudioFile::GetNumberFrames() const
 
407
{
 
408
        AudioFilePacketTableInfo pti;
 
409
        UInt32 propertySize = sizeof(pti);
 
410
        OSStatus err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
 
411
        if (err == noErr)
 
412
                return pti.mNumberValidFrames;
 
413
        return mFileDataFormat.mFramesPerPacket * GetNumberPackets() - mFrame0Offset;
 
414
}
 
415
 
 
416
// _______________________________________________________________________________________
 
417
//
 
418
void    CAAudioFile::SetNumberFrames(SInt64 nFrames)
 
419
{
 
420
        XThrowIf(mFileDataFormat.mFramesPerPacket != 1, kExtAudioFileError_InvalidDataFormat, "SetNumberFrames only supported for PCM");
 
421
        XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, sizeof(SInt64), &nFrames), "Couldn't set number of packets on audio file");
 
422
}
 
423
 
 
424
// _______________________________________________________________________________________
 
425
//
 
426
// call for existing file, NOT new one - from Open() or Wrap()
 
427
void    CAAudioFile::GetExistingFileInfo()
 
428
{
 
429
        LOG_FUNCTION("CAAudioFile::GetExistingFileInfo", "%p", this);
 
430
        UInt32 propertySize;
 
431
        OSStatus err;
 
432
        
 
433
        // get mFileDataFormat
 
434
        propertySize = sizeof(AudioStreamBasicDescription);
 
435
        XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &propertySize, &mFileDataFormat), "get audio file's data format");
 
436
        
 
437
        // get mFileChannelLayout
 
438
        err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, NULL);
 
439
        if (err == noErr && propertySize > 0) {
 
440
                AudioChannelLayout *layout = static_cast<AudioChannelLayout *>(malloc(propertySize));
 
441
                err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyChannelLayout, &propertySize, layout);
 
442
                if (err == noErr) {
 
443
                        mFileChannelLayout = layout;
 
444
#if VERBOSE_CHANNELMAP
 
445
                        printf("existing file's channel layout: %s\n", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
446
#endif
 
447
                }
 
448
                free(layout);
 
449
                XThrowIfError(err, "get audio file's channel layout");
 
450
        }
 
451
        if (mMode != kReading)
 
452
                return;
 
453
        
 
454
#if 0
 
455
        // get mNumberPackets
 
456
        propertySize = sizeof(mNumberPackets);
 
457
        XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyAudioDataPacketCount, &propertySize, &mNumberPackets), "get audio file's packet count");
 
458
#if VERBOSE_IO
 
459
        printf("CAAudioFile::GetExistingFileInfo: %qd packets\n", mNumberPackets);
 
460
#endif
 
461
#endif
 
462
        
 
463
        // get mMagicCookie
 
464
        err = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
 
465
        if (err == noErr && propertySize > 0) {
 
466
                mMagicCookie = new Byte[propertySize];
 
467
                mMagicCookieSize = propertySize;
 
468
                XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &propertySize, mMagicCookie), "get audio file's magic cookie");
 
469
        }
 
470
        InitFileMaxPacketSize();
 
471
        mPacketMark = 0;
 
472
        mFrameMark = 0;
 
473
        
 
474
        UpdateClientMaxPacketSize();
 
475
}
 
476
 
 
477
// =======================================================================================
 
478
 
 
479
// _______________________________________________________________________________________
 
480
//
 
481
void    CAAudioFile::SetFileChannelLayout(const CAAudioChannelLayout &layout)
 
482
{
 
483
        LOG_FUNCTION("CAAudioFile::SetFileChannelLayout", "%p", this);
 
484
        mFileChannelLayout = layout;
 
485
#if VERBOSE_CHANNELMAP
 
486
        printf("file channel layout set explicitly (%s): %s\n", mMode == kReading ? "read" : "write", CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
487
#endif
 
488
        if (mMode != kReading)
 
489
                FileFormatChanged();
 
490
}
 
491
 
 
492
// _______________________________________________________________________________________
 
493
//
 
494
void    CAAudioFile::SetClientFormat(const CAStreamBasicDescription &dataFormat, const CAAudioChannelLayout *layout)
 
495
{
 
496
        LOG_FUNCTION("CAAudioFile::SetClientFormat", "%p", this);
 
497
        XThrowIf(!dataFormat.IsPCM(), kExtAudioFileError_NonPCMClientFormat, "non-PCM client format on audio file");
 
498
        
 
499
        bool dataFormatChanging = (mClientDataFormat.mFormatID == 0 || mClientDataFormat != dataFormat);
 
500
        
 
501
        if (dataFormatChanging) {
 
502
                CloseConverter();
 
503
                if (mWriteBufferList) {
 
504
                        delete mWriteBufferList;
 
505
                        mWriteBufferList = NULL;
 
506
                }
 
507
                mClientDataFormat = dataFormat;
 
508
        }
 
509
        
 
510
        if (layout && layout->IsValid()) {
 
511
                XThrowIf(layout->NumberChannels() != mClientDataFormat.NumberChannels(), kExtAudioFileError_InvalidChannelMap, "inappropriate channel map");
 
512
                mClientChannelLayout = *layout;
 
513
        }
 
514
        
 
515
        bool differentLayouts;
 
516
        if (mClientChannelLayout.IsValid()) {
 
517
                if (mFileChannelLayout.IsValid()) {
 
518
                        differentLayouts = mClientChannelLayout.Tag() != mFileChannelLayout.Tag();
 
519
#if VERBOSE_CHANNELMAP
 
520
                        printf("two valid layouts, %s\n", differentLayouts ? "different" : "same");
 
521
#endif
 
522
                } else {
 
523
                        differentLayouts = false;
 
524
#if VERBOSE_CHANNELMAP
 
525
                        printf("valid client layout, unknown file layout\n");
 
526
#endif
 
527
                }
 
528
        } else {
 
529
                differentLayouts = false;
 
530
#if VERBOSE_CHANNELMAP
 
531
                if (mFileChannelLayout.IsValid())
 
532
                        printf("valid file layout, unknown client layout\n");
 
533
                else
 
534
                        printf("two invalid layouts\n");
 
535
#endif
 
536
        }
 
537
        
 
538
        if (mClientDataFormat != mFileDataFormat || differentLayouts) {
 
539
                // We need an AudioConverter.
 
540
                if (mMode == kReading) {
 
541
                        // file -> client (decode)
 
542
//mFileDataFormat.PrintFormat(  stdout, "", "File:   ");
 
543
//mClientDataFormat.PrintFormat(stdout, "", "Client: ");
 
544
 
 
545
                        if (mConverter == NULL)
 
546
                                XThrowIfError(AudioConverterNew(&mFileDataFormat, &mClientDataFormat, &mConverter),
 
547
                                "create audio converter");
 
548
                        
 
549
#if VERBOSE_CONVERTER
 
550
                        printf("CAAudioFile %p -- created converter\n", this);
 
551
                        CAShow(mConverter);
 
552
#endif
 
553
                        // set the magic cookie, if any (for decode)
 
554
                        if (mMagicCookie)
 
555
                                SetConverterProperty(kAudioConverterDecompressionMagicCookie, mMagicCookieSize, mMagicCookie, mFileDataFormat.IsPCM());
 
556
                                        // we get cookies from some AIFF's but the converter barfs on them,
 
557
                                        // so we set canFail to true for PCM
 
558
 
 
559
                        SetConverterChannelLayout(false, mFileChannelLayout);
 
560
                        SetConverterChannelLayout(true, mClientChannelLayout);
 
561
                        
 
562
                        // propagate leading/trailing frame counts
 
563
                        if (mFileDataFormat.mBitsPerChannel == 0) {
 
564
                                UInt32 propertySize;
 
565
                                OSStatus err;
 
566
                                AudioFilePacketTableInfo pti;
 
567
                                propertySize = sizeof(pti);
 
568
                                err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
 
569
                                if (err == noErr && (pti.mPrimingFrames > 0 || pti.mRemainderFrames > 0)) {
 
570
                                        AudioConverterPrimeInfo primeInfo;
 
571
                                        primeInfo.leadingFrames = pti.mPrimingFrames;
 
572
                                        primeInfo.trailingFrames = pti.mRemainderFrames;
 
573
                                        /* ignore any error. better to play it at all than not. */
 
574
                                        /*err = */AudioConverterSetProperty(mConverter, kAudioConverterPrimeInfo, sizeof(primeInfo), &primeInfo);
 
575
                                        //XThrowIfError(err, "couldn't set prime info on converter");
 
576
                                }
 
577
                        }
 
578
                } else if (mMode == kPreparingToCreate || mMode == kPreparingToWrite) {
 
579
                        // client -> file (encode)
 
580
                        if (mConverter == NULL)
 
581
                                XThrowIfError(AudioConverterNew(&mClientDataFormat, &mFileDataFormat, &mConverter), "create audio converter");
 
582
                        mWriteBufferList = CABufferList::New("", mClientDataFormat);
 
583
                        SetConverterChannelLayout(false, mClientChannelLayout);
 
584
                        SetConverterChannelLayout(true, mFileChannelLayout);
 
585
                        if (mMode == kPreparingToWrite)
 
586
                                FileFormatChanged();
 
587
                } else
 
588
                        XThrowIfError(kExtAudioFileError_InvalidOperationOrder, "audio file format not yet known");
 
589
        }
 
590
        UpdateClientMaxPacketSize();
 
591
}
 
592
 
 
593
// _______________________________________________________________________________________
 
594
//
 
595
OSStatus        CAAudioFile::SetConverterProperty(      
 
596
                                                                                        AudioConverterPropertyID        inPropertyID,
 
597
                                                                                        UInt32                                          inPropertyDataSize,
 
598
                                                                                        const void*                                     inPropertyData,
 
599
                                                                                        bool                                            inCanFail)
 
600
{
 
601
        OSStatus err = noErr;
 
602
        //LOG_FUNCTION("ExtAudioFile::SetConverterProperty", "%p %-4.4s", this, (char *)&inPropertyID);
 
603
        if (inPropertyID == kAudioConverterPropertySettings && *(CFPropertyListRef *)inPropertyData == NULL)
 
604
                ;
 
605
        else {
 
606
                err = AudioConverterSetProperty(mConverter, inPropertyID, inPropertyDataSize, inPropertyData);
 
607
                if (!inCanFail) {
 
608
                        XThrowIfError(err, "set audio converter property");
 
609
                }
 
610
        }
 
611
        UpdateClientMaxPacketSize();
 
612
        if (mMode == kPreparingToWrite)
 
613
                FileFormatChanged();
 
614
        return err;
 
615
}
 
616
 
 
617
// _______________________________________________________________________________________
 
618
//
 
619
void    CAAudioFile::SetConverterChannelLayout(bool output, const CAAudioChannelLayout &layout)
 
620
{
 
621
        LOG_FUNCTION("CAAudioFile::SetConverterChannelLayout", "%p", this);
 
622
        OSStatus err;
 
623
        
 
624
        if (layout.IsValid()) {
 
625
#if VERBOSE_CHANNELMAP
 
626
                printf("Setting converter's %s channel layout: %s\n", output ? "output" : "input",
 
627
                        CAChannelLayouts::ConstantToString(mFileChannelLayout.Tag()));
 
628
#endif
 
629
                if (output) {
 
630
                        err = AudioConverterSetProperty(mConverter, kAudioConverterOutputChannelLayout,
 
631
                                layout.Size(), &layout.Layout());
 
632
                        XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's output channel layout");
 
633
                } else {
 
634
                        err = AudioConverterSetProperty(mConverter, kAudioConverterInputChannelLayout,
 
635
                                layout.Size(), &layout.Layout());
 
636
                        XThrowIf(err && err != kAudioConverterErr_OperationNotSupported, err, "couldn't set converter's input channel layout");
 
637
                }
 
638
                if (mMode == kPreparingToWrite)
 
639
                        FileFormatChanged();
 
640
        }
 
641
}
 
642
 
 
643
// _______________________________________________________________________________________
 
644
//
 
645
CFArrayRef  CAAudioFile::GetConverterConfig()
 
646
{
 
647
        CFArrayRef plist;
 
648
        UInt32 propertySize = sizeof(plist);
 
649
        XThrowIfError(AudioConverterGetProperty(mConverter, kAudioConverterPropertySettings, &propertySize, &plist), "get converter property settings");
 
650
        return plist;
 
651
}
 
652
 
 
653
// _______________________________________________________________________________________
 
654
//
 
655
void    CAAudioFile::UpdateClientMaxPacketSize()
 
656
{
 
657
        LOG_FUNCTION("CAAudioFile::UpdateClientMaxPacketSize", "%p", this);
 
658
        mFrame0Offset = 0;
 
659
        if (mConverter != NULL) {
 
660
                AudioConverterPropertyID property = (mMode == kReading) ? 
 
661
                        kAudioConverterPropertyMaximumOutputPacketSize :
 
662
                        kAudioConverterPropertyMaximumInputPacketSize;
 
663
                        
 
664
                UInt32 propertySize = sizeof(UInt32);
 
665
                XThrowIfError(AudioConverterGetProperty(mConverter, property, &propertySize, &mClientMaxPacketSize),
 
666
                        "get audio converter's maximum packet size");
 
667
                
 
668
                if (mFileDataFormat.mBitsPerChannel == 0) {
 
669
                        AudioConverterPrimeInfo primeInfo;
 
670
                        propertySize = sizeof(primeInfo);
 
671
                        OSStatus err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
 
672
                        if (err == noErr)
 
673
                                mFrame0Offset = primeInfo.leadingFrames;
 
674
#if VERBOSE_CONVERTER
 
675
                        printf("kAudioConverterPrimeInfo: err = %ld, leadingFrames = %ld\n", err, mFrame0Offset);
 
676
#endif
 
677
                }
 
678
        } else {
 
679
                mClientMaxPacketSize = mFileMaxPacketSize;
 
680
        }
 
681
}
 
682
 
 
683
// _______________________________________________________________________________________
 
684
//      Allocates: mIOBufferList, mIOBufferSizePackets, mPacketDescs
 
685
//      Dependent on: mFileMaxPacketSize, mIOBufferSizeBytes
 
686
void    CAAudioFile::AllocateBuffers(bool okToFail)
 
687
{
 
688
        LOG_FUNCTION("CAAudioFile::AllocateBuffers", "%p", this);
 
689
        if (mFileMaxPacketSize == 0) {
 
690
                if (okToFail)
 
691
                        return;
 
692
                XThrowIf(true, kExtAudioFileError_MaxPacketSizeUnknown, "file's maximum packet size is 0");
 
693
        }
 
694
        UInt32 bufferSizeBytes = mIOBufferSizeBytes = std::max(mIOBufferSizeBytes, mFileMaxPacketSize);
 
695
                // must be big enough for at least one maximum size packet
 
696
        
 
697
        if (mIOBufferList.mBuffers[0].mDataByteSize != bufferSizeBytes) {
 
698
                mIOBufferList.mNumberBuffers = 1;
 
699
                mIOBufferList.mBuffers[0].mNumberChannels = mFileDataFormat.mChannelsPerFrame;
 
700
                if (!mClientOwnsIOBuffer) {
 
701
                        //printf("reallocating I/O buffer\n");
 
702
                        delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
 
703
                        mIOBufferList.mBuffers[0].mData = new Byte[bufferSizeBytes];
 
704
                }
 
705
                mIOBufferList.mBuffers[0].mDataByteSize = bufferSizeBytes;
 
706
                mIOBufferSizePackets = bufferSizeBytes / mFileMaxPacketSize;
 
707
        }
 
708
        
 
709
        UInt32 propertySize = sizeof(UInt32);
 
710
        UInt32 externallyFramed;
 
711
        XThrowIfError(AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed,
 
712
                        sizeof(AudioStreamBasicDescription), &mFileDataFormat, &propertySize, &externallyFramed),
 
713
                        "is format externally framed");
 
714
        if (mNumPacketDescs != (externallyFramed ? mIOBufferSizePackets : 0)) {
 
715
                delete[] mPacketDescs;
 
716
                mPacketDescs = NULL;
 
717
                mNumPacketDescs = 0;
 
718
 
 
719
                if (externallyFramed) {
 
720
                        //printf("reallocating packet descs\n");
 
721
                        mPacketDescs = new AudioStreamPacketDescription[mIOBufferSizePackets];
 
722
                        mNumPacketDescs = mIOBufferSizePackets;
 
723
                }
 
724
        }
 
725
}
 
726
 
 
727
// _______________________________________________________________________________________
 
728
//
 
729
void    CAAudioFile::SetIOBuffer(void *buf)
 
730
{
 
731
        if (!mClientOwnsIOBuffer)
 
732
                delete[] (Byte *)mIOBufferList.mBuffers[0].mData;
 
733
        mIOBufferList.mBuffers[0].mData = buf;
 
734
 
 
735
        if (buf == NULL) {
 
736
                mClientOwnsIOBuffer = false;
 
737
                SetIOBufferSizeBytes(mIOBufferSizeBytes);
 
738
        } else {
 
739
                mClientOwnsIOBuffer = true;
 
740
                AllocateBuffers();
 
741
        }
 
742
//      printf("CAAudioFile::SetIOBuffer %p: %p, 0x%lx bytes, mClientOwns = %d\n", this, mIOBufferList.mBuffers[0].mData, mIOBufferSizeBytes, mClientOwnsIOBuffer);
 
743
}
 
744
 
 
745
// ===============================================================================
 
746
 
 
747
/*
 
748
For Tiger:
 
749
added kAudioFilePropertyPacketToFrame and kAudioFilePropertyFrameToPacket.
 
750
You pass in an AudioFramePacketTranslation struct, with the appropriate field filled in, to AudioFileGetProperty.
 
751
 
 
752
        kAudioFilePropertyPacketToFrame                 =       'pkfr',
 
753
                // pass a AudioFramePacketTranslation with mPacket filled out and get mFrame back. mFrameOffsetInPacket is ignored.
 
754
        kAudioFilePropertyFrameToPacket                 =       'frpk',
 
755
                // pass a AudioFramePacketTranslation with mFrame filled out and get mPacket and mFrameOffsetInPacket back.
 
756
 
 
757
struct AudioFramePacketTranslation
 
758
{
 
759
        SInt64 mFrame;
 
760
        SInt64 mPacket;
 
761
        UInt32 mFrameOffsetInPacket;
 
762
};
 
763
*/
 
764
 
 
765
SInt64  CAAudioFile::PacketToFrame(SInt64 packet) const
 
766
{
 
767
        AudioFramePacketTranslation trans;
 
768
        UInt32 propertySize;
 
769
        
 
770
        switch (mFileDataFormat.mFramesPerPacket) {
 
771
        case 1:
 
772
                return packet;
 
773
        case 0:
 
774
                trans.mPacket = packet;
 
775
                propertySize = sizeof(trans);
 
776
                XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketToFrame, &propertySize, &trans),
 
777
                        "packet <-> frame translation unimplemented for format with variable frames/packet");
 
778
                return trans.mFrame;
 
779
        }
 
780
        return packet * mFileDataFormat.mFramesPerPacket;
 
781
}
 
782
 
 
783
SInt64  CAAudioFile::FrameToPacket(SInt64 inFrame) const
 
784
{
 
785
        AudioFramePacketTranslation trans;
 
786
        UInt32 propertySize;
 
787
        
 
788
        switch (mFileDataFormat.mFramesPerPacket) {
 
789
        case 1:
 
790
                return inFrame;
 
791
        case 0:
 
792
                trans.mFrame = inFrame;
 
793
                propertySize = sizeof(trans);
 
794
                XThrowIfError(AudioFileGetProperty(mAudioFile, kAudioFilePropertyFrameToPacket, &propertySize, &trans),
 
795
                        "packet <-> frame translation unimplemented for format with variable frames/packet");
 
796
                return trans.mPacket;
 
797
        }
 
798
        return inFrame / mFileDataFormat.mFramesPerPacket;
 
799
}
 
800
 
 
801
// _______________________________________________________________________________________
 
802
//
 
803
 
 
804
SInt64  CAAudioFile::Tell() const       // frameNumber
 
805
{
 
806
        return mFrameMark - mFrame0Offset;
 
807
}
 
808
 
 
809
void    CAAudioFile::SeekToPacket(SInt64 packetNumber)
 
810
{
 
811
#if VERBOSE_IO
 
812
        printf("CAAudioFile::SeekToPacket: %qd\n", packetNumber);
 
813
#endif
 
814
        XThrowIf(mMode != kReading || packetNumber < 0 /*|| packetNumber >= mNumberPackets*/ , kExtAudioFileError_InvalidSeek, "seek to packet in audio file");
 
815
        if (mPacketMark == packetNumber)
 
816
                return; // already there! don't reset converter
 
817
        mPacketMark = packetNumber;
 
818
        
 
819
        mFrameMark = PacketToFrame(packetNumber) - mFrame0Offset;
 
820
        mFramesToSkipFollowingSeek = 0;
 
821
        if (mConverter)
 
822
                // must reset -- if we reached end of stream. converter will no longer work otherwise
 
823
                AudioConverterReset(mConverter);
 
824
}
 
825
 
 
826
/*
 
827
        Example: AAC, 1024 frames/packet, 2112 frame offset
 
828
        
 
829
                                           2112
 
830
                                             |
 
831
    Absolute frames:  0       1024      2048 |    3072
 
832
                      +---------+---------+--|------+---------+---------+
 
833
    Packets:          |    0    |    1    |  | 2    |    3    |    4    |
 
834
                      +---------+---------+--|------+---------+---------+
 
835
    Client frames:  -2112   -1088       -64  |     960                                          SeekToFrame, TellFrame
 
836
                                             |
 
837
                                             0
 
838
 
 
839
        *   Offset between absolute and client frames is mFrame0Offset.
 
840
        *** mFrameMark is in client frames ***
 
841
        
 
842
        Examples:
 
843
                clientFrame                                     0               960             1000    1024
 
844
                absoluteFrame                           2112    3072    3112    3136
 
845
                packet                                          0               0               0               1
 
846
                tempFrameMark*                          -2112   -2112   -2112   -1088
 
847
                mFramesToSkipFollowingSeek      2112    3072    3112    2112
 
848
*/
 
849
void    CAAudioFile::Seek(SInt64 clientFrame)
 
850
{
 
851
        if (clientFrame == mFrameMark)
 
852
                return; // already there! don't reset converter
 
853
 
 
854
        //SInt64 absoluteFrame = clientFrame + mFrame0Offset;
 
855
        XThrowIf(mMode != kReading || clientFrame < 0 || !mClientDataFormat.IsPCM(), kExtAudioFileError_InvalidSeek, "seek to frame in audio file");
 
856
 
 
857
#if VERBOSE_IO
 
858
        SInt64 prevFrameMark = mFrameMark;
 
859
#endif
 
860
        
 
861
        SInt64 packet;
 
862
        packet = FrameToPacket(clientFrame);
 
863
        if (packet < 0)
 
864
                packet = 0;
 
865
        SeekToPacket(packet);
 
866
        // this will have backed up mFrameMark to match the beginning of the packet
 
867
        mFramesToSkipFollowingSeek = std::max(UInt32(clientFrame - mFrameMark), UInt32(0));
 
868
        mFrameMark = clientFrame;
 
869
        
 
870
#if VERBOSE_IO
 
871
        printf("CAAudioFile::SeekToFrame: frame %qd (from %qd), packet %qd, skip %ld frames\n", mFrameMark, prevFrameMark, packet, mFramesToSkipFollowingSeek);
 
872
#endif
 
873
}
 
874
 
 
875
// _______________________________________________________________________________________
 
876
//
 
877
void    CAAudioFile::Read(UInt32 &ioNumPackets, AudioBufferList *ioData)
 
878
                        // May read fewer packets than requested if:
 
879
                        //              buffer is not big enough
 
880
                        //              file does not contain that many more packets
 
881
                        // Note that eofErr is not fatal, just results in 0 packets returned
 
882
                        // ioData's buffer sizes may be shortened
 
883
{
 
884
        XThrowIf(mClientMaxPacketSize == 0, kExtAudioFileError_MaxPacketSizeUnknown, "client maximum packet size is 0");
 
885
        if (mIOBufferList.mBuffers[0].mData == NULL) {
 
886
#if DEBUG
 
887
                printf("warning: CAAudioFile::AllocateBuffers called from ReadPackets\n");
 
888
#endif
 
889
                AllocateBuffers();
 
890
        }
 
891
        UInt32 bufferSizeBytes = ioData->mBuffers[0].mDataByteSize;
 
892
        UInt32 maxNumPackets = bufferSizeBytes / mClientMaxPacketSize;  
 
893
        // older versions of AudioConverterFillComplexBuffer don't do this, so do our own sanity check
 
894
        UInt32 nPackets = std::min(ioNumPackets, maxNumPackets);
 
895
        
 
896
        mMaxPacketsToRead = ~0UL;
 
897
        
 
898
        if (mClientDataFormat.mFramesPerPacket == 1) {  // PCM or equivalent
 
899
                while (mFramesToSkipFollowingSeek > 0) {
 
900
                        UInt32 skipFrames = std::min(mFramesToSkipFollowingSeek, maxNumPackets);
 
901
                        UInt32 framesPerPacket;
 
902
                        if ((framesPerPacket=mFileDataFormat.mFramesPerPacket) > 0)
 
903
                                mMaxPacketsToRead = (skipFrames + framesPerPacket - 1) / framesPerPacket;
 
904
 
 
905
                        if (mConverter == NULL) {
 
906
                                XThrowIfError(ReadInputProc(NULL, &skipFrames, ioData, NULL, this), "read audio file");
 
907
                        } else {
 
908
#if CAAUDIOFILE_PROFILE
 
909
                                mInConverter = true;
 
910
#endif
 
911
                                StartTiming(this, fill);
 
912
                                XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &skipFrames, ioData, NULL), "convert audio packets (pcm read)");
 
913
                                ElapsedTime(this, fill, mTicksInConverter);
 
914
#if CAAUDIOFILE_PROFILE
 
915
                                mInConverter = false;
 
916
#endif
 
917
                        }
 
918
                        if (skipFrames == 0) {  // hit EOF
 
919
                                ioNumPackets = 0;
 
920
                                return;
 
921
                        }
 
922
                        mFrameMark += skipFrames;
 
923
#if VERBOSE_IO
 
924
                        printf("CAAudioFile::ReadPackets: skipped %ld frames\n", skipFrames);
 
925
#endif
 
926
 
 
927
                        mFramesToSkipFollowingSeek -= skipFrames;
 
928
 
 
929
                        // restore mDataByteSize
 
930
                        for (int i = ioData->mNumberBuffers; --i >= 0 ; )
 
931
                                ioData->mBuffers[i].mDataByteSize = bufferSizeBytes;
 
932
                }
 
933
        }
 
934
        
 
935
        if (mFileDataFormat.mFramesPerPacket > 0)
 
936
                // don't read more packets than we are being asked to produce
 
937
                mMaxPacketsToRead = nPackets / mFileDataFormat.mFramesPerPacket + 1;
 
938
        if (mConverter == NULL) {
 
939
                XThrowIfError(ReadInputProc(NULL, &nPackets, ioData, NULL, this), "read audio file");
 
940
        } else {
 
941
#if CAAUDIOFILE_PROFILE
 
942
                mInConverter = true;
 
943
#endif
 
944
                StartTiming(this, fill);
 
945
                XThrowIfError(AudioConverterFillComplexBuffer(mConverter, ReadInputProc, this, &nPackets, ioData, NULL), "convert audio packets (read)");
 
946
                ElapsedTime(this, fill, mTicksInConverter);
 
947
#if CAAUDIOFILE_PROFILE
 
948
                mInConverter = false;
 
949
#endif
 
950
        }
 
951
        if (mClientDataFormat.mFramesPerPacket == 1)
 
952
                mFrameMark += nPackets;
 
953
        
 
954
        ioNumPackets = nPackets;
 
955
}
 
956
 
 
957
// _______________________________________________________________________________________
 
958
//
 
959
OSStatus CAAudioFile::ReadInputProc(    AudioConverterRef                               inAudioConverter,
 
960
                                                                                UInt32*                                                 ioNumberDataPackets,
 
961
                                                                                AudioBufferList*                                ioData,
 
962
                                                                                AudioStreamPacketDescription**  outDataPacketDescription,
 
963
                                                                                void*                                                   inUserData)
 
964
{
 
965
        CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
 
966
 
 
967
#if 0
 
968
        SInt64 remainingPacketsInFile = This->mNumberPackets - This->mPacketMark;
 
969
        if (remainingPacketsInFile <= 0) {
 
970
                *ioNumberDataPackets = 0;
 
971
                ioData->mBuffers[0].mDataByteSize = 0;
 
972
                if (outDataPacketDescription)
 
973
                        *outDataPacketDescription = This->mPacketDescs;
 
974
#if VERBOSE_IO
 
975
                printf("CAAudioFile::ReadInputProc: EOF\n");
 
976
#endif
 
977
                return noErr;   // not eofErr; EOF is signified by 0 packets/0 bytes
 
978
        }
 
979
#endif
 
980
        
 
981
        // determine how much to read
 
982
        AudioBufferList *readBuffer;
 
983
        UInt32 readPackets;
 
984
        if (inAudioConverter != NULL) {
 
985
                // getting called from converter, need to use our I/O buffer
 
986
                readBuffer = &This->mIOBufferList;
 
987
                readPackets = This->mIOBufferSizePackets;
 
988
        } else {
 
989
                // getting called directly from ReadPackets, use supplied buffer
 
990
                if (This->mFileMaxPacketSize == 0)
 
991
                        return kExtAudioFileError_MaxPacketSizeUnknown;
 
992
                readBuffer = ioData;
 
993
                readPackets = std::min(*ioNumberDataPackets, readBuffer->mBuffers[0].mDataByteSize / This->mFileMaxPacketSize);
 
994
                        // don't attempt to read more packets than will fit in the buffer
 
995
        }
 
996
        // don't try to read past EOF
 
997
//      if (readPackets > remainingPacketsInFile)
 
998
//              readPackets = remainingPacketsInFile;
 
999
        // don't read more packets than necessary to produce the requested amount of converted data
 
1000
        if (readPackets > This->mMaxPacketsToRead) {
 
1001
#if VERBOSE_IO
 
1002
                printf("CAAudioFile::ReadInputProc: limiting read to %ld packets (from %ld)\n", This->mMaxPacketsToRead, readPackets);
 
1003
#endif
 
1004
                readPackets = This->mMaxPacketsToRead;
 
1005
        }
 
1006
        
 
1007
        // read
 
1008
        UInt32 bytesRead;
 
1009
        OSStatus err;
 
1010
        
 
1011
        StartTiming(This, read);
 
1012
        StartTiming(This, readinconv);
 
1013
        err = AudioFileReadPackets(This->mAudioFile, This->mUseCache, &bytesRead, This->mPacketDescs, This->mPacketMark, &readPackets, readBuffer->mBuffers[0].mData);
 
1014
#if CAAUDIOFILE_PROFILE
 
1015
        if (This->mInConverter) ElapsedTime(This, readinconv, This->mTicksInReadInConverter);
 
1016
#endif
 
1017
        ElapsedTime(This, read, This->mTicksInIO);
 
1018
 
 
1019
        if (err) {
 
1020
                DebugMessageN1("Error %ld from AudioFileReadPackets!!!\n", err);
 
1021
                return err;
 
1022
        }
 
1023
        
 
1024
#if VERBOSE_IO
 
1025
        printf("CAAudioFile::ReadInputProc: read %ld packets (%qd-%qd), %ld bytes, err %ld\n", readPackets, This->mPacketMark, This->mPacketMark + readPackets, bytesRead, err);
 
1026
#if VERBOSE_IO >= 2
 
1027
        if (This->mPacketDescs) {
 
1028
                for (UInt32 i = 0; i < readPackets; ++i) {
 
1029
                        printf("  read packet %qd : offset %qd, length %ld\n", This->mPacketMark + i, This->mPacketDescs[i].mStartOffset, This->mPacketDescs[i].mDataByteSize);
 
1030
                }
 
1031
        }
 
1032
        printf("  read buffer:"); CAShowAudioBufferList(readBuffer, 0, 4);
 
1033
#endif
 
1034
#endif
 
1035
        if (readPackets == 0) {
 
1036
                *ioNumberDataPackets = 0;
 
1037
                ioData->mBuffers[0].mDataByteSize = 0;
 
1038
                return noErr;
 
1039
        }
 
1040
 
 
1041
        if (outDataPacketDescription)
 
1042
                *outDataPacketDescription = This->mPacketDescs;
 
1043
        ioData->mBuffers[0].mDataByteSize = bytesRead;
 
1044
        ioData->mBuffers[0].mData = readBuffer->mBuffers[0].mData;
 
1045
 
 
1046
        This->mPacketMark += readPackets;
 
1047
        if (This->mClientDataFormat.mFramesPerPacket != 1) {    // for PCM client formats we update in Read
 
1048
                // but for non-PCM client format (weird case) we must update here/now
 
1049
                if (This->mFileDataFormat.mFramesPerPacket > 0)
 
1050
                        This->mFrameMark += readPackets * This->mFileDataFormat.mFramesPerPacket;
 
1051
                else {
 
1052
                        for (UInt32 i = 0; i < readPackets; ++i)
 
1053
                                This->mFrameMark += This->mPacketDescs[i].mVariableFramesInPacket;
 
1054
                }
 
1055
        }
 
1056
        *ioNumberDataPackets = readPackets;
 
1057
        return noErr;
 
1058
}
 
1059
 
 
1060
// _______________________________________________________________________________________
 
1061
//
 
1062
void    CAAudioFile::Write(UInt32 numPackets, const AudioBufferList *data)
 
1063
{
 
1064
        if (mIOBufferList.mBuffers[0].mData == NULL) {
 
1065
#if DEBUG
 
1066
                printf("warning: CAAudioFile::AllocateBuffers called from WritePackets\n");
 
1067
#endif
 
1068
                AllocateBuffers();
 
1069
        }
 
1070
 
 
1071
        if (mMode == kPreparingToWrite)
 
1072
                mMode = kWriting;
 
1073
        else
 
1074
                XThrowIf(mMode != kWriting, kExtAudioFileError_InvalidOperationOrder, "can't write to this file");
 
1075
        if (mConverter != NULL) {
 
1076
                mWritePackets = numPackets;
 
1077
                mWriteBufferList->SetFrom(data);
 
1078
                WritePacketsFromCallback(WriteInputProc, this);
 
1079
        } else {
 
1080
                StartTiming(this, write);
 
1081
                XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, data->mBuffers[0].mDataByteSize, 
 
1082
                                                NULL, mPacketMark, &numPackets, data->mBuffers[0].mData),
 
1083
                                                "write audio file");
 
1084
                ElapsedTime(this, write, mTicksInIO);
 
1085
#if VERBOSE_IO
 
1086
                printf("CAAudioFile::WritePackets: wrote %ld packets at %qd, %ld bytes\n", numPackets, mPacketMark, data->mBuffers[0].mDataByteSize);
 
1087
#endif
 
1088
                //mNumberPackets = 
 
1089
                mPacketMark += numPackets;
 
1090
                if (mFileDataFormat.mFramesPerPacket > 0)
 
1091
                        mFrameMark += numPackets * mFileDataFormat.mFramesPerPacket;
 
1092
                // else: shouldn't happen since we're only called when there's no converter
 
1093
        }
 
1094
}
 
1095
 
 
1096
// _______________________________________________________________________________________
 
1097
//
 
1098
void    CAAudioFile::FlushEncoder()
 
1099
{
 
1100
        if (mConverter != NULL) {
 
1101
                mFinishingEncoding = true;
 
1102
                WritePacketsFromCallback(WriteInputProc, this);
 
1103
                mFinishingEncoding = false;
 
1104
 
 
1105
                // get priming info from converter, set it on the file
 
1106
                if (mFileDataFormat.mBitsPerChannel == 0) {
 
1107
                        UInt32 propertySize;
 
1108
                        OSStatus err;
 
1109
                        AudioConverterPrimeInfo primeInfo;
 
1110
                        propertySize = sizeof(primeInfo);
 
1111
        
 
1112
                        err = AudioConverterGetProperty(mConverter, kAudioConverterPrimeInfo, &propertySize, &primeInfo);
 
1113
                        if (err == noErr) {
 
1114
                                AudioFilePacketTableInfo pti;
 
1115
                                propertySize = sizeof(pti);
 
1116
                                err = AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, &propertySize, &pti);
 
1117
                                if (err == noErr) {
 
1118
//printf("old packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
 
1119
                                        UInt64 totalFrames = pti.mNumberValidFrames + pti.mPrimingFrames + pti.mRemainderFrames;
 
1120
                                        pti.mPrimingFrames = primeInfo.leadingFrames;
 
1121
                                        pti.mRemainderFrames = primeInfo.trailingFrames;
 
1122
                                        pti.mNumberValidFrames = totalFrames - pti.mPrimingFrames - pti.mRemainderFrames;
 
1123
//printf("new packet table info: %qd valid, %ld priming, %ld remainder\n", pti.mNumberValidFrames, pti.mPrimingFrames, pti.mRemainderFrames);
 
1124
                                        XThrowIfError(AudioFileSetProperty(mAudioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti), "couldn't set packet table info on audio file");
 
1125
                                }
 
1126
                        }
 
1127
                }
 
1128
        }
 
1129
}
 
1130
 
 
1131
// _______________________________________________________________________________________
 
1132
//
 
1133
OSStatus CAAudioFile::WriteInputProc(   AudioConverterRef                               /*inAudioConverter*/,
 
1134
                                                                                UInt32 *                                                ioNumberDataPackets,
 
1135
                                                                                AudioBufferList*                                ioData,
 
1136
                                                                                AudioStreamPacketDescription ** outDataPacketDescription,
 
1137
                                                                                void*                                                   inUserData)
 
1138
{
 
1139
        CAAudioFile *This = static_cast<CAAudioFile *>(inUserData);
 
1140
        if (This->mFinishingEncoding) {
 
1141
                *ioNumberDataPackets = 0;
 
1142
                ioData->mBuffers[0].mDataByteSize = 0;
 
1143
                ioData->mBuffers[0].mData = NULL;
 
1144
                if (outDataPacketDescription)
 
1145
                        *outDataPacketDescription = NULL;
 
1146
                return noErr;
 
1147
        }
 
1148
        UInt32 numPackets = This->mWritePackets;
 
1149
        if (numPackets == 0) {
 
1150
                return kNoMoreInputRightNow;
 
1151
        }
 
1152
        This->mWriteBufferList->ToAudioBufferList(ioData);
 
1153
        This->mWriteBufferList->BytesConsumed(numPackets * This->mClientDataFormat.mBytesPerFrame);
 
1154
        *ioNumberDataPackets = numPackets;
 
1155
        if (outDataPacketDescription)
 
1156
                *outDataPacketDescription = NULL;
 
1157
        This->mWritePackets -= numPackets;
 
1158
        return noErr;
 
1159
}
 
1160
 
 
1161
// _______________________________________________________________________________________
 
1162
//
 
1163
#if VERBOSE_IO
 
1164
static void     hexdump(const void *addr, long len)
 
1165
{
 
1166
        const Byte *p = (Byte *)addr;
 
1167
        UInt32 offset = 0;
 
1168
        
 
1169
        if (len > 0x400) len = 0x400;
 
1170
        
 
1171
        while (len > 0) {
 
1172
                int n = len > 16 ? 16 : len;
 
1173
                printf("%08lX:  ", offset);
 
1174
                for (int i = 0; i < 16; ++i)
 
1175
                        if (i < n)
 
1176
                                printf("%02X ", p[i]);
 
1177
                        else printf("   ");
 
1178
                for (int i = 0; i < 16; ++i)
 
1179
                        if (i < n)
 
1180
                                putchar(p[i] >= ' ' && p[i] < 127 ? p[i] : '.');
 
1181
                        else putchar(' ');
 
1182
                putchar('\n');
 
1183
                p += 16;
 
1184
                len -= 16;
 
1185
                offset += 16;
 
1186
        }
 
1187
}
 
1188
#endif
 
1189
 
 
1190
// _______________________________________________________________________________________
 
1191
//
 
1192
void    CAAudioFile::WritePacketsFromCallback(
 
1193
                                                                AudioConverterComplexInputDataProc      inInputDataProc,
 
1194
                                                                void *                                                          inInputDataProcUserData)
 
1195
{
 
1196
        while (true) {
 
1197
                // keep writing until we exhaust the input (temporary stop), or produce no output (EOF)
 
1198
                UInt32 numEncodedPackets = mIOBufferSizePackets;
 
1199
                mIOBufferList.mBuffers[0].mDataByteSize = mIOBufferSizeBytes;
 
1200
#if CAAUDIOFILE_PROFILE
 
1201
                mInConverter = true;
 
1202
#endif
 
1203
                StartTiming(this, fill);
 
1204
                OSStatus err = AudioConverterFillComplexBuffer(mConverter, inInputDataProc, inInputDataProcUserData, 
 
1205
                                        &numEncodedPackets, &mIOBufferList, mPacketDescs);
 
1206
                ElapsedTime(this, fill, mTicksInConverter);
 
1207
#if CAAUDIOFILE_PROFILE
 
1208
                mInConverter = false;
 
1209
#endif
 
1210
                XThrowIf(err != 0 && err != kNoMoreInputRightNow, err, "convert audio packets (write)");
 
1211
                if (numEncodedPackets == 0)
 
1212
                        break;
 
1213
                Byte *buf = (Byte *)mIOBufferList.mBuffers[0].mData;
 
1214
#if VERBOSE_IO
 
1215
                printf("CAAudioFile::WritePacketsFromCallback: wrote %ld packets, %ld bytes\n", numEncodedPackets, mIOBufferList.mBuffers[0].mDataByteSize);
 
1216
                if (mPacketDescs) {
 
1217
                        for (UInt32 i = 0; i < numEncodedPackets; ++i) {
 
1218
                                printf("  write packet %qd : offset %qd, length %ld\n", mPacketMark + i, mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
 
1219
#if VERBOSE_IO >= 2
 
1220
                                hexdump(buf + mPacketDescs[i].mStartOffset, mPacketDescs[i].mDataByteSize);
 
1221
#endif
 
1222
                        }
 
1223
                }
 
1224
#endif
 
1225
                StartTiming(this, write);
 
1226
                XThrowIfError(AudioFileWritePackets(mAudioFile, mUseCache, mIOBufferList.mBuffers[0].mDataByteSize, mPacketDescs, mPacketMark, &numEncodedPackets, buf), "write audio file");
 
1227
                ElapsedTime(this, write, mTicksInIO);
 
1228
                mPacketMark += numEncodedPackets;
 
1229
                //mNumberPackets += numEncodedPackets;
 
1230
                if (mFileDataFormat.mFramesPerPacket > 0)
 
1231
                        mFrameMark += numEncodedPackets * mFileDataFormat.mFramesPerPacket;
 
1232
                else {
 
1233
                        for (UInt32 i = 0; i < numEncodedPackets; ++i)
 
1234
                                mFrameMark += mPacketDescs[i].mVariableFramesInPacket;
 
1235
                }
 
1236
                if (err == kNoMoreInputRightNow)
 
1237
                        break;
 
1238
        }
 
1239
}
 
1240
 
 
1241
#endif // !CAAF_USE_EXTAUDIOFILE