Jamoma API  0.6.0.a19
CAAudioUnit.cpp
1 /* Copyright 2007 Apple Inc. All Rights Reserved.
2 
3  Disclaimer: IMPORTANT: This Apple software is supplied to you by
4  Apple Inc. ("Apple") in consideration of your agreement to the
5  following terms, and your use, installation, modification or
6  redistribution of this Apple software constitutes acceptance of these
7  terms. If you do not agree with these terms, please do not use,
8  install, modify or redistribute this Apple software.
9 
10  In consideration of your agreement to abide by the following terms, and
11  subject to these terms, Apple grants you a personal, non-exclusive
12  license, under Apple's copyrights in this original Apple software (the
13  "Apple Software"), to use, reproduce, modify and redistribute the Apple
14  Software, with or without modifications, in source and/or binary forms;
15  provided that if you redistribute the Apple Software in its entirety and
16  without modifications, you must retain this notice and the following
17  text and disclaimers in all such redistributions of the Apple Software.
18  Neither the name, trademarks, service marks or logos of Apple Inc.
19  may be used to endorse or promote products derived from the Apple
20  Software without specific prior written permission from Apple. Except
21  as expressly stated in this notice, no other rights or licenses, express
22  or implied, are granted by Apple herein, including but not limited to
23  any patent rights that may be infringed by your derivative works or by
24  other works in which the Apple Software may be incorporated.
25 
26  The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
31 
32  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
33  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
36  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
37  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
38  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
39  POSSIBILITY OF SUCH DAMAGE.
40 */
41 #include "CAAudioUnit.h"
42 
43 #if !TARGET_OS_IPHONE
44 #if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
45  #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/Components.h>
46  #include <AudioUnit/MusicDevice.h>
47  #include <dlfcn.h>
48 #else
49  #include <Components.h>
50  #include <MusicDevice.h>
51 #endif
52 #endif
53 
54 #include "CAXException.h"
55 #include "CAReferenceCounted.h"
56 #include "AUOutputBL.h" //this is for the Preroll only
57 
58 struct StackAUChannelInfo {
59  StackAUChannelInfo (UInt32 inSize) : mChanInfo ((AUChannelInfo*)malloc (inSize)) {}
60  ~StackAUChannelInfo() { free (mChanInfo); }
61 
62  AUChannelInfo* mChanInfo;
63 };
64 
65 // is this symbol is not defined then we use the default setting which is that fast dispatch
66 // is supported on a desktop environment
67 #ifndef CA_AU_USE_FAST_DISPATCH
68  #define CA_AU_USE_FAST_DISPATCH !TARGET_OS_IPHONE
69 #endif
70 
71 class CAAudioUnit::AUState : public CAReferenceCounted {
72 public:
73  AUState (AudioComponent inComp)
74  : mUnit(0), mNode (0)
75  {
76  OSStatus result = ::AudioComponentInstanceNew (inComp, &mUnit);
77  if (result)
78  throw result;
79  Init();
80  }
81 
82  AUState (const AUNode &inNode, const AudioUnit& inUnit)
83  : mUnit (inUnit), mNode (inNode)
84  {
85  Init();
86  }
87 
88  ~AUState();
89 
90  AudioUnit mUnit;
91  AUNode mNode;
92 
93  OSStatus GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
94  Float32 &outValue) const
95  {
96 #if CA_AU_USE_FAST_DISPATCH
97  if (mGetParamProc != NULL) {
98  return (mGetParamProc) (mConnInstanceStorage, inID, scope, element, &outValue);
99  }
100 #endif
101  return AudioUnitGetParameter(mUnit, inID, scope, element, &outValue);
102  }
103 
104  OSStatus SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
105  Float32 value, UInt32 bufferOffsetFrames)
106  {
107 #if CA_AU_USE_FAST_DISPATCH
108  if (mSetParamProc != NULL) {
109  return (mSetParamProc) (mConnInstanceStorage, inID, scope, element, value, bufferOffsetFrames);
110  }
111 #endif
112  return AudioUnitSetParameter(mUnit, inID, scope, element, value, bufferOffsetFrames);
113  }
114 
115  OSStatus Render (AudioUnitRenderActionFlags * ioActionFlags,
116  const AudioTimeStamp * inTimeStamp,
117  UInt32 inOutputBusNumber,
118  UInt32 inNumberFrames,
119  AudioBufferList * ioData)
120  {
121 #if CA_AU_USE_FAST_DISPATCH
122  if (mRenderProc != NULL) {
123  return (mRenderProc) (mConnInstanceStorage, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
124  }
125 #endif
126  return AudioUnitRender(mUnit, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
127  }
128 
129 #if !TARGET_OS_IPHONE
130  OSStatus MIDIEvent (UInt32 inStatus,
131  UInt32 inData1,
132  UInt32 inData2,
133  UInt32 inOffsetSampleFrame)
134  {
135 #if !TARGET_OS_WIN32
136 #if CA_AU_USE_FAST_DISPATCH
137  if (mMIDIEventProc != NULL) {
138  return (mMIDIEventProc) (mConnInstanceStorage, inStatus, inData1, inData2, inOffsetSampleFrame);
139  }
140 #endif
141  return MusicDeviceMIDIEvent (mUnit, inStatus, inData1, inData2, inOffsetSampleFrame);
142 #else // ON WINDOWS _ NO MIDI EVENT dispatch
143  return paramErr;
144 #endif
145  }
146 
147  OSStatus StartNote (MusicDeviceInstrumentID inInstrument,
148  MusicDeviceGroupID inGroupID,
149  NoteInstanceID * outNoteInstanceID,
150  UInt32 inOffsetSampleFrame,
151  const MusicDeviceNoteParams * inParams)
152  {
153 #if !TARGET_OS_WIN32
154 #if CA_AU_USE_FAST_DISPATCH
155  if (mStartNoteProc != NULL) {
156  return (mStartNoteProc) (mConnInstanceStorage, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
157  }
158 #endif
159  return MusicDeviceStartNote (mUnit, inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams);
160 #else
161  return paramErr;
162 #endif
163  }
164  OSStatus StopNote (MusicDeviceGroupID inGroupID,
165  NoteInstanceID inNoteInstanceID,
166  UInt32 inOffsetSampleFrame)
167  {
168 #if !TARGET_OS_WIN32
169 #if CA_AU_USE_FAST_DISPATCH
170  if (mStopNoteProc != NULL) {
171  return (mStopNoteProc) (mConnInstanceStorage, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
172  }
173 #endif
174  return MusicDeviceStopNote (mUnit, inGroupID, inNoteInstanceID, inOffsetSampleFrame);
175 #else
176  return paramErr;
177 #endif
178  }
179 #endif// !TARGET_OS_IPHONE
180 
181 private:
182  // get the fast dispatch pointers
183  void Init()
184  {
185 #if CA_AU_USE_FAST_DISPATCH
186  UInt32 size = sizeof(AudioUnitRenderProc);
187  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
188  kAudioUnitScope_Global, kAudioUnitRenderSelect,
189  &mRenderProc, &size) != noErr)
190  mRenderProc = NULL;
191 
192  size = sizeof(AudioUnitGetParameterProc);
193  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
194  kAudioUnitScope_Global, kAudioUnitGetParameterSelect,
195  &mGetParamProc, &size) != noErr)
196  mGetParamProc = NULL;
197 
198  size = sizeof(AudioUnitSetParameterProc);
199  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
200  kAudioUnitScope_Global, kAudioUnitSetParameterSelect,
201  &mSetParamProc, &size) != noErr)
202  mSetParamProc = NULL;
203 
204  size = sizeof(MusicDeviceMIDIEventProc);
205  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
206  kAudioUnitScope_Global, kMusicDeviceMIDIEventSelect,
207  &mMIDIEventProc, &size) != noErr)
208  mMIDIEventProc = NULL;
209 
210  size = sizeof(MusicDeviceStartNoteProc);
211  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
212  kAudioUnitScope_Global, kMusicDeviceStartNoteSelect,
213  &mStartNoteProc, &size) != noErr)
214  mStartNoteProc = NULL;
215 
216  size = sizeof(MusicDeviceStopNoteProc);
217  if (AudioUnitGetProperty(mUnit, kAudioUnitProperty_FastDispatch,
218  kAudioUnitScope_Global, kMusicDeviceStopNoteSelect,
219  &mStopNoteProc, &size) != noErr)
220  mStopNoteProc = NULL;
221 
222  if (mRenderProc || mGetParamProc || mSetParamProc || mMIDIEventProc || mStartNoteProc || mStopNoteProc) {
223  mConnInstanceStorage = GetComponentInstanceStorage ( mUnit );
224  } else
225  mConnInstanceStorage = NULL;
226 #else
227  mConnInstanceStorage = NULL;
228 #endif
229  }
230 
231 #if CA_AU_USE_FAST_DISPATCH
232  AudioUnitRenderProc mRenderProc;
233  AudioUnitGetParameterProc mGetParamProc;
234  AudioUnitSetParameterProc mSetParamProc;
235  MusicDeviceMIDIEventProc mMIDIEventProc;
236  MusicDeviceStartNoteProc mStartNoteProc;
237  MusicDeviceStopNoteProc mStopNoteProc;
238 #endif
239 
240  void * mConnInstanceStorage;
241 
242 private:
243  // get the compiler to tell us when we do a bad thing!!!
244  AUState () {}
245  AUState (const AUState&) {}
246  AUState& operator= (const AUState&) { return *this; }
247 };
248 
249 
250 CAAudioUnit::AUState::~AUState ()
251 {
252  if (mUnit && (mNode == 0)) {
253  ::AudioComponentInstanceDispose (mUnit);
254  }
255  mNode = 0;
256  mUnit = 0;
257 }
258 
259 OSStatus CAAudioUnit::Open (const CAComponent& inComp, CAAudioUnit &outUnit)
260 {
261  try {
262  outUnit = inComp;
263  return noErr;
264  } catch (OSStatus res) {
265  return res;
266  } catch (...) {
267  return -1;
268  }
269 }
270 
271 CAAudioUnit::CAAudioUnit (const AudioUnit& inUnit)
272  : mComp (inUnit), mDataPtr (new AUState (kCAAU_DoNotKnowIfAUNode, inUnit))
273 {
274 }
275 
276 CAAudioUnit::CAAudioUnit (const CAComponent& inComp)
277  : mComp (inComp), mDataPtr (new AUState (mComp.Comp()))
278 {
279 }
280 
281 CAAudioUnit::CAAudioUnit (const AUNode &inNode, const AudioUnit& inUnit)
282  : mComp (inUnit), mDataPtr(new AUState (inNode, inUnit))
283 {
284 }
285 
286 CAAudioUnit::~CAAudioUnit ()
287 {
288  Close();
289 }
290 
291 void CAAudioUnit::Close()
292 {
293  if (mDataPtr) {
294  mDataPtr->release();
295  mDataPtr = NULL;
296  }
297 }
298 
299 CAAudioUnit& CAAudioUnit::operator= (const CAAudioUnit &a)
300 {
301  if (mDataPtr != a.mDataPtr) {
302  if (mDataPtr)
303  mDataPtr->release();
304 
305  if ((mDataPtr = a.mDataPtr) != NULL)
306  mDataPtr->retain();
307 
308  mComp = a.mComp;
309  }
310 
311  return *this;
312 }
313 
314 bool CAAudioUnit::operator== (const CAAudioUnit& y) const
315 {
316  if (mDataPtr == y.mDataPtr) return true;
317  AudioUnit au1 = mDataPtr ? mDataPtr->mUnit : 0;
318  AudioUnit au2 = y.mDataPtr ? y.mDataPtr->mUnit : 0;
319  return au1 == au2;
320 }
321 
322 bool CAAudioUnit::operator== (const AudioUnit& y) const
323 {
324  if (!mDataPtr) return false;
325  return mDataPtr->mUnit == y;
326 }
327 
328 OSStatus CAAudioUnit::RemovePropertyListener (AudioUnitPropertyID inID,
329  AudioUnitPropertyListenerProc inProc,
330  void * inProcUserData)
331 {
332  // we call this first. If it fails we call the old API as the failure can
333  // mean that the AU doesn't implement that selector.
334  OSStatus result = AudioUnitRemovePropertyListenerWithUserData(AU(), inID,
335  inProc, inProcUserData);
336  #if !__LP64__ && !TARGET_OS_IPHONE
337  if (result) result = AudioUnitRemovePropertyListener (AU(), inID, inProc);
338  #endif
339  return result;
340 }
341 
342 #pragma mark __State Management
343 
344 bool CAAudioUnit::IsValid () const
345 {
346  return mDataPtr ? mDataPtr->mUnit != 0 : false;
347 }
348 
349 AudioUnit CAAudioUnit::AU() const
350 {
351  return mDataPtr ? mDataPtr->mUnit : 0;
352 }
353 
354 AUNode CAAudioUnit::GetAUNode () const
355 {
356  return mDataPtr ? mDataPtr->mNode : 0;
357 }
358 
359 #pragma mark __Format Handling
360 
361 bool CAAudioUnit::CanDo ( int inChannelsIn,
362  int inChannelsOut) const
363 {
364  // this is the default assumption of an audio effect unit
365  Boolean* isWritable = 0;
366  UInt32 dataSize = 0;
367  // lets see if the unit has any channel restrictions
368  OSStatus result = AudioUnitGetPropertyInfo (AU(),
369  kAudioUnitProperty_SupportedNumChannels,
370  kAudioUnitScope_Global, 0,
371  &dataSize, isWritable); //don't care if this is writable
372 
373  // if this property is NOT implemented an FX unit
374  // is expected to deal with same channel valance in and out
375  if (result)
376  {
377  if (Comp().Desc().IsEffect() && (inChannelsIn == inChannelsOut)
378  || Comp().Desc().IsOffline() && (inChannelsIn == inChannelsOut))
379  {
380  return true;
381  }
382  else
383  {
384  // the au should either really tell us about this
385  // or we will assume the worst
386  return false;
387  }
388  }
389 
390  StackAUChannelInfo info (dataSize);
391 
392  result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
393  kAudioUnitScope_Global, 0,
394  info.mChanInfo, &dataSize);
395  if (result) { return false; }
396 
397  return ValidateChannelPair (inChannelsIn, inChannelsOut, info.mChanInfo, (dataSize / sizeof (AUChannelInfo)));
398 }
399 
400 bool CAAudioUnit::ValidateChannelPair (int inChannelsIn,
401  int inChannelsOut,
402  const AUChannelInfo * info,
403  UInt32 numChanInfo) const
404 {
405 // we've the following cases (some combinations) to test here:
406 /*
407 >0 An explicit number of channels on either side
408 0 that side (generally input!) has no elements
409 -1 wild card:
410 -1,-1 any num channels as long as same channels on in and out
411 -1,-2 any num channels channels on in and out - special meaning
412 -2+ indicates total num channs AU can handle
413  - elements configurable to any num channels,
414  - element count in scope must be writable
415 */
416 
417  //now chan layout can contain -1 for either scope (ie. doesn't care)
418  for (unsigned int i = 0; i < numChanInfo; ++i)
419  {
420  //less than zero on both sides - check for special attributes
421  if ((info[i].inChannels < 0) && (info[i].outChannels < 0))
422  {
423  // these are our wild card matches
424  if (info[i].inChannels == -1 && info[i].outChannels == -1) {
425  if (inChannelsIn && inChannelsOut) {
426  if (inChannelsOut == inChannelsIn)
427  return true;
428  } else
429  return true; // if one of these is zero, then a -1 means any
430  }
431  else if ((info[i].inChannels == -1 && info[i].outChannels == -2)
432  || (info[i].inChannels == -2 && info[i].outChannels == -1))
433  {
434  return true;
435  }
436  // these are our total num channels matches
437  // element count MUST be writable
438  else {
439  bool outWrite = false; bool inWrite = false;
440  IsElementCountWritable (kAudioUnitScope_Output, outWrite);
441  IsElementCountWritable (kAudioUnitScope_Input, inWrite);
442  if (inWrite && outWrite) {
443  if ((inChannelsOut <= abs(info[i].outChannels))
444  && (inChannelsIn <= abs(info[i].inChannels)))
445  {
446  return true;
447  }
448  }
449  }
450  }
451 
452  // special meaning on input, specific num on output
453  else if (info[i].inChannels < 0) {
454  if (info[i].outChannels == inChannelsOut)
455  {
456  // can do any in channels
457  if (info[i].inChannels == -1) {
458  return true;
459  }
460  // total chans on input
461  else {
462  bool inWrite = false;
463  IsElementCountWritable (kAudioUnitScope_Input, inWrite);
464  if (inWrite && (inChannelsIn <= abs(info[i].inChannels))) {
465  return true;
466  }
467  }
468  }
469  }
470 
471  // special meaning on output, specific num on input
472  else if (info[i].outChannels < 0) {
473  if (info[i].inChannels == inChannelsIn)
474  {
475  // can do any out channels
476  if (info[i].outChannels == -1) {
477  return true;
478  }
479  // total chans on output
480  else {
481  bool outWrite = false;
482  IsElementCountWritable (kAudioUnitScope_Output, outWrite);
483  if (outWrite && (inChannelsOut <= abs(info[i].outChannels))) {
484  return true;
485  }
486  }
487  }
488  }
489 
490  // both chans in struct >= 0 - thus has to explicitly match
491  else if ((info[i].inChannels == inChannelsIn) && (info[i].outChannels == inChannelsOut)) {
492  return true;
493  }
494 
495  // now check to see if a wild card on the args (inChannelsIn or inChannelsOut chans is zero) is found
496  // tells us to match just one side of the scopes
497  else if (inChannelsIn == 0) {
498  if (info[i].outChannels == inChannelsOut) {
499  return true;
500  }
501  }
502  else if (inChannelsOut == 0) {
503  if (info[i].inChannels == inChannelsIn) {
504  return true;
505  }
506  }
507  }
508 
509  return false;
510 }
511 
512 bool CheckDynCount (SInt32 inTotalChans, const CAAUChanHelper &inHelper)
513 {
514  int totalChans = 0;
515  for (unsigned int i = 0; i < inHelper.mNumEls; ++i)
516  totalChans += inHelper.mChans[i];
517  return (totalChans <= inTotalChans);
518 }
519 
520 bool CAAudioUnit::CheckOneSide (const CAAUChanHelper &inHelper,
521  bool checkOutput,
522  const AUChannelInfo *info,
523  UInt32 numInfo) const
524 {
525  // now we can use the wildcard option (see above impl) to see if this matches
526  for (unsigned int el = 0; el < inHelper.mNumEls; ++el) {
527  bool testAlready = false;
528  for (unsigned int i = 0; i < el; ++i) {
529  if (inHelper.mChans[i] == inHelper.mChans[el]) {
530  testAlready = true;
531  break;
532  }
533  }
534  if (!testAlready) {
535  if (checkOutput) {
536  if (!ValidateChannelPair (0, inHelper.mChans[el], info, numInfo)) return false;
537  } else {
538  if (!ValidateChannelPair (inHelper.mChans[el], 0, info, numInfo)) return false;
539  }
540  }
541  }
542  return true;
543 }
544 
545 bool CAAudioUnit::CanDo (const CAAUChanHelper &inputs,
546  const CAAUChanHelper &outputs) const
547 
548 {
549 // first check our state
550  // huh!
551  if (inputs.mNumEls == 0 && outputs.mNumEls == 0) return false;
552 
553  UInt32 elCount;
554  if (GetElementCount (kAudioUnitScope_Input, elCount)) { return false; }
555  if (elCount != inputs.mNumEls) return false;
556 
557  if (GetElementCount (kAudioUnitScope_Output, elCount)) { return false; }
558  if (elCount != outputs.mNumEls) return false;
559 
560 // (1) special cases (effects and sources (generators and instruments) only)
561  UInt32 dataSize = 0;
562  if (GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
563  kAudioUnitScope_Global, 0, &dataSize, NULL) != noErr)
564  {
565  if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline()) {
566  UInt32 numChan = outputs.mNumEls > 0 ? outputs.mChans[0] : inputs.mChans[0];
567  for (unsigned int in = 0; in < inputs.mNumEls; ++in)
568  if (numChan != inputs.mChans[in]) return false;
569  for (unsigned int out = 0; out < outputs.mNumEls; ++out)
570  if (numChan != outputs.mChans[out]) return false;
571  return true;
572  }
573 
574  // in this case, all the channels have to match the current config
575  if (Comp().Desc().IsGenerator() || Comp().Desc().IsMusicDevice()) {
576  for (unsigned int in = 0; in < inputs.mNumEls; ++in) {
577  UInt32 chan;
578  if (NumberChannels (kAudioUnitScope_Input, in, chan)) return false;
579  if (chan != UInt32(inputs.mChans[in])) return false;
580  }
581  for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
582  UInt32 chan;
583  if (NumberChannels (kAudioUnitScope_Output, out, chan)) return false;
584  if (chan != UInt32(outputs.mChans[out])) return false;
585  }
586  return true;
587  }
588 
589  // if we get here we can't determine anything about channel capabilities
590  return false;
591  }
592 
593  StackAUChannelInfo info (dataSize);
594 
595  if (GetProperty (kAudioUnitProperty_SupportedNumChannels,
596  kAudioUnitScope_Global, 0,
597  info.mChanInfo, &dataSize) != noErr)
598  {
599  return false;
600  }
601 
602  int numInfo = dataSize / sizeof(AUChannelInfo);
603 
604 // (2) Test for dynamic capability (or no elements on that scope)
605  SInt32 dynInChans = 0;
606  if (ValidateDynamicScope (kAudioUnitScope_Input, dynInChans, info.mChanInfo, numInfo)) {
607  if (CheckDynCount (dynInChans, inputs) == false) return false;
608  }
609 
610  SInt32 dynOutChans = 0;
611  if (ValidateDynamicScope (kAudioUnitScope_Output, dynOutChans, info.mChanInfo, numInfo)) {
612  if (CheckDynCount (dynOutChans, outputs) == false) return false;
613  }
614 
615  if (dynOutChans && dynInChans) { return true; }
616 
617 // (3) Just need to test one side
618  if (dynInChans || (inputs.mNumEls == 0)) {
619  return CheckOneSide (outputs, true, info.mChanInfo, numInfo);
620  }
621 
622  if (dynOutChans || (outputs.mNumEls == 0)) {
623  return CheckOneSide (inputs, false, info.mChanInfo, numInfo);
624  }
625 
626 // (4) - not a dynamic AU, has ins and outs, and has channel constraints so we test every possible pairing
627  for (unsigned int in = 0; in < inputs.mNumEls; ++in)
628  {
629  bool testInAlready = false;
630  for (unsigned int i = 0; i < in; ++i) {
631  if (inputs.mChans[i] == inputs.mChans[in]) {
632  testInAlready = true;
633  break;
634  }
635  }
636  if (!testInAlready) {
637  for (unsigned int out = 0; out < outputs.mNumEls; ++out) {
638  // try to save a little bit and not test the same pairing multiple times...
639  bool testOutAlready = false;
640  for (unsigned int i = 0; i < out; ++i) {
641  if (outputs.mChans[i] == outputs.mChans[out]) {
642  testOutAlready = true;
643  break;
644  }
645  }
646  if (!testOutAlready) {
647  if (!ValidateChannelPair (inputs.mChans[in], outputs.mChans[out],info.mChanInfo, numInfo)) {
648  return false;
649  }
650  }
651  }
652  }
653  }
654 
655  return true;
656 }
657 
658 bool CAAudioUnit::SupportsNumChannels () const
659 {
660  // this is the default assumption of an audio effect unit
661  Boolean* isWritable = 0;
662  UInt32 dataSize = 0;
663  // lets see if the unit has any channel restrictions
664  OSStatus result = AudioUnitGetPropertyInfo (AU(),
665  kAudioUnitProperty_SupportedNumChannels,
666  kAudioUnitScope_Global, 0,
667  &dataSize, isWritable); //don't care if this is writable
668 
669  // if this property is NOT implemented an FX unit
670  // is expected to deal with same channel valance in and out
671  if (result) {
672  if (Comp().Desc().IsEffect() || Comp().Desc().IsOffline())
673  return true;
674  }
675  return result == noErr;
676 }
677 
678 OSStatus CAAudioUnit::GetChannelLayoutTags (AudioUnitScope inScope,
679  AudioUnitElement inEl,
680  ChannelTagVector &outChannelVector) const
681 {
682  if (HasChannelLayouts (inScope, inEl) == false) return kAudioUnitErr_InvalidProperty;
683 
684  UInt32 dataSize;
685  OSStatus result = AudioUnitGetPropertyInfo (AU(),
686  kAudioUnitProperty_SupportedChannelLayoutTags,
687  inScope, inEl,
688  &dataSize, NULL);
689 
690  if (result) return result;
691 
692  // OK lets get our channel layouts and see if the one we want is present
693  AudioChannelLayoutTag* info = (AudioChannelLayoutTag*)malloc (dataSize);
694  result = AudioUnitGetProperty (AU(),
695  kAudioUnitProperty_SupportedChannelLayoutTags,
696  inScope, inEl,
697  info, &dataSize);
698  if (result) goto home;
699 
700  outChannelVector.erase (outChannelVector.begin(), outChannelVector.end());
701  for (unsigned int i = 0; i < (dataSize / sizeof (AudioChannelLayoutTag)); ++i)
702  outChannelVector.push_back (info[i]);
703 
704 home:
705  free (info);
706  return result;
707 }
708 
709 bool CAAudioUnit::HasChannelLayouts (AudioUnitScope inScope,
710  AudioUnitElement inEl) const
711 {
712  OSStatus result = AudioUnitGetPropertyInfo (AU(),
713  kAudioUnitProperty_SupportedChannelLayoutTags,
714  inScope, inEl,
715  NULL, NULL);
716  return !result;
717 }
718 
719 bool CAAudioUnit::HasChannelLayout (AudioUnitScope inScope,
720  AudioUnitElement inEl) const
721 {
722  Boolean writable;
723  UInt32 size;
724 
725  return AudioUnitGetPropertyInfo (AU(),
726  kAudioUnitProperty_AudioChannelLayout,
727  inScope, inEl,
728  &size, &writable) == noErr;
729 }
730 
731 OSStatus CAAudioUnit::GetChannelLayout (AudioUnitScope inScope,
732  AudioUnitElement inEl,
733  CAAudioChannelLayout &outLayout) const
734 {
735  UInt32 size;
736  OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_AudioChannelLayout,
737  inScope, inEl, &size, NULL);
738  if (result) return result;
739 
740  AudioChannelLayout *layout = (AudioChannelLayout*)malloc (size);
741 
742  ca_require_noerr (result = AudioUnitGetProperty (AU(), kAudioUnitProperty_AudioChannelLayout,
743  inScope, inEl, layout, &size), home);
744 
745  outLayout = CAAudioChannelLayout (layout);
746 
747 home:
748  free (layout);
749  return result;
750 }
751 
752 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
753  AudioUnitElement inEl,
754  const CAAudioChannelLayout &inLayout)
755 {
756  OSStatus result = AudioUnitSetProperty (AU(),
757  kAudioUnitProperty_AudioChannelLayout,
758  inScope, inEl,
759  inLayout, inLayout.Size());
760  return result;
761 }
762 
763 OSStatus CAAudioUnit::SetChannelLayout (AudioUnitScope inScope,
764  AudioUnitElement inEl,
765  const AudioChannelLayout &inLayout,
766  UInt32 inSize)
767 {
768  OSStatus result = AudioUnitSetProperty (AU(),
769  kAudioUnitProperty_AudioChannelLayout,
770  inScope, inEl,
771  &inLayout, inSize);
772  return result;
773 }
774 
775 OSStatus CAAudioUnit::ClearChannelLayout (AudioUnitScope inScope,
776  AudioUnitElement inEl)
777 {
778  return AudioUnitSetProperty (AU(),
779  kAudioUnitProperty_AudioChannelLayout,
780  inScope, inEl, NULL, 0);
781 }
782 
783 OSStatus CAAudioUnit::GetFormat (AudioUnitScope inScope,
784  AudioUnitElement inEl,
785  AudioStreamBasicDescription &outFormat) const
786 {
787  UInt32 dataSize = sizeof (AudioStreamBasicDescription);
788  return AudioUnitGetProperty (AU(), kAudioUnitProperty_StreamFormat,
789  inScope, inEl,
790  &outFormat, &dataSize);
791 }
792 
793 OSStatus CAAudioUnit::SetFormat (AudioUnitScope inScope,
794  AudioUnitElement inEl,
795  const AudioStreamBasicDescription &inFormat)
796 {
797  return AudioUnitSetProperty (AU(), kAudioUnitProperty_StreamFormat,
798  inScope, inEl,
799  const_cast<AudioStreamBasicDescription*>(&inFormat),
800  sizeof (AudioStreamBasicDescription));
801 }
802 
803 OSStatus CAAudioUnit::GetSampleRate (AudioUnitScope inScope,
804  AudioUnitElement inEl,
805  Float64 &outRate) const
806 {
807  UInt32 dataSize = sizeof (Float64);
808  return AudioUnitGetProperty (AU(), kAudioUnitProperty_SampleRate,
809  inScope, inEl,
810  &outRate, &dataSize);
811 }
812 
813 OSStatus CAAudioUnit::SetSampleRate (AudioUnitScope inScope,
814  AudioUnitElement inEl,
815  Float64 inRate)
816 {
817  AudioStreamBasicDescription desc;
818  OSStatus result = GetFormat (inScope, inEl, desc);
819  if (result) return result;
820  desc.mSampleRate = inRate;
821  return SetFormat (inScope, inEl, desc);
822 }
823 
824 OSStatus CAAudioUnit::SetSampleRate (Float64 inSampleRate)
825 {
826  OSStatus result;
827 
828  UInt32 elCount;
829  ca_require_noerr (result = GetElementCount(kAudioUnitScope_Input, elCount), home);
830  if (elCount) {
831  for (unsigned int i = 0; i < elCount; ++i) {
832  ca_require_noerr (result = SetSampleRate (kAudioUnitScope_Input, i, inSampleRate), home);
833  }
834  }
835 
836  ca_require_noerr (result = GetElementCount(kAudioUnitScope_Output, elCount), home);
837  if (elCount) {
838  for (unsigned int i = 0; i < elCount; ++i) {
839  ca_require_noerr (result = SetSampleRate (kAudioUnitScope_Output, i, inSampleRate), home);
840  }
841  }
842 
843 home:
844  return result;
845 }
846 
847 OSStatus CAAudioUnit::NumberChannels (AudioUnitScope inScope,
848  AudioUnitElement inEl,
849  UInt32 &outChans) const
850 {
851  AudioStreamBasicDescription desc;
852  OSStatus result = GetFormat (inScope, inEl, desc);
853  if (!result)
854  outChans = desc.mChannelsPerFrame;
855  return result;
856 }
857 
858 OSStatus CAAudioUnit::SetNumberChannels (AudioUnitScope inScope,
859  AudioUnitElement inEl,
860  UInt32 inChans)
861 {
862  // set this as the output of the AU
863  CAStreamBasicDescription desc;
864  OSStatus result = GetFormat (inScope, inEl, desc);
865  if (result) return result;
866  desc.SetAUCanonical (inChans, desc.IsInterleaved());
867  result = SetFormat (inScope, inEl, desc);
868  return result;
869 }
870 
871 OSStatus CAAudioUnit::IsElementCountWritable (AudioUnitScope inScope, bool &outWritable) const
872 {
873  Boolean isWritable;
874  UInt32 outDataSize;
875  OSStatus result = GetPropertyInfo (kAudioUnitProperty_ElementCount, inScope, 0, &outDataSize, &isWritable);
876  if (result)
877  return result;
878  outWritable = isWritable ? true : false;
879  return noErr;
880 }
881 
882 OSStatus CAAudioUnit::GetElementCount (AudioUnitScope inScope, UInt32 &outCount) const
883 {
884  UInt32 propSize = sizeof(outCount);
885  return GetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &outCount, &propSize);
886 }
887 
888 OSStatus CAAudioUnit::SetElementCount (AudioUnitScope inScope, UInt32 inCount)
889 {
890  return SetProperty (kAudioUnitProperty_ElementCount, inScope, 0, &inCount, sizeof(inCount));
891 }
892 
893 bool CAAudioUnit::HasDynamicScope (AudioUnitScope inScope, SInt32 &outTotalNumChannels) const
894 {
895  // ok - now we need to check the AU's capability here.
896  // this is the default assumption of an audio effect unit
897  Boolean* isWritable = 0;
898  UInt32 dataSize = 0;
899  OSStatus result = GetPropertyInfo (kAudioUnitProperty_SupportedNumChannels,
900  kAudioUnitScope_Global, 0,
901  &dataSize, isWritable); //don't care if this is writable
902 
903  // AU has to explicitly tell us about this.
904  if (result) return false;
905 
906  StackAUChannelInfo info (dataSize);
907 
908  result = GetProperty (kAudioUnitProperty_SupportedNumChannels,
909  kAudioUnitScope_Global, 0,
910  info.mChanInfo, &dataSize);
911  if (result) return false;
912 
913  return ValidateDynamicScope (inScope, outTotalNumChannels, info.mChanInfo, (dataSize / sizeof(AUChannelInfo)));
914 }
915 
916 // as we've already checked that the element count is writable
917 // the following conditions will match this..
918 /*
919 -1, -2 -> signifies no restrictions
920 -2, -1 -> signifies no restrictions -> in this case outTotalNumChannels == -1 (any num channels)
921 
922 -N (where N is less than -2), signifies the total channel count on the scope side (in or out)
923 */
924 bool CAAudioUnit::ValidateDynamicScope (AudioUnitScope inScope,
925  SInt32 &outTotalNumChannels,
926  const AUChannelInfo *info,
927  UInt32 numInfo) const
928 {
929  bool writable = false;
930  OSStatus result = IsElementCountWritable (inScope, writable);
931  if (result || (writable == false))
932  return false;
933 
934  //now chan layout can contain -1 for either scope (ie. doesn't care)
935  for (unsigned int i = 0; i < numInfo; ++i)
936  {
937  // lets test the special wild card case first...
938  // this says the AU can do any num channels on input or output - for eg. Matrix Mixer
939  if (((info[i].inChannels == -1) && (info[i].outChannels == -2))
940  || ((info[i].inChannels == -2) && (info[i].outChannels == -1)))
941  {
942  outTotalNumChannels = -1;
943  return true;
944  }
945 
946  // ok lets now test our special case....
947  if (inScope == kAudioUnitScope_Input) {
948  // isn't dynamic on this side at least
949  if (info[i].inChannels >= 0)
950  continue;
951 
952  if (info[i].inChannels < -2) {
953  outTotalNumChannels = abs (info[i].inChannels);
954  return true;
955  }
956  }
957 
958  else if (inScope == kAudioUnitScope_Output) {
959  // isn't dynamic on this side at least
960  if (info[i].outChannels >= 0)
961  continue;
962 
963  if (info[i].outChannels < -2) {
964  outTotalNumChannels = abs (info[i].outChannels);
965  return true;
966  }
967  }
968 
969  else {
970  break; // wrong scope was specified
971  }
972  }
973 
974  return false;
975 }
976 
977 OSStatus CAAudioUnit::ConfigureDynamicScope (AudioUnitScope inScope,
978  UInt32 inNumElements,
979  UInt32 *inChannelsPerElement,
980  Float64 inSampleRate)
981 {
982  SInt32 numChannels = 0;
983  bool isDyamic = HasDynamicScope (inScope, numChannels);
984  if (isDyamic == false)
985  return kAudioUnitErr_InvalidProperty;
986 
987  //lets to a sanity check...
988  // if numChannels == -1, then it can do "any"...
989  if (numChannels > 0) {
990  SInt32 count = 0;
991  for (unsigned int i = 0; i < inNumElements; ++i)
992  count += inChannelsPerElement[i];
993  if (count > numChannels)
994  return kAudioUnitErr_InvalidPropertyValue;
995  }
996 
997  OSStatus result = SetElementCount (inScope, inNumElements);
998  if (result)
999  return result;
1000 
1001  CAStreamBasicDescription desc;
1002  desc.mSampleRate = inSampleRate;
1003  for (unsigned int i = 0; i < inNumElements; ++i) {
1004  desc.SetAUCanonical (inChannelsPerElement[i], false);
1005  result = SetFormat (inScope, i, desc);
1006  if (result)
1007  return result;
1008  }
1009  return noErr;
1010 }
1011 
1012 #pragma mark __Properties
1013 
1014 bool CAAudioUnit::CanBypass () const
1015 {
1016  Boolean outWritable;
1017  OSStatus result = AudioUnitGetPropertyInfo (AU(), kAudioUnitProperty_BypassEffect,
1018  kAudioUnitScope_Global, 0,
1019  NULL, &outWritable);
1020  return (!result && outWritable);
1021 }
1022 
1023 bool CAAudioUnit::GetBypass () const
1024 {
1025  UInt32 dataSize = sizeof (UInt32);
1026  UInt32 outBypass;
1027  OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_BypassEffect,
1028  kAudioUnitScope_Global, 0,
1029  &outBypass, &dataSize);
1030  return (result ? false : outBypass);
1031 }
1032 
1033 OSStatus CAAudioUnit::SetBypass (bool inBypass) const
1034 {
1035  UInt32 bypass = inBypass ? 1 : 0;
1036  return AudioUnitSetProperty (AU(), kAudioUnitProperty_BypassEffect,
1037  kAudioUnitScope_Global, 0,
1038  &bypass, sizeof (UInt32));
1039 }
1040 
1041 Float64 CAAudioUnit::Latency () const
1042 {
1043  Float64 secs;
1044  UInt32 size = sizeof(secs);
1045  if (GetProperty (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &secs, &size))
1046  return 0;
1047  return secs;
1048 }
1049 
1050 OSStatus CAAudioUnit::GetAUPreset (CFPropertyListRef &outData) const
1051 {
1052  UInt32 dataSize = sizeof(outData);
1053  return AudioUnitGetProperty (AU(), kAudioUnitProperty_ClassInfo,
1054  kAudioUnitScope_Global, 0,
1055  &outData, &dataSize);
1056 }
1057 
1058 OSStatus CAAudioUnit::SetAUPreset (CFPropertyListRef &inData)
1059 {
1060  return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfo,
1061  kAudioUnitScope_Global, 0,
1062  &inData, sizeof (CFPropertyListRef));
1063 }
1064 
1065 #if !TARGET_OS_IPHONE
1066 OSStatus CAAudioUnit::SetAUPresetFromDocument (CFPropertyListRef &inData)
1067 {
1068  return AudioUnitSetProperty (AU(), kAudioUnitProperty_ClassInfoFromDocument,
1069  kAudioUnitScope_Global, 0,
1070  &inData, sizeof (CFPropertyListRef));
1071 }
1072 #endif
1073 
1074 OSStatus CAAudioUnit::GetPresentPreset (AUPreset &outData) const
1075 {
1076  UInt32 dataSize = sizeof(outData);
1077  OSStatus result = AudioUnitGetProperty (AU(), kAudioUnitProperty_PresentPreset,
1078  kAudioUnitScope_Global, 0,
1079  &outData, &dataSize);
1080 #if !TARGET_OS_IPHONE
1081 #ifndef __LP64__
1082  if (result == kAudioUnitErr_InvalidProperty) {
1083  dataSize = sizeof(outData);
1084  result = AudioUnitGetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1085  kAudioUnitScope_Global, 0,
1086  &outData, &dataSize);
1087  if (result == noErr) {
1088  // we now retain the CFString in the preset so for the client of this API
1089  // it is consistent (ie. the string should be released when done)
1090  if (outData.presetName)
1091  CFRetain (outData.presetName);
1092  }
1093  }
1094 #endif
1095 #endif
1096  return result;
1097 }
1098 
1099 OSStatus CAAudioUnit::SetPresentPreset (AUPreset &inData)
1100 {
1101  OSStatus result = AudioUnitSetProperty (AU(), kAudioUnitProperty_PresentPreset,
1102  kAudioUnitScope_Global, 0,
1103  &inData, sizeof (AUPreset));
1104 #if !TARGET_OS_IPHONE
1105 #ifndef __LP64__
1106  if (result == kAudioUnitErr_InvalidProperty) {
1107  result = AudioUnitSetProperty (AU(), kAudioUnitProperty_CurrentPreset,
1108  kAudioUnitScope_Global, 0,
1109  &inData, sizeof (AUPreset));
1110  }
1111 #endif
1112 #endif
1113  return result;
1114 }
1115 
1116 bool CAAudioUnit::HasCustomView () const
1117 {
1118 #if !TARGET_OS_IPHONE
1119  UInt32 dataSize = 0;
1120  OSStatus result = -4/*unimpErr*/;
1121 #ifndef __LP64__
1122  result = GetPropertyInfo(kAudioUnitProperty_GetUIComponentList,
1123  kAudioUnitScope_Global, 0,
1124  &dataSize, NULL);
1125 #endif
1126  if (result || !dataSize) {
1127  dataSize = 0;
1128  result = GetPropertyInfo(kAudioUnitProperty_CocoaUI,
1129  kAudioUnitScope_Global, 0,
1130  &dataSize, NULL);
1131  if (result || !dataSize)
1132  return false;
1133  }
1134  return true;
1135 #else
1136  return false;
1137 #endif
1138 
1139 }
1140 
1141 OSStatus CAAudioUnit::GetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1142  Float32 &outValue) const
1143 {
1144  return mDataPtr ? mDataPtr->GetParameter (inID, scope, element, outValue) : paramErr;
1145 }
1146 
1147 OSStatus CAAudioUnit::SetParameter(AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element,
1148  Float32 value, UInt32 bufferOffsetFrames)
1149 {
1150  return mDataPtr ? mDataPtr->SetParameter (inID, scope, element, value, bufferOffsetFrames) : paramErr;
1151 }
1152 
1153 #if !TARGET_OS_IPHONE
1154 OSStatus CAAudioUnit::MIDIEvent (UInt32 inStatus,
1155  UInt32 inData1,
1156  UInt32 inData2,
1157  UInt32 inOffsetSampleFrame)
1158 {
1159  return mDataPtr ? mDataPtr->MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame) : paramErr;
1160 }
1161 
1162 OSStatus CAAudioUnit::StartNote (MusicDeviceInstrumentID inInstrument,
1163  MusicDeviceGroupID inGroupID,
1164  NoteInstanceID * outNoteInstanceID,
1165  UInt32 inOffsetSampleFrame,
1166  const MusicDeviceNoteParams * inParams)
1167 {
1168  return mDataPtr ? mDataPtr->StartNote (inInstrument, inGroupID, outNoteInstanceID, inOffsetSampleFrame, inParams)
1169  : paramErr;
1170 }
1171 
1172 OSStatus CAAudioUnit::StopNote (MusicDeviceGroupID inGroupID,
1173  NoteInstanceID inNoteInstanceID,
1174  UInt32 inOffsetSampleFrame)
1175 {
1176  return mDataPtr ? mDataPtr->StopNote (inGroupID, inNoteInstanceID, inOffsetSampleFrame) : paramErr;
1177 }
1178 #endif
1179 
1180 
1181 #pragma mark __Render
1182 
1183 OSStatus CAAudioUnit::Render (AudioUnitRenderActionFlags * ioActionFlags,
1184  const AudioTimeStamp * inTimeStamp,
1185  UInt32 inOutputBusNumber,
1186  UInt32 inNumberFrames,
1187  AudioBufferList * ioData)
1188 {
1189  return mDataPtr ? mDataPtr->Render (ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData) : paramErr;
1190 }
1191 
1192 #pragma mark __CAAUChanHelper
1193 
1194 CAAUChanHelper::CAAUChanHelper(const CAAudioUnit &inAU, AudioUnitScope inScope)
1195  :mChans(NULL), mNumEls(0), mDidAllocate(false)
1196 {
1197  UInt32 elCount;
1198  if (inAU.GetElementCount (inScope, elCount)) return;
1199  if (elCount > kStaticElCount) {
1200  mChans = new UInt32[elCount];
1201  mDidAllocate = true;
1202  memset (mChans, 0, sizeof(int) * elCount);
1203  } else {
1204  mChans = mStaticChans;
1205  memset (mChans, 0, sizeof(int) * kStaticElCount);
1206  }
1207  for (unsigned int i = 0; i < elCount; ++i) {
1208  UInt32 numChans;
1209  if (inAU.NumberChannels (inScope, i, numChans)) return;
1210  mChans[i] = numChans;
1211  }
1212  mNumEls = elCount;
1213 }
1214 
1215 CAAUChanHelper::CAAUChanHelper(UInt32 inMaxElems)
1216  : mNumEls(inMaxElems), mDidAllocate(false)
1217 {
1218  if (inMaxElems > kStaticElCount) {
1219  mChans = new UInt32[inMaxElems];
1220  mDidAllocate = true;
1221  memset (mChans, 0, sizeof(int) * inMaxElems);
1222  } else {
1223  mChans = mStaticChans;
1224  memset (mChans, 0, sizeof(int) * kStaticElCount);
1225  }
1226 }
1227 
1228 CAAUChanHelper::~CAAUChanHelper()
1229 {
1230  if (mDidAllocate) delete [] mChans;
1231 }
1232 
1233 CAAUChanHelper& CAAUChanHelper::operator= (const CAAUChanHelper &c)
1234 {
1235  if (mDidAllocate) delete [] mChans;
1236  if (c.mDidAllocate) {
1237  mChans = new UInt32[c.mNumEls];
1238  mDidAllocate = true;
1239  } else {
1240  mDidAllocate = false;
1241  mChans = mStaticChans;
1242  }
1243  memcpy (mChans, c.mChans, c.mNumEls * sizeof(int));
1244 
1245  return *this;
1246 }
1247 
1248 
1249 #pragma mark __Print Utilities
1250 
1251 void CAAudioUnit::Print (FILE* file) const
1252 {
1253  fprintf (file, "AudioUnit:%p\n", AU());
1254  if (IsValid()) {
1255  fprintf (file, "\tnode=%ld\t", (long)GetAUNode()); Comp().Print (file);
1256  }
1257 }
bool TTFOUNDATION_EXPORT operator==(const TTObject &anObject, const TTObject &anotherObject)
Compare two objects for equality.
Definition: TTObject.cpp:167