this repo has no description
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 auprofile.cpp
40
41=============================================================================*/
42
43/*
44 auprofile
45 - takes a source audio file, an AU and processes this for performance metrics
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#if DEBUG
61 #define VERBOSE 0
62#endif
63
64UInt64 sLastReadTime = 0;
65Float64 sMinTime = 9999999999.; // too big a time!
66Float64 sMaxTime = 0;
67
68#pragma mark __print helpers
69
70void PerfResult(const char *toolname, int group, const char *testname, double value, const char *units, const char *fmt="%.3f")
71{
72 printf("<result tool='%s' group='%d' test='%s' value='", toolname, group, testname);
73 printf(fmt, value);
74 printf("' units='%s' />\n", units);
75}
76
77#pragma mark __Inpput Callback Definitions
78
79
80// the file is read into memory - the input's role is to parse through this memory
81
82struct ReadBuffer {
83 AUOutputBL *readData;
84 UInt64 totalInputFrames;
85 UInt32 lastInputFrames;
86};
87
88const int kEndOfInput = 12345;
89
90static OSStatus MemoryInputCallback (void *inRefCon,
91 AudioUnitRenderActionFlags *ioActionFlags,
92 const AudioTimeStamp *inTimeStamp,
93 UInt32 inBusNumber,
94 UInt32 inNumberFrames,
95 AudioBufferList *ioData)
96{
97 UInt64 now = CAHostTimeBase::GetTheCurrentTime();
98 OSStatus result = 0;
99
100 ReadBuffer *readBuffer = (ReadBuffer*)inRefCon;
101 if (inTimeStamp->mSampleTime >= readBuffer->totalInputFrames) {
102 #if VERBOSE
103 printf ("reading: %.0f past input: %.0f\n", inTimeStamp->mSampleTime,
104 (double)readBuffer->totalInputFrames);
105 #endif
106 result = kEndOfInput;
107 readBuffer->lastInputFrames = 0;
108 goto end;
109 }
110
111 // can get pulled multiple times
112 readBuffer->lastInputFrames += inNumberFrames;
113
114 if (UInt64(inTimeStamp->mSampleTime + inNumberFrames) > readBuffer->totalInputFrames) {
115 // first set this to zero as we're only going to read a partial number of frames
116 AudioBuffer *buf = ioData->mBuffers;
117 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf)
118 memset((Byte *)buf->mData, 0, buf->mDataByteSize);
119
120 inNumberFrames = UInt32 (readBuffer->totalInputFrames - UInt64(inTimeStamp->mSampleTime));
121 }
122
123 // copy data from the source to the ioData buffers
124 {
125 AudioBuffer *buf = ioData->mBuffers;
126 AudioBuffer *rBuf = readBuffer->readData->ABL()->mBuffers;
127 for (UInt32 i = ioData->mNumberBuffers; i--; ++buf, ++rBuf) {
128 AudioBuffer readB = *rBuf;
129 readB.mData = static_cast<Float32*>(rBuf->mData) + UInt32(inTimeStamp->mSampleTime);
130 memcpy (buf->mData, readB.mData, (inNumberFrames * sizeof(Float32)));
131 }
132 }
133
134end:
135 sLastReadTime += (CAHostTimeBase::GetTheCurrentTime() - now);
136
137 return result;
138}
139
140#pragma mark __Utility Helpers
141
142CFPropertyListRef ReadPresetFromPresetFile (char* filePath)
143{
144 if (!filePath)
145 return NULL;
146
147 FSRef ref;
148 if (FSPathMakeRef((UInt8 *)filePath, &ref, NULL))
149 return NULL;
150
151 CFDataRef resourceData = NULL;
152 CFPropertyListRef theData = NULL;
153 CFStringRef errString = NULL;
154 CFURLRef fileURL = CFURLCreateFromFSRef (kCFAllocatorDefault, &ref);
155 if (fileURL == NULL) {
156 goto home;
157 }
158
159 SInt32 result;
160
161 // Read the XML file.
162 Boolean status; status = CFURLCreateDataAndPropertiesFromResource (kCFAllocatorDefault, fileURL,
163 &resourceData, // place to put file data
164 NULL, NULL, &result);
165 if (status == false || result) {
166 goto home;
167 }
168
169 theData = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, resourceData,
170 kCFPropertyListImmutable, &errString);
171 if (theData == NULL || errString) {
172 if (theData)
173 CFRelease (theData);
174 theData = NULL;
175 goto home;
176 }
177
178home:
179 if (fileURL)
180 CFRelease (fileURL);
181 if (resourceData)
182 CFRelease (resourceData);
183 if (errString)
184 CFRelease (errString);
185
186 return theData;
187}
188
189#pragma mark __the setup code
190
191#define OFFLINE_AU_CMD "[-au TYPE SUBTYPE MANU] The Audio Unit component description\n\t"
192#define INPUT_FILE "[-i /Path/To/File] The file that is to be processed.\n\t"
193#define AU_PRESET_CMD "[-p /Path/To/AUPreset/File] Specify an AU Preset File to establish the state of the AU\n\t"
194#define USE_MAX_FRAMES "[-f max_frames] default is 4096"
195
196static char* usageStr = "Usage: auprofile\n\t"
197 OFFLINE_AU_CMD
198 INPUT_FILE
199 AU_PRESET_CMD
200 USE_MAX_FRAMES;
201
202int main(int argc, const char * argv[])
203{
204#if TARGET_OS_MAC
205 {
206 thread_extended_policy_data_t theFixedPolicy;
207 theFixedPolicy.timeshare = false; // set to true for a non-fixed thread
208 thread_policy_set(pthread_mach_thread_np(pthread_self()),
209 THREAD_EXTENDED_POLICY,
210 (thread_policy_t)&theFixedPolicy,
211 THREAD_EXTENDED_POLICY_COUNT);
212
213 // We keep a reference to the spawning thread's priority around (initialized in the constructor),
214 // and set the importance of the child thread relative to the spawning thread's priority.
215 thread_precedence_policy_data_t thePrecedencePolicy;
216
217 thePrecedencePolicy.importance = 63 - 36;
218 thread_policy_set(pthread_mach_thread_np(pthread_self()),
219 THREAD_PRECEDENCE_POLICY,
220 (thread_policy_t)&thePrecedencePolicy,
221 THREAD_PRECEDENCE_POLICY_COUNT);
222 }
223#endif
224
225
226// These are the variables that are set up from the input parsing
227 char* srcFilePath = NULL;
228 char* auPresetFile = NULL;
229 OSType manu, subType, type = 0;
230 UInt32 numFrames = 4096;
231
232 for (int i = 1; i < argc; ++i)
233 {
234 if (strcmp (argv[i], "-au") == 0) {
235 if ( (i + 3) < argc ) {
236 StrToOSType (argv[i + 1], type);
237 StrToOSType (argv[i + 2], subType);
238 StrToOSType (argv[i + 3], manu);
239 i += 3;
240 } else {
241 printf ("Which Audio Unit:\n%s", usageStr);
242 exit(1);
243 }
244 }
245 else if (strcmp (argv[i], "-i") == 0) {
246 srcFilePath = const_cast<char*>(argv[++i]);
247 }
248 else if (strcmp (argv[i], "-p") == 0) {
249 auPresetFile = const_cast<char*>(argv[++i]);
250 }
251 else if (strcmp (argv[i], "-f") == 0) {
252 sscanf(argv[++i], "%ld", &numFrames);
253 }
254 else {
255 printf ("%s\n", usageStr);
256 exit(1);
257 }
258 }
259
260 if (!type || !srcFilePath) {
261 printf ("%s\n", usageStr);
262 exit(1);
263 }
264
265 CAComponentDescription desc(type, subType, manu);
266
267 CFPropertyListRef presetDict = ReadPresetFromPresetFile(auPresetFile);
268
269#pragma mark -
270#pragma mark __ The driving code
271#pragma mark -
272
273 try
274 {
275 CAComponent comp(desc);
276
277 // CAAUProcessor's constructor throws... so make sure the component is valid
278 if (comp.IsValid() == false) {
279 printf ("Can't Find Component\n");
280 desc.Print();
281 exit(1);
282 }
283
284 CAAUProcessor processor(comp);
285 processor.AU().Comp().Print();
286
287 CAAudioFile srcFile;
288
289 srcFile.Open(srcFilePath);
290
291#if !CAAF_USE_EXTAUDIOFILE
292 UInt64 numInputSamples = srcFile.GetNumberPackets();
293#else
294 UInt64 numInputSamples = srcFile.GetNumberFrames();
295#endif
296
297 Float64 inputSecs = (numInputSamples / srcFile.GetFileDataFormat().mSampleRate);
298
299 CAStreamBasicDescription procFormat (srcFile.GetFileDataFormat());
300 procFormat.SetCanonical (srcFile.GetFileDataFormat().NumberChannels(), false);
301
302 printf ("Processing file: %s, %.1f secs [proc: %ld frames]\n", srcFilePath, inputSecs, numFrames);
303 #if VERBOSE
304 printf("\t");
305 procFormat.Print();
306 #endif
307
308 srcFile.SetClientFormat (procFormat);
309
310 AUOutputBL outputList(procFormat);
311
312 // read the entire file into memory
313 ReadBuffer* readBuf = new ReadBuffer;
314 readBuf->readData = new AUOutputBL(procFormat);
315 readBuf->totalInputFrames = numInputSamples;
316 readBuf->readData->Allocate (numInputSamples);
317 readBuf->readData->Prepare();
318 UInt32 readSamps = (UInt32)numInputSamples;
319 srcFile.Read (readSamps, readBuf->readData->ABL());
320
321 AURenderCallbackStruct inputCallback;
322 inputCallback.inputProc = MemoryInputCallback;
323 inputCallback.inputProcRefCon = readBuf;
324
325 OSStatus result;
326 require_noerr (result = processor.EstablishInputCallback (inputCallback), home);
327 require_noerr (result = processor.SetMaxFramesPerRender (numFrames), home);
328 require_noerr (result = processor.Initialize (procFormat, numInputSamples), home);
329 if (presetDict) {
330 require_noerr (result = processor.SetAUPreset (presetDict), home);
331 CFRelease (presetDict);
332 }
333 // this does ALL of the preflighting.. could be specialise for an OfflineAU type
334 // to do this piecemeal and do a progress bar by using the OfflineAUPreflight method
335 readBuf->lastInputFrames = 0;
336 require_noerr (result = processor.Preflight (), home);
337
338 float mean;
339
340 // now do the processing....
341 {
342 const int kThrasherSize = 4000000;
343 char* thrasher = new char[kThrasherSize];
344
345 bool isDone = false;
346
347 UInt32 numMeasures = 0;
348 Float64 totalMSqrd;
349 Float64 totalM;
350
351 int i = 0;
352 int discardResults = 3;
353
354 // this is the render loop
355 while (!isDone)
356 {
357 bool isSilence, postProcess;
358
359 outputList.Prepare(); // have to do this every time...
360 readBuf->lastInputFrames = 0;
361 sLastReadTime = 0;
362 memset (thrasher, numMeasures, kThrasherSize);
363
364 UInt64 now = CAHostTimeBase::GetTheCurrentTime();
365 require_noerr (result = processor.Render (outputList.ABL(), numFrames, isSilence, &isDone, &postProcess), home);
366 UInt64 renderTime = (CAHostTimeBase::GetTheCurrentTime() - now);
367
368 if (i++ < discardResults) continue;
369 if (!readBuf->lastInputFrames) break;
370
371 Float64 renderTimeSecs = CAHostTimeBase::ConvertToNanos (renderTime - sLastReadTime) / 1.0e9;
372
373 Float64 cpuTime = (renderTimeSecs / (readBuf->lastInputFrames / procFormat.mSampleRate)) * 100.;
374 numMeasures++;
375
376 totalMSqrd += (cpuTime * cpuTime);
377 totalM += cpuTime;
378
379 if (cpuTime > sMaxTime)
380 sMaxTime = cpuTime;
381 if (cpuTime < sMinTime)
382 sMinTime = cpuTime;
383
384#if VERBOSE
385// printf ("current measure: %.2f\n", cpuTime);
386 if (numMeasures % 5 == 0) {
387 Float64 mean = totalM / numMeasures;
388 // stdDev = (sum of Xsquared -((sum of X)*(sum of X)/N)) / (N-1))
389 Float64 stdDev = sqrt ((totalMSqrd - ((totalM * totalM) / numMeasures)) / (numMeasures-1.0));
390 printf ("ave: %.2f, min: %.2f, max: %.2f, stdev: %.2f, numMeasures: %ld, current: %f\n",
391 mean, sMinTime, sMaxTime, stdDev, numMeasures, cpuTime);
392 }
393#endif
394 }
395 delete [] thrasher;
396
397 mean = totalM / numMeasures;
398 // stdDev = (sum of Xsquared -((sum of X)*(sum of X)/N)) / (N-1))
399 Float64 stdDev = sqrt ((totalMSqrd - ((totalM * totalM) / numMeasures)) / (numMeasures-1.0));
400
401 printf ("ave: %.2f, min: %.2f, max: %.2f, sd: %.2f, sd / mean: %.2f%%\n",
402 mean, sMinTime, sMaxTime, stdDev, (stdDev / mean * 100.));
403 }
404
405 // we don't care about post-processing
406
407home:
408 if (result) {
409 printf ("Exit with bad result:%ld\n", result);
410 exit(result);
411 }
412
413 if (readBuf) {
414 delete readBuf->readData;
415 delete readBuf;
416 }
417
418
419 CFStringRef str = comp.GetCompName();
420 UInt32 compNameLen = CFStringGetLength (str);
421
422 CFStringRef presetName = NULL;
423 if (auPresetFile) {
424 CFPropertyListRef dict;
425 if (processor.AU().GetAUPreset (dict) == noErr) {
426 presetName = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)dict, CFSTR("name"));
427 CFRelease (dict);
428 }
429 }
430
431 UInt32 presetLen = presetName ? CFStringGetLength(presetName) : 0;
432
433 UInt32 groupID = comp.Desc().componentSubType;
434
435 char* cstr = (char*)malloc (compNameLen + presetLen + 2 + 1);
436 CFStringGetCString (str, cstr, (CFStringGetLength (str) + 1), kCFStringEncodingASCII);
437 if (presetName) {
438 cstr[compNameLen] = ':';
439 cstr[compNameLen+1] = ':';
440 CFStringGetCString (presetName, cstr + compNameLen + 2, (CFStringGetLength (presetName) + 1), kCFStringEncodingASCII);
441 int len = strlen(cstr);
442 for (int i = 0; i < len; ++i)
443 groupID += cstr[i];
444 }
445 PerfResult("AU Profile", EndianU32_NtoB(groupID), cstr, mean, "%realtime");
446 free (cstr);
447
448 }
449 catch (CAXException &e) {
450 char buf[256];
451 printf("Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
452 exit(1);
453 }
454 catch (...) {
455 printf("An unknown error occurred\n");
456 exit(1);
457 }
458
459 return 0;
460}
461