/ src / CoreAudio / AudioFileTools / auprocess.cpp
auprocess.cpp
  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  	main.cpp
 40  	
 41  =============================================================================*/
 42  
 43  /*
 44  	auprocess
 45  		- takes a source audio file, an AU and generates a processed file 
 46  */
 47  
 48  #include "CAAUProcessor.h"
 49  #include "CAAudioFile.h"
 50  #include "CAXException.h"
 51  #include "CAHostTimeBase.h"
 52  #include "CAFilePathUtils.h"
 53  #include "CAAudioFileFormats.h"
 54  
 55  #if TARGET_OS_MAC
 56  	#include <pthread.h>
 57  	#include <mach/mach.h>
 58  #endif
 59  
 60  #define require_noerr ca_require_noerr
 61  
 62  #define CA_AU_PROFILE_TIME 1
 63  
 64  #if CA_AU_PROFILE_TIME
 65  			UInt64 sReadTime = 0;
 66  			UInt64 sRenderTime = 0;
 67  #endif
 68  
 69  #pragma mark __print helpers
 70  
 71  void PRINT_MARKS ()
 72  {
 73  	printf ("| ");
 74  	for (int i = 0; i < 48; ++i)
 75  		printf (" ");
 76  	printf ("|\n");
 77  }
 78  
 79  void PerfResult(const char *toolname, int group, const char *testname, double value, const char *units, const char *fmt="%.3f")
 80  {
 81  	printf("<result tool='%s' group='%d' test='%s' value='", toolname, group, testname);
 82  	printf(fmt, value);
 83  	printf("' units='%s' />\n", units);
 84  }
 85  
 86  static int lastProgressPrintDone = -1;
 87  void PRINT_PROGRESS (Float32 inPercent) 
 88  {
 89  	int current = int(inPercent / 4.0);
 90  	for (int i = lastProgressPrintDone; i < current; ++i)
 91  		printf ("* ");
 92  	lastProgressPrintDone = current;
 93  }
 94  
 95  #pragma mark __Inpput Callback Definitions
 96  
 97  static AURenderCallbackStruct sInputCallback; // we set one of these two callbacks based on AU type
 98  
 99  static OSStatus InputCallback (void 			*inRefCon, 
100  					AudioUnitRenderActionFlags 	*ioActionFlags, 
101  					const AudioTimeStamp 		*inTimeStamp, 
102  					UInt32 						inBusNumber, 
103  					UInt32 						inNumberFrames, 
104  					AudioBufferList 			*ioData)
105  {
106  													#if CA_AU_PROFILE_TIME 
107  														UInt64 now = CAHostTimeBase::GetTheCurrentTime(); 
108  													#endif
109  
110  	CAAudioFile &readFile = *(static_cast<CAAudioFile*>(inRefCon));
111  
112  #if !CAAF_USE_EXTAUDIOFILE
113  	if (SInt64(inTimeStamp->mSampleTime) > readFile.GetNumberPackets()) {
114  #else
115  	if (SInt64(inTimeStamp->mSampleTime) > readFile.GetNumberFrames()) {
116  #endif
117  #if DEBUG
118  	printf ("reading past end of input\n");
119  #endif
120  		return -1;
121  	}
122  
123  	readFile.Seek (SInt64(inTimeStamp->mSampleTime));
124  	readFile.Read (inNumberFrames, ioData);
125  
126  													#if CA_AU_PROFILE_TIME 
127  														sReadTime += (CAHostTimeBase::GetTheCurrentTime() - now); 
128  													#endif
129  
130  	return noErr;
131  }
132  
133  static OSStatus FConvInputCallback (void 			*inRefCon, 
134  					AudioUnitRenderActionFlags 	*ioActionFlags, 
135  					const AudioTimeStamp 		*inTimeStamp, 
136  					UInt32 						inBusNumber, 
137  					UInt32 						inNumberFrames, 
138  					AudioBufferList 			*ioData)
139  {
140  												#if CA_AU_PROFILE_TIME 
141  													UInt64 now = CAHostTimeBase::GetTheCurrentTime(); 
142  												#endif
143  
144  	CAAudioFile &readFile = *(static_cast<CAAudioFile*>(inRefCon));
145  
146  		// this test is ONLY needed in case of processing with a Format Converter type of AU
147  		// in all other cases, the CAAUProcessor class will NEVER call you for input
148  		// beyond the end of the file....
149  
150  #if !CAAF_USE_EXTAUDIOFILE
151  	if (SInt64(inTimeStamp->mSampleTime) >= readFile.GetNumberPackets()) {
152  #else
153  	if (SInt64(inTimeStamp->mSampleTime) >= readFile.GetNumberFrames()) {
154  #endif
155  		return -1;
156  	}
157  	
158  	readFile.Seek (SInt64(inTimeStamp->mSampleTime));
159  	UInt32 readPackets = inNumberFrames;
160  		
161  		// also, have to do this for a format converter AU - otherwise we'd just read what we're told
162  #if !CAAF_USE_EXTAUDIOFILE
163  	if (SInt64(inTimeStamp->mSampleTime + inNumberFrames) > readFile.GetNumberPackets()) {
164  #else
165  	if (SInt64(inTimeStamp->mSampleTime + inNumberFrames) > readFile.GetNumberFrames()) {
166  #endif
167  		// first set this to zero as we're only going to read a partial number of frames
168  		AudioBuffer *buf = ioData->mBuffers;
169  		for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
170  			memset((Byte *)buf->mData, 0, buf->mDataByteSize);
171  #if !CAAF_USE_EXTAUDIOFILE
172  		readPackets = UInt32 (readFile.GetNumberPackets() - SInt64(inTimeStamp->mSampleTime));
173  #else
174  		readPackets = UInt32 (readFile.GetNumberFrames() - SInt64(inTimeStamp->mSampleTime));
175  #endif
176  	}
177  	
178  	readFile.Read (readPackets, ioData);
179  
180  													#if CA_AU_PROFILE_TIME 
181  														sReadTime += (CAHostTimeBase::GetTheCurrentTime() - now); 
182  													#endif
183  
184  	return noErr;
185  }
186  
187  struct ReadBuffer {
188  	AUOutputBL *readData;
189  	UInt32 readFrames;
190  };
191  
192  static OSStatus MemoryInputCallback (void		*inRefCon, 
193  					AudioUnitRenderActionFlags 	*ioActionFlags, 
194  					const AudioTimeStamp 		*inTimeStamp, 
195  					UInt32 						inBusNumber, 
196  					UInt32 						inNumberFrames, 
197  					AudioBufferList 			*ioData)
198  {
199  													#if CA_AU_PROFILE_TIME 
200  														UInt64 now = CAHostTimeBase::GetTheCurrentTime(); 
201  													#endif
202  
203  	ReadBuffer *readBuffer = (ReadBuffer*)inRefCon;
204  	
205  	if (((readBuffer->readFrames + inNumberFrames) * sizeof(Float32)) > (readBuffer->readData->ABL()->mBuffers[0].mDataByteSize)) 
206  	{
207  		// going past read size
208  		AudioBuffer *buf = ioData->mBuffers;
209  		for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
210  			memset((Byte *)buf->mData, 0, buf->mDataByteSize);
211  	}
212  	else
213  	{
214  		AudioBuffer *buf = ioData->mBuffers;
215  		AudioBuffer *rBuf = readBuffer->readData->ABL()->mBuffers;
216  		for (UInt32 i = ioData->mNumberBuffers; i--; ++buf, ++rBuf) {
217  			AudioBuffer readB = *rBuf;
218  			readB.mData = static_cast<Float32*>(rBuf->mData) + readBuffer->readFrames;
219  			memcpy (buf->mData, readB.mData, buf->mDataByteSize);
220  		}
221  		readBuffer->readFrames += inNumberFrames;
222  	}
223  
224  													#if CA_AU_PROFILE_TIME 
225  														sReadTime += (CAHostTimeBase::GetTheCurrentTime() - now); 
226  													#endif
227  
228  	return noErr;
229  }
230  
231  #pragma mark __Utility Helpers
232  
233  CFPropertyListRef	 ReadPresetFromPresetFile (char* filePath)
234  {	
235  	if (!filePath)
236  		return NULL;
237  	
238  	FSRef ref;
239  	if (FSPathMakeRef((UInt8 *)filePath, &ref, NULL))
240  		return NULL;
241  		
242  	CFDataRef			resourceData = NULL;
243  	CFPropertyListRef   theData = NULL;
244  	CFStringRef			errString = NULL;
245  	CFURLRef			fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, &ref);
246  		if (fileURL == NULL) {
247  			goto home;
248  		}
249  		
250  	SInt32				result;
251      
252     // Read the XML file.
253     Boolean status; status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL,
254                                                                  &resourceData,	// place to put file data
255                                                                  NULL, NULL, &result);
256          if (status == false || result) {
257              goto home;
258          }
259      
260  	theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData,  
261  													kCFPropertyListImmutable, &errString);
262          if (theData == NULL || errString) {
263              if (theData)
264  				CFRelease (theData);
265  			theData = NULL;
266  			goto home;
267         }
268  	
269  home:
270  	if (fileURL)
271  		CFRelease (fileURL);
272  	if (resourceData)
273  		CFRelease (resourceData);
274      if (errString)
275  		CFRelease (errString);
276  		
277  	return theData;
278  }
279  
280  #pragma mark __the setup code
281  
282  #define OFFLINE_AU_CMD 		"[-au TYPE SUBTYPE MANU] The Audio Unit component description\n\t"
283  #define INPUT_FILE	 		"[-i /Path/To/File] The file that is to be processed.\n\t"
284  #define OUTPUT_FILE			"[-o /Path/To/File/To/Create] This will be in the same format as the input file\n\t"
285  #define AU_PRESET_CMD		"[-p /Path/To/AUPreset/File] Specify an AU Preset File to establish the state of the AU\n\t"
286  #define SHORT_MEM_CMD		"[-m] Just reads and processes the first half second of the input file\n\t"
287  #define USE_MAX_FRAMES		"[-f max_frames] default is 32768 (512 for aufc units)"
288   
289  static char* usageStr = "Usage: auprocess\n\t" 
290  				OFFLINE_AU_CMD 
291  				INPUT_FILE
292  				OUTPUT_FILE
293  				AU_PRESET_CMD
294  				SHORT_MEM_CMD
295  				USE_MAX_FRAMES;
296  
297  int main(int argc, const char * argv[])
298  {
299  	setbuf (stdout, NULL);
300  
301  
302  #if TARGET_OS_MAC
303  	{
304  		thread_extended_policy_data_t		theFixedPolicy;
305  		theFixedPolicy.timeshare = false;	// set to true for a non-fixed thread
306  		thread_policy_set(pthread_mach_thread_np(pthread_self()), 
307  													THREAD_EXTENDED_POLICY, 
308  													(thread_policy_t)&theFixedPolicy, 
309  													THREAD_EXTENDED_POLICY_COUNT);
310  
311  		// We keep a reference to the spawning thread's priority around (initialized in the constructor), 
312  		// and set the importance of the child thread relative to the spawning thread's priority.
313  		thread_precedence_policy_data_t		thePrecedencePolicy;
314  		
315  		thePrecedencePolicy.importance = 63 - 36;
316  		thread_policy_set(pthread_mach_thread_np(pthread_self()), 
317  													THREAD_PRECEDENCE_POLICY, 
318  													(thread_policy_t)&thePrecedencePolicy, 
319  													THREAD_PRECEDENCE_POLICY_COUNT);
320  	}
321  #endif
322  
323  
324  // These are the variables that are set up from the input parsing
325  	char* srcFilePath = NULL;
326  	char* destFilePath = NULL;
327  	char* auPresetFile = NULL;
328  	bool shortMemoryProfile = false;
329  	OSType manu, subType, type = 0;
330  	int userSetFrames = -1;
331  	
332  	for (int i = 1; i < argc; ++i)
333  	{
334  		if (strcmp (argv[i], "-au") == 0) {
335              if ( (i + 3) < argc ) {                
336                  StrToOSType (argv[i + 1], type);
337                  StrToOSType (argv[i + 2], subType);
338                  StrToOSType (argv[i + 3], manu);
339  				i += 3;
340  			} else {
341  				printf ("Which Audio Unit:\n%s", usageStr);
342  				exit(1);
343  			}
344  		}
345  		else if (strcmp (argv[i], "-i") == 0) {
346  			srcFilePath = const_cast<char*>(argv[++i]);
347  		}
348  		else if (strcmp (argv[i], "-o") == 0) {
349  			destFilePath = const_cast<char*>(argv[++i]);
350  		}
351  		else if (strcmp (argv[i], "-p") == 0) {
352  			auPresetFile = const_cast<char*>(argv[++i]);
353  		}
354  		else if (strcmp (argv[i], "-m") == 0) {
355  			shortMemoryProfile = true;
356  		}
357  		else if (strcmp (argv[i], "-f") == 0) {
358  			sscanf(argv[++i], "%d", &userSetFrames);
359  		}
360  		else {
361  			printf ("%s\n", usageStr);
362  			exit(1);
363  		}
364  	}
365  	
366  	if (!type || !srcFilePath) {
367  		printf ("%s\n", usageStr);
368  		exit(1);
369  	}
370  	if (!destFilePath) {
371  		if (!shortMemoryProfile) {
372  			printf ("%s\n", usageStr);
373  			exit(1);
374  		}
375  	}
376  			// delete pre-existing output file
377  	if (!shortMemoryProfile) {
378  		FSRef destFSRef;
379  		if (FSPathMakeRef((UInt8 *)destFilePath, &destFSRef, NULL) == noErr) {
380  			// output file exists - delete it
381  			if (FSDeleteObject(&destFSRef)) {
382  				printf ("Cannot Delete Output File\n");
383  				exit(1);
384  			}
385  		}
386  	}
387  	
388  	CAComponentDescription desc(type, subType, manu);
389  	
390  	CFPropertyListRef presetDict = ReadPresetFromPresetFile(auPresetFile);
391  	
392  		// the num of frames to use when processing the file with the Render call
393  	UInt32 maxFramesToUse = shortMemoryProfile ? 512 : 32768;
394  
395  		// not set from command line
396  	if (userSetFrames > 0) {
397  		maxFramesToUse = userSetFrames; 
398  	}
399  		
400  		// in some settings (for instance a delay with 100% feedback) tail time is essentially infinite
401  		// so you should safeguard the final OL render stage (post process) which is aimed at pulling the tail through
402  		// if you want to bypass this completely, just set this to zero.
403  	Float64 maxTailTimeSecs = 10.;
404  	
405  #pragma mark -
406  #pragma mark __ The driving code
407  #pragma mark -
408  
409  	try 
410  	{
411  		CAComponent comp(desc);
412  			
413  			 // CAAUProcessor's constructor throws... so make sure the component is valid
414  		if (comp.IsValid() == false) {
415  			printf ("Can't Find Component\n");
416  			desc.Print();
417  			exit(1);
418  		}
419  			
420  		CAAUProcessor processor(comp);
421  													processor.AU().Print();
422  		
423  		CAAudioFile srcFile;
424  		CAAudioFile destFile; 
425  		
426  		srcFile.Open(srcFilePath);
427  
428  		CAStreamBasicDescription procFormat (srcFile.GetFileDataFormat());
429  		procFormat.SetCanonical (srcFile.GetFileDataFormat().NumberChannels(), false);
430  
431  													printf ("Processing Format:\n\t");
432  													procFormat.Print();
433  		
434  		
435  		if (!shortMemoryProfile) {
436  			FSRef parentDir;
437  			CFStringRef filename;
438  			PosixPathToParentFSRefAndName(destFilePath, parentDir, filename);
439  			destFile.CreateNew (parentDir, filename, 'AIFF', srcFile.GetFileDataFormat());
440  			destFile.SetClientFormat (procFormat);
441  		}
442  	
443  		srcFile.SetClientFormat (procFormat);
444  		
445  		AUOutputBL outputList(procFormat);
446  
447  		ReadBuffer* readBuf = NULL;	
448  
449  #if !CAAF_USE_EXTAUDIOFILE
450  		UInt64 numInputSamples = srcFile.GetNumberPackets();
451  #else
452  		UInt64 numInputSamples = srcFile.GetNumberFrames();
453  #endif
454  	
455  		if (shortMemoryProfile) {
456  			readBuf = new ReadBuffer;
457  			readBuf->readData = new AUOutputBL(procFormat);
458  			readBuf->readFrames = 0;
459  			UInt32 numFrames = UInt32(procFormat.mSampleRate / 2);
460  			readBuf->readData->Allocate (numFrames); // half a second of audio data
461  			readBuf->readData->Prepare(); // half a second of audio data
462  				
463  				// read 1/2 second of audio into this read buffer
464  			srcFile.Read (numFrames, readBuf->readData->ABL());
465  			
466  			sInputCallback.inputProc = MemoryInputCallback;
467  			sInputCallback.inputProcRefCon = readBuf;
468  			numInputSamples = numFrames;
469  		}
470  		else {
471  			if (desc.IsFConv()) {
472  				maxFramesToUse = userSetFrames == -1 ? 512 : maxFramesToUse; 
473  				// some format converter's can call you several times in small granularities
474  				// so you can't use a large buffer to render or you won't return all of the input data
475  				// this also lessens the final difference between what you should get and what you do
476  				// converter units *really* should have a version that are offline AU's to 
477  				// handle this for you.
478  				sInputCallback.inputProc = FConvInputCallback;
479  			} else
480  				sInputCallback.inputProc = InputCallback;
481  			
482  			sInputCallback.inputProcRefCon = &srcFile;
483  		}
484  				
485  		OSStatus result;
486  		require_noerr (result = processor.EstablishInputCallback (sInputCallback), home);
487  		require_noerr (result = processor.SetMaxFramesPerRender (maxFramesToUse), home); 
488  		processor.SetMaxTailTime (maxTailTimeSecs);
489  		require_noerr (result = processor.Initialize (procFormat, numInputSamples), home);
490  		if (presetDict) {
491  			require_noerr (result = processor.SetAUPreset (presetDict), home);
492  			CFRelease (presetDict);
493  		}
494  			// this does ALL of the preflighting.. could be specialise for an OfflineAU type
495  			// to do this piecemeal and do a progress bar by using the OfflineAUPreflight method
496  		require_noerr (result = processor.Preflight (), home);
497  		
498  		bool isDone; isDone = false;
499  		bool needsPostProcessing;
500  		bool isSilence;
501  		UInt32 numFrames; numFrames = processor.MaxFramesPerRender();
502  
503  #if CA_AU_PROFILE_TIME
504  		sReadTime = 0;
505  		sRenderTime = 0;
506  #endif
507  					
508  PRINT_MARKS();
509  			// this is the render loop
510  		while (!isDone) 
511  		{
512  											#if CA_AU_PROFILE_TIME 
513  												UInt64 now = CAHostTimeBase::GetTheCurrentTime(); 
514  											#endif
515  			outputList.Prepare(); // have to do this every time...
516  			require_noerr (result = processor.Render (outputList.ABL(), numFrames, isSilence, &isDone,
517  											&needsPostProcessing), home);
518  											#if CA_AU_PROFILE_TIME 
519  												sRenderTime += (CAHostTimeBase::GetTheCurrentTime() - now);
520  											#endif
521  
522  if (!shortMemoryProfile)
523  	PRINT_PROGRESS(processor.GetOLPercentComplete());
524  else
525  	PRINT_PROGRESS(((processor.SampleTime() / numInputSamples) * 100.));
526  	
527  			if (numFrames && !shortMemoryProfile)
528  				destFile.Write (numFrames, outputList.ABL());
529  		}
530  			
531  			// this is the postprocessing if needed
532  		if (!shortMemoryProfile && needsPostProcessing) 
533  		{
534  			isDone = false;
535  			numFrames = processor.MaxFramesPerRender();
536  			while (!isDone) {
537  				outputList.Prepare(); // have to do this every time...
538  											#if CA_AU_PROFILE_TIME 
539  												UInt64 now = CAHostTimeBase::GetTheCurrentTime(); 
540  											#endif
541  				require_noerr (result = processor.PostProcess (outputList.ABL(), numFrames, 
542  													isSilence, isDone), home);
543  											#if CA_AU_PROFILE_TIME 
544  												sRenderTime += (CAHostTimeBase::GetTheCurrentTime() - now); 
545  											#endif
546  
547  PRINT_PROGRESS(processor.GetOLPercentComplete());
548  
549  				if (numFrames && !shortMemoryProfile)
550  					destFile.Write (numFrames, outputList.ABL());
551  			}
552  		}
553  
554  printf ("\n");
555  
556  home:
557  		if (result) {
558  			printf ("Exit with bad result:%ld\n", result);
559  			exit(result);
560  		}
561  		
562  		if (readBuf) {
563  			delete readBuf->readData;
564  			delete readBuf;
565  		}
566  					
567  #if CA_AU_PROFILE_TIME
568  	if (!shortMemoryProfile) {
569  			// this flushes any remaing data to be written to the disk. 
570  			// the source file is closed in its destructor of course
571  		destFile.Close(); 
572  			// open the file again, to get stats about it for profiling
573  		destFile.Open(destFilePath);
574  	}
575  
576  	SInt64 numWritten;
577  	if (shortMemoryProfile)
578  		numWritten = 0;
579  	else {
580  #if !CAAF_USE_EXTAUDIOFILE
581  		numWritten = destFile.GetNumberPackets();
582  #else
583  		numWritten = destFile.GetNumberFrames();
584  #endif
585  	}
586  
587  	printf ("Read File Time:%.2f secs for %lld packets (%.1f secs), wrote %lld packets\n", 
588  						(CAHostTimeBase::ConvertToNanos (sReadTime) / 1.0e9),
589  						numInputSamples,
590  						(numInputSamples / procFormat.mSampleRate),
591  						numWritten);
592  
593  	if (!shortMemoryProfile) 
594  	{
595  #if !CAAF_USE_EXTAUDIOFILE
596  		UInt64 numOutputSamples = destFile.GetNumberPackets();
597  #else
598  		UInt64 numOutputSamples = destFile.GetNumberFrames();
599  #endif
600  	
601  		if (numOutputSamples == numInputSamples) {
602  			printf ("\tWrote the same number of packets as read\n");
603  		} else {
604  			bool expectationMet = !desc.IsOffline(); // we don't have any expectations for offline AU's
605  			if (processor.LatencySampleCount() || processor.TailSampleCount()) {
606  				if (numOutputSamples - numInputSamples == processor.TailSampleCount())
607  					expectationMet = true;
608  				if (expectationMet)	
609  					printf ("Correctly wrote \'Read Size + Tail\'. ");
610  				printf ("AU reports (samples): %ld latency, %ld tail\n", 
611  										processor.LatencySampleCount(), processor.TailSampleCount());
612  			}
613  			if (expectationMet == false) 
614  			{
615  				if (numOutputSamples > numInputSamples) {
616  					printf ("\tWrote %lld packets (%.2f secs) more than read\n", 
617  								(numOutputSamples - numInputSamples), 
618  								((numOutputSamples - numInputSamples) / procFormat.mSampleRate));
619  				} else {
620  					printf ("\tRead %lld packets (%.2f secs) more than wrote\n", 
621  								(numInputSamples - numOutputSamples), 
622  								((numInputSamples - numOutputSamples) / procFormat.mSampleRate));
623  				}
624  			}
625  		}
626  	}
627  	
628  	Float64 renderTimeSecs = CAHostTimeBase::ConvertToNanos (sRenderTime - sReadTime) / 1.0e9;
629  	printf ("Total Render Time:%.2f secs, using render slice size of %ld frames\n", 
630  							renderTimeSecs, maxFramesToUse);
631  	
632  	Float64 cpuUsage;
633  	if (shortMemoryProfile)
634  		cpuUsage = (renderTimeSecs / 0.5) * 100.;
635  	else
636  		cpuUsage = (renderTimeSecs / (numInputSamples / procFormat.mSampleRate)) * 100.;
637  	printf ("CPU Usage for Render Time:%.2f%%\n", cpuUsage);
638  
639  	CFStringRef str = comp.GetCompName();
640  	UInt32 compNameLen = CFStringGetLength (str);
641  	
642  	CFStringRef presetName = NULL;
643  	if (auPresetFile) {
644  		CFPropertyListRef dict;
645  		if (processor.AU().GetAUPreset (dict) == noErr) {
646  			presetName = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)dict, CFSTR("name"));
647  			CFRelease (dict);
648  		}
649  	}
650  
651  	UInt32 presetLen = presetName ? CFStringGetLength(presetName) : 0;
652  
653  	char* cstr = (char*)malloc (compNameLen + presetLen + 2 + 1);
654  	CFStringGetCString (str, cstr, (CFStringGetLength (str) + 1), kCFStringEncodingASCII);
655  	if (presetName) {
656  		cstr[compNameLen] = ':';
657  		cstr[compNameLen+1] = ':';
658  		CFStringGetCString (presetName, cstr + compNameLen + 2, (CFStringGetLength (presetName) + 1), kCFStringEncodingASCII);
659  	}
660  	PerfResult("AudioUnitProcess", EndianU32_NtoB(comp.Desc().componentSubType), cstr, cpuUsage, "%realtime");
661  	free (cstr);
662  #endif
663  
664  
665  	}
666  	catch (CAXException &e) {
667  		char buf[256];
668  		printf("Error: %s (%s)\n", e.mOperation, e.FormatError(buf, sizeof(buf)));
669  		exit(1);
670  	}
671  	catch (...) {
672  		printf("An unknown error occurred\n");
673  		exit(1);
674  	}
675  			
676  	return 0;
677  }
678