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:
FunctionLogger(const char * name,const char * fmt=NULL,...)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 		}
~FunctionLogger()84 		~FunctionLogger() {
85 			--sIndent;
86 			Indent();
87 			printf("<- %s\n", mName);
88 			if (sIndent == 0)
89 				printf("\n");
90 		}
91 
Indent()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 //
CAAudioFile()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 //
~CAAudioFile()153 CAAudioFile::~CAAudioFile()
154 {
155 	Close();
156 }
157 
158 // _______________________________________________________________________________________
159 //
Close()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 //
CloseConverter()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 //
Open(const FSRef & fsref)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 //
Wrap(AudioFileID fileID,bool forWriting)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 //
CreateNew(const FSRef & parentDir,CFStringRef filename,AudioFileTypeID filetype,const AudioStreamBasicDescription & dataFormat,const AudioChannelLayout * layout)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
FileFormatChanged(const FSRef * parentDir,CFStringRef filename,AudioFileTypeID filetype)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 //
InitFileMaxPacketSize()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 //
FileDataOffset()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 //
GetNumberFrames() const406 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 //
SetNumberFrames(SInt64 nFrames)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()
GetExistingFileInfo()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 //
SetFileChannelLayout(const CAAudioChannelLayout & layout)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 //
SetClientFormat(const CAStreamBasicDescription & dataFormat,const CAAudioChannelLayout * layout)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 //
SetConverterProperty(AudioConverterPropertyID inPropertyID,UInt32 inPropertyDataSize,const void * inPropertyData,bool inCanFail)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 //
SetConverterChannelLayout(bool output,const CAAudioChannelLayout & layout)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 //
GetConverterConfig()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 //
UpdateClientMaxPacketSize()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
AllocateBuffers(bool okToFail)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 //
SetIOBuffer(void * buf)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 
PacketToFrame(SInt64 packet) const765 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 
FrameToPacket(SInt64 inFrame) const783 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 
Tell() const804 SInt64  CAAudioFile::Tell() const	// frameNumber
805 {
806 	return mFrameMark - mFrame0Offset;
807 }
808 
SeekToPacket(SInt64 packetNumber)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 */
Seek(SInt64 clientFrame)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 //
Read(UInt32 & ioNumPackets,AudioBufferList * ioData)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 //
ReadInputProc(AudioConverterRef inAudioConverter,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)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 //
Write(UInt32 numPackets,const AudioBufferList * data)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 //
FlushEncoder()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 //
WriteInputProc(AudioConverterRef,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)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
hexdump(const void * addr,long len)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 //
WritePacketsFromCallback(AudioConverterComplexInputDataProc inInputDataProc,void * inInputDataProcUserData)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
1242