alternative new version of the AppleUtility library
[ardour.git] / libs / appleutility / CoreAudio / AudioUnits / AUPublic / OtherBases / AUPannerBase.cpp
1 /*
2      File: AUPannerBase.cpp
3  Abstract: AUPannerBase.h
4   Version: 1.1
5  
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12  
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28  
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34  
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43  
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45  
46 */
47 #include "AUPannerBase.h"
48 #include "CABundleLocker.h"
49 #include <AudioToolbox/AudioToolbox.h>
50 #include <Accelerate/Accelerate.h>
51
52 static bool sLocalized = false;
53
54 static CFStringRef kPanner_Azimuth_Name = CFSTR("azimuth");     
55 static CFStringRef kPanner_Elevation_Name = CFSTR("elevation");         
56 static CFStringRef kPanner_Distance_Name = CFSTR("distance");
57
58 static CFStringRef kPanner_CoordScale_Name = CFSTR("coordinate scale");         
59 static CFStringRef kPanner_RefDistance_Name = CFSTR("reference distance");      
60 static CFStringRef kPanner_Gain_Name = CFSTR("gain");   
61
62 static Float32 kPannerParamDefault_Azimuth = 0.;
63 static Float32 kPannerParamDefault_Elevation = 0.;
64 static Float32 kPannerParamDefault_Distance = 1.;
65
66 static Float32 kPannerParamDefault_CoordScale = 10.;
67 static Float32 kPannerParamDefault_RefDistance = 1.;
68 static Float32 kPannerParamDefault_Gain = 1.;
69
70 //_____________________________________________________________________________
71 //
72 AUPannerBase::AUPannerBase(AudioComponentInstance inAudioUnit)
73     : AUBase(inAudioUnit, 1, 1), mBypassEffect(false)
74 {
75         {
76                 CABundleLocker lock;
77                 if (!sLocalized)
78                 {               
79                         CFBundleRef bundle = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.audio.units.Components") );
80                         if (bundle != NULL)
81                         {
82                                 kPanner_Azimuth_Name    = CFCopyLocalizedStringFromTableInBundle(kPanner_Azimuth_Name,    CFSTR("AudioUnits"), bundle, CFSTR(""));
83                                 kPanner_Elevation_Name  = CFCopyLocalizedStringFromTableInBundle(kPanner_Elevation_Name,  CFSTR("AudioUnits"), bundle, CFSTR(""));
84                                 kPanner_Distance_Name   = CFCopyLocalizedStringFromTableInBundle(kPanner_Distance_Name,   CFSTR("AudioUnits"), bundle, CFSTR(""));
85                                 
86                                 kPanner_CoordScale_Name  = CFCopyLocalizedStringFromTableInBundle(kPanner_CoordScale_Name,  CFSTR("AudioUnits"), bundle, CFSTR(""));
87                                 kPanner_RefDistance_Name = CFCopyLocalizedStringFromTableInBundle(kPanner_RefDistance_Name, CFSTR("AudioUnits"), bundle, CFSTR(""));
88                                 kPanner_Gain_Name            = CFCopyLocalizedStringFromTableInBundle(kPanner_Gain_Name,        CFSTR("AudioUnits"), bundle, CFSTR(""));
89
90                         }
91                         
92                         sLocalized = true;
93                 }
94         }
95         
96         CreateElements();
97         
98     SetParameter(kPannerParam_Azimuth, kPannerParamDefault_Azimuth);
99     SetParameter(kPannerParam_Elevation, kPannerParamDefault_Elevation);
100     SetParameter(kPannerParam_Distance, kPannerParamDefault_Distance);
101     
102     SetParameter(kPannerParam_CoordScale, kPannerParamDefault_CoordScale);
103     SetParameter(kPannerParam_RefDistance, kPannerParamDefault_RefDistance);
104     SetParameter(kPannerParam_Gain, kPannerParamDefault_Gain);
105 }
106
107 //_____________________________________________________________________________
108 //
109 AUPannerBase::~AUPannerBase()
110 {
111     Cleanup();
112 }
113
114 //_____________________________________________________________________________
115 //
116 /*! @method Initialize */
117 OSStatus                        AUPannerBase::Initialize()
118 {
119         OSStatus err = noErr;
120         AllocBypassMatrix();
121         err = UpdateBypassMatrix();
122         return err;
123 }
124
125 //_____________________________________________________________________________
126 //
127 /*! @method AllocBypassMatrix */
128 void            AUPannerBase::AllocBypassMatrix()
129 {
130         UInt32 inChannels = GetNumberOfInputChannels();
131         UInt32 outChannels = GetNumberOfOutputChannels();
132         mBypassMatrix.alloc(inChannels * outChannels, true);
133 }
134
135 static AudioChannelLayoutTag DefaultTagForNumberOfChannels(UInt32 inNumberChannels)
136 {
137         switch (inNumberChannels) {
138                 case 1: return kAudioChannelLayoutTag_Mono; 
139                 case 2: return kAudioChannelLayoutTag_Stereo; 
140                 case 4: return kAudioChannelLayoutTag_Quadraphonic; 
141                 case 5: return kAudioChannelLayoutTag_AudioUnit_5_0; 
142                 case 6: return kAudioChannelLayoutTag_AudioUnit_6_0; 
143                 case 7: return kAudioChannelLayoutTag_AudioUnit_7_0; 
144                 case 8: return kAudioChannelLayoutTag_AudioUnit_8; 
145
146                 default: return 0xFFFF0000 | inNumberChannels;
147         }
148 }
149
150 //_____________________________________________________________________________
151 //
152 /*! @method UpdateBypassMatrix */
153 OSStatus                        AUPannerBase::SetDefaultChannelLayoutsIfNone()
154 {
155         OSStatus err = noErr;
156         
157         // if layout has not been set, then guess layout from number of channels
158         UInt32 inChannels = GetNumberOfInputChannels();
159         AudioChannelLayout inputLayoutSubstitute;
160         const AudioChannelLayout* inputLayout = &GetInputLayout();
161         if (inputLayout == NULL || inputLayout->mChannelLayoutTag == 0) {
162                 inputLayout = &inputLayoutSubstitute;
163                 inputLayoutSubstitute.mNumberChannelDescriptions = 0;
164                 inputLayoutSubstitute.mChannelBitmap = 0;
165                 inputLayoutSubstitute.mChannelLayoutTag = DefaultTagForNumberOfChannels(inChannels);
166
167                 mInputLayout = &inputLayoutSubstitute;
168                 err = SetAudioChannelLayout(kAudioUnitScope_Input, 0, &GetInputLayout());
169                 if (err) return err;
170         }
171                 
172         // if layout has not been set, then guess layout from number of channels
173         UInt32 outChannels = GetNumberOfOutputChannels();
174         AudioChannelLayout outputLayoutSubstitute;
175         const AudioChannelLayout* outputLayout = &GetOutputLayout();
176         if (outputLayout == NULL || outputLayout->mChannelLayoutTag == 0) {
177                 outputLayout = &outputLayoutSubstitute;
178                 outputLayoutSubstitute.mNumberChannelDescriptions = 0;
179                 outputLayoutSubstitute.mChannelBitmap = 0;
180                 outputLayoutSubstitute.mChannelLayoutTag = DefaultTagForNumberOfChannels(outChannels);
181
182                 mOutputLayout = &outputLayoutSubstitute;
183                 err = SetAudioChannelLayout(kAudioUnitScope_Output, 0, &GetOutputLayout());
184                 if (err) return err;
185         }
186         
187         return err;
188 }
189
190
191         
192 OSStatus                        AUPannerBase::UpdateBypassMatrix()
193 {
194         OSStatus err = SetDefaultChannelLayoutsIfNone();
195         if (err) return err;
196         
197         UInt32 inChannels = GetNumberOfInputChannels();
198         UInt32 outChannels = GetNumberOfOutputChannels();       
199         
200         const AudioChannelLayout* inoutACL[2];
201         
202         inoutACL[0] = &GetInputLayout();
203         inoutACL[1] = &GetOutputLayout();
204
205         mBypassMatrix.alloc(inChannels * outChannels, true);
206         
207         UInt32 propSize = inChannels * outChannels * sizeof(Float32);
208         
209         err = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap, sizeof(inoutACL), inoutACL, &propSize, mBypassMatrix());
210         if (err) {
211                 // if there is an error, use a diagonal matrix.
212                 Float32* bypass = mBypassMatrix();
213                 for (UInt32 chan = 0; chan < std::min(inChannels, outChannels); ++chan)
214                 {
215                         float *amp = bypass + (chan * outChannels + chan);
216                         *amp = 1.;
217                 }
218         }
219
220     return noErr;
221 }
222
223 //_____________________________________________________________________________
224 //
225 /*! @method Cleanup */
226 void                            AUPannerBase::Cleanup()
227 {
228     
229 }
230
231
232 //_____________________________________________________________________________
233 //
234 /*! @method Reset */
235 OSStatus                        AUPannerBase::Reset(            AudioUnitScope                          inScope,
236                                                                                 AudioUnitElement                        inElement)
237 {
238     return AUBase::Reset(inScope, inElement);
239 }
240
241 //_____________________________________________________________________________
242 //
243 /*! @method GetParameterInfo */
244 OSStatus                        AUPannerBase::GetParameterInfo( AudioUnitScope                  inScope,
245                                                                                                 AudioUnitParameterID    inParameterID,
246                                                                                                 AudioUnitParameterInfo  &outParameterInfo )
247 {
248         OSStatus result = noErr;
249
250         outParameterInfo.flags =        kAudioUnitParameterFlag_IsWritable
251                                                 +               kAudioUnitParameterFlag_IsReadable;
252                 
253         if (inScope == kAudioUnitScope_Global) {
254                 
255                 switch(inParameterID)
256                 {
257                         case kPannerParam_Azimuth:
258                                 AUBase::FillInParameterName (outParameterInfo, kPanner_Azimuth_Name, false);
259                                 outParameterInfo.unit = kAudioUnitParameterUnit_Degrees;
260                                 outParameterInfo.minValue = -180.;
261                                 outParameterInfo.maxValue = 180;
262                                 outParameterInfo.defaultValue = kPannerParamDefault_Azimuth;
263                         break;
264
265                         case kPannerParam_Elevation:
266                                 AUBase::FillInParameterName (outParameterInfo, kPanner_Elevation_Name, false);
267                                 outParameterInfo.unit = kAudioUnitParameterUnit_Degrees;
268                                 outParameterInfo.minValue = -90.;
269                                 outParameterInfo.maxValue = 90;
270                                 outParameterInfo.defaultValue = kPannerParamDefault_Elevation;
271                         break;
272
273                         case kPannerParam_Distance:
274                                 AUBase::FillInParameterName (outParameterInfo, kPanner_Distance_Name, false);
275                                 outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
276                                 outParameterInfo.minValue = 0.0;
277                                 outParameterInfo.maxValue = 1.;
278                                 outParameterInfo.defaultValue = kPannerParamDefault_Distance;
279                                 outParameterInfo.flags += kAudioUnitParameterFlag_IsHighResolution;
280                                 //outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
281                         break;
282  
283                         case kPannerParam_CoordScale:
284                                 AUBase::FillInParameterName (outParameterInfo, kPanner_CoordScale_Name, false);
285                                 outParameterInfo.unit = kAudioUnitParameterUnit_Meters;
286                                 outParameterInfo.minValue = 0.01;
287                                 outParameterInfo.maxValue = 1000.;
288                                 outParameterInfo.defaultValue = kPannerParamDefault_CoordScale;
289                                 outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
290                         break;
291
292                         case kPannerParam_RefDistance:
293                                 AUBase::FillInParameterName (outParameterInfo, kPanner_RefDistance_Name, false);
294                                 outParameterInfo.unit = kAudioUnitParameterUnit_Meters;
295                                 outParameterInfo.minValue = 0.01;
296                                 outParameterInfo.maxValue = 1000.;
297                                 outParameterInfo.defaultValue = kPannerParamDefault_RefDistance;
298                                 outParameterInfo.flags += kAudioUnitParameterFlag_DisplayLogarithmic;
299                         break;
300
301                         case kPannerParam_Gain:
302                                 AUBase::FillInParameterName (outParameterInfo, kPanner_Gain_Name, false);
303                                 outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
304                                 outParameterInfo.minValue = 0.;
305                                 outParameterInfo.maxValue = 1.;
306                                 outParameterInfo.defaultValue = kPannerParamDefault_Gain;
307                         break;
308
309                         default:
310                                 result = kAudioUnitErr_InvalidParameter;
311                                 break;
312                 }
313         } else {
314                 result = kAudioUnitErr_InvalidParameter;
315         }
316         
317         return result;
318 }                                                                                               
319
320 //_____________________________________________________________________________
321 //
322 OSStatus        AUPannerBase::GetParameter(             AudioUnitParameterID                    inParamID,
323                                                                                                         AudioUnitScope                                  inScope,
324                                                                                                         AudioUnitElement                                inElement,
325                                                                                                         Float32 &                                               outValue)
326 {
327         if (inScope != kAudioUnitScope_Global) 
328                 return kAudioUnitErr_InvalidScope;
329                 
330         outValue = Globals()->GetParameter(inParamID);
331
332         return noErr;
333 }
334
335                                                                                         
336 //_____________________________________________________________________________
337 //
338 OSStatus        AUPannerBase::SetParameter(             AudioUnitParameterID                    inParamID,
339                                                                                                         AudioUnitScope                                  inScope,
340                                                                                                         AudioUnitElement                                inElement,
341                                                                                                         Float32                                                 inValue,
342                                                                                                         UInt32                                                  inBufferOffsetInFrames)
343 {
344         if (inScope != kAudioUnitScope_Global) 
345                 return kAudioUnitErr_InvalidScope;
346
347         Globals()->SetParameter(inParamID, inValue);
348         
349         return noErr;
350 }
351
352
353
354 //_____________________________________________________________________________
355 //
356 /*! @method GetPropertyInfo */
357 OSStatus                        AUPannerBase::GetPropertyInfo (AudioUnitPropertyID      inID,
358                                                                                 AudioUnitScope                          inScope,
359                                                                                 AudioUnitElement                        inElement,
360                                                                                 UInt32 &                                        outDataSize,
361                                                                                 Boolean &                                       outWritable)
362 {
363         OSStatus err = noErr;
364         switch (inID) {
365                 case kAudioUnitProperty_BypassEffect:
366                         if (inScope != kAudioUnitScope_Global)
367                                 return kAudioUnitErr_InvalidScope;
368                                 
369                         outWritable = true;
370                         outDataSize = sizeof (UInt32);
371                         break;
372                 default:
373                         err = AUBase::GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
374         }
375         return err;
376 }
377
378 //_____________________________________________________________________________
379 //
380 /*! @method GetProperty */
381 OSStatus                        AUPannerBase::GetProperty (AudioUnitPropertyID          inID,
382                                                                                 AudioUnitScope                          inScope,
383                                                                                 AudioUnitElement                        inElement,
384                                                                                 void *                                          outData)
385 {
386         OSStatus err = noErr;
387         switch (inID) 
388         {
389                 case kAudioUnitProperty_BypassEffect:
390                         if (inScope != kAudioUnitScope_Global)
391                                 return kAudioUnitErr_InvalidScope;
392
393                         *((UInt32*)outData) = (IsBypassEffect() ? 1 : 0);
394                         break;
395                 default:
396                         err = AUBase::GetProperty(inID, inScope, inElement, outData);
397         }
398         return err;
399 }
400
401 //_____________________________________________________________________________
402 //
403 /*! @method SetProperty */
404 OSStatus                        AUPannerBase::SetProperty(AudioUnitPropertyID           inID,
405                                                                                 AudioUnitScope                          inScope,
406                                                                                 AudioUnitElement                        inElement,
407                                                                                 const void *                            inData,
408                                                                                 UInt32                                          inDataSize)
409 {
410         switch (inID) 
411         {
412                 case kAudioUnitProperty_BypassEffect:
413                                 if (inDataSize < sizeof(UInt32))
414                                         return kAudioUnitErr_InvalidPropertyValue;
415                                 bool tempNewSetting = *((UInt32*)inData) != 0;
416                                         // we're changing the state of bypass
417                                 if (tempNewSetting != IsBypassEffect()) 
418                                 {
419                                         if (!tempNewSetting && IsBypassEffect() && IsInitialized()) // turning bypass off and we're initialized
420                                                 Reset(0, 0);
421                                         SetBypassEffect (tempNewSetting);
422                                 }
423                                 return noErr;
424         }
425     return AUBase::SetProperty(inID, inScope, inElement, inData, inDataSize);
426 }
427
428
429 //_____________________________________________________________________________
430 //
431 /*! @method StreamFormatWritable */
432 bool                            AUPannerBase::StreamFormatWritable (AudioUnitScope      scope,
433                                                                                 AudioUnitElement                        element)
434 {
435     return true;
436 }
437
438 //_____________________________________________________________________________
439 //
440 /*! @method ChangeStreamFormat */
441 OSStatus                        AUPannerBase::ChangeStreamFormat (
442                                                                         AudioUnitScope                                          inScope,
443                                                                         AudioUnitElement                                        inElement,
444                                                                         const CAStreamBasicDescription &        inPrevFormat,
445                                                                         const CAStreamBasicDescription &        inNewFormat)
446 {
447         if (inScope == kAudioUnitScope_Input && !InputChannelConfigIsSupported(inNewFormat.NumberChannels())) 
448                 return kAudioUnitErr_FormatNotSupported;
449                 
450         if (inScope == kAudioUnitScope_Output && !OutputChannelConfigIsSupported(inNewFormat.NumberChannels())) 
451                 return kAudioUnitErr_FormatNotSupported;
452                 
453         if (inNewFormat.NumberChannels() != inPrevFormat.NumberChannels())
454                 RemoveAudioChannelLayout(inScope, inElement);
455                 
456         return AUBase::ChangeStreamFormat(inScope, inElement, inPrevFormat, inNewFormat);
457 }
458
459 //_____________________________________________________________________________
460 //
461 /*! @method Render */
462 OSStatus        AUPannerBase::Render(AudioUnitRenderActionFlags &               ioActionFlags,
463                                                                         const AudioTimeStamp &                  inTimeStamp,
464                                                                         UInt32                                                  inNumberFrames)
465 {
466         if (IsBypassEffect()) 
467                 return BypassRender(ioActionFlags, inTimeStamp, inNumberFrames);
468         else 
469                 return PannerRender(ioActionFlags, inTimeStamp, inNumberFrames);
470 }
471
472 //_____________________________________________________________________________
473 //
474 /*! @method Render */
475 OSStatus        AUPannerBase::BypassRender(AudioUnitRenderActionFlags &         ioActionFlags,
476                                                                         const AudioTimeStamp &                  inTimeStamp,
477                                                                         UInt32                                                  inNumberFrames)
478 {
479         AudioUnitRenderActionFlags xflags = 0;
480         OSStatus result = PullInput(0, xflags, inTimeStamp, inNumberFrames);
481         if (result) return false;
482         bool isSilent = xflags & kAudioUnitRenderAction_OutputIsSilence;
483
484         AudioBufferList& outputBufferList = GetOutput(0)->GetBufferList();
485         AUBufferList::ZeroBuffer(outputBufferList);
486         
487         if (!isSilent) 
488         {
489                 UInt32 inChannels = GetNumberOfInputChannels();
490                 UInt32 outChannels = GetNumberOfOutputChannels();
491                 Float32* bypass = mBypassMatrix();
492                 for (UInt32 outChan = 0; outChan < outChannels; ++outChan)
493                 {
494                         float* outData = GetOutput(0)->GetChannelData(outChan); 
495                         
496                         for (UInt32 inChan = 0; inChan < inChannels; ++inChan)
497                         {
498                                 float* inData = GetInput(0)->GetChannelData(inChan);
499                                 float *amp = bypass + (inChan * outChannels + outChan);
500                                 vDSP_vsma(inData, 1, amp, outData, 1, outData, 1, inNumberFrames);
501                         }
502                 }
503         }
504     return noErr;
505 }
506
507 //_____________________________________________________________________________
508 //
509 UInt32          AUPannerBase::GetAudioChannelLayout(    AudioUnitScope                          inScope,
510                                                                                                         AudioUnitElement                        inElement,
511                                                                                                         AudioChannelLayout *            outLayoutPtr,
512                                                                                                         Boolean &                                       outWritable)
513 {       
514         SetDefaultChannelLayoutsIfNone();
515         
516         outWritable = true;
517         
518         CAAudioChannelLayout* caacl = NULL;
519         switch (inScope) 
520         {
521                 case kAudioUnitScope_Input:
522                         caacl = &mInputLayout;
523                         break;
524                 case kAudioUnitScope_Output:
525                         caacl = &mOutputLayout;
526                         break;
527                 default:
528                         COMPONENT_THROW(kAudioUnitErr_InvalidScope);
529         }
530         
531         if (inElement != 0)
532                 COMPONENT_THROW(kAudioUnitErr_InvalidElement);
533                 
534         UInt32 size = caacl->IsValid() ? caacl->Size() : 0;
535         if (size > 0 && outLayoutPtr)
536                 memcpy(outLayoutPtr, &caacl->Layout(), size);
537                 
538         return size;
539 }
540
541 //_____________________________________________________________________________
542 //
543 OSStatus        AUPannerBase::RemoveAudioChannelLayout(         AudioUnitScope                          inScope,
544                                                                                                                 AudioUnitElement                        inElement)
545 {
546         CAAudioChannelLayout* caacl = NULL;
547         switch (inScope) 
548         {
549                 case kAudioUnitScope_Input:
550                         caacl = &mInputLayout;
551                         break;
552                 case kAudioUnitScope_Output:
553                         caacl = &mOutputLayout;
554                         break;
555                 default:
556                         return kAudioUnitErr_InvalidScope;
557         }
558         
559         if (inElement != 0)
560                 return kAudioUnitErr_InvalidElement;
561         
562         *caacl = NULL;
563         return noErr;
564 }
565
566 //_____________________________________________________________________________
567 //
568 OSStatus        AUPannerBase::SetAudioChannelLayout(            AudioUnitScope                          inScope, 
569                                                                                                                 AudioUnitElement                        inElement,
570                                                                                                                 const AudioChannelLayout *      inLayout)
571 {
572         if (!inLayout)
573                 return RemoveAudioChannelLayout(inScope, inElement);
574
575         if (!ChannelLayoutTagIsSupported(inScope, inElement, inLayout->mChannelLayoutTag))
576                 return kAudioUnitErr_FormatNotSupported;
577         
578         CAAudioChannelLayout* caacl = NULL;
579         UInt32 currentChannels;
580         switch (inScope) 
581         {
582                 case kAudioUnitScope_Input:
583                         caacl = &mInputLayout;
584                         currentChannels = GetNumberOfInputChannels();
585                         break;
586                 case kAudioUnitScope_Output:
587                         caacl = &mOutputLayout;
588                         currentChannels = GetNumberOfOutputChannels();
589                         break;
590                 default:
591                         return kAudioUnitErr_InvalidScope;
592         }
593         
594         if (inElement != 0)
595                 return kAudioUnitErr_InvalidElement;
596
597         UInt32 numChannelsInLayout = CAAudioChannelLayout::NumberChannels(*inLayout);
598         if (currentChannels != numChannelsInLayout)
599                 return kAudioUnitErr_InvalidPropertyValue;
600         
601         *caacl = inLayout;
602         if (IsInitialized())
603                 UpdateBypassMatrix();
604                 
605         return noErr;
606 }
607
608 //_____________________________________________________________________________
609 //
610 UInt32 AUPannerBase::GetChannelLayoutTags(              AudioUnitScope                          inScope,
611                                                                                                 AudioUnitElement                        inElement, 
612                                                                                                 AudioChannelLayoutTag*          outTags)
613 {
614         switch (inScope) 
615         {
616                 case kAudioUnitScope_Input:
617                         if (outTags) {
618                                 outTags[0] = kAudioChannelLayoutTag_Mono;
619                                 outTags[1] = kAudioChannelLayoutTag_Stereo;
620                                 outTags[2] = kAudioChannelLayoutTag_Ambisonic_B_Format;
621                         }
622                         return 3;
623                 case kAudioUnitScope_Output:
624                         if (outTags) {
625                                 outTags[0] = kAudioChannelLayoutTag_Stereo;
626                                 outTags[1] = kAudioChannelLayoutTag_Quadraphonic;
627                                 outTags[2] = kAudioChannelLayoutTag_AudioUnit_5_0;
628                                 outTags[3] = kAudioChannelLayoutTag_AudioUnit_6_0;
629                                 outTags[4] = kAudioChannelLayoutTag_AudioUnit_7_0;
630                                 outTags[5] = kAudioChannelLayoutTag_AudioUnit_7_0_Front;
631                                 outTags[6] = kAudioChannelLayoutTag_AudioUnit_8;
632                         }
633                         return 7;
634                 default: {
635                         OSStatus err = kAudioUnitErr_InvalidScope;
636                         throw err;
637                 }
638         }
639 }
640
641
642
643 //_____________________________________________________________________________
644 //
645 bool    AUPannerBase::ChannelConfigIsSupported()
646 {
647         UInt32 inChannels = GetNumberOfInputChannels();
648         UInt32 outChannels = GetNumberOfOutputChannels();
649         const AUChannelInfo* cinfo = NULL;
650         UInt32 numConfigs = SupportedNumChannels(&cinfo);
651         for (UInt32 i = 0; i < numConfigs; ++i)
652         {
653                 if (cinfo[i].inChannels == (SInt16)inChannels && cinfo[i].outChannels == (SInt16)outChannels)
654                         return true;
655         }
656         return false;
657 }
658
659
660 //_____________________________________________________________________________
661 //
662 bool    AUPannerBase::InputChannelConfigIsSupported(UInt32 inNumberChannels)
663 {
664         const AUChannelInfo* cinfo = NULL;
665         UInt32 numConfigs = SupportedNumChannels(&cinfo);
666         for (UInt32 i = 0; i < numConfigs; ++i)
667         {
668                 if (cinfo[i].inChannels == (SInt16)inNumberChannels)
669                         return true;
670         }
671         return false;
672 }
673
674 //_____________________________________________________________________________
675 //
676 bool    AUPannerBase::OutputChannelConfigIsSupported(UInt32 inNumberChannels)
677 {
678         const AUChannelInfo* cinfo = NULL;
679         UInt32 numConfigs = SupportedNumChannels(&cinfo);
680         for (UInt32 i = 0; i < numConfigs; ++i)
681         {
682                 if (cinfo[i].outChannels == (SInt16)inNumberChannels)
683                         return true;
684         }
685         return false;
686 }
687
688 //_____________________________________________________________________________
689 //
690 bool    AUPannerBase::ChannelLayoutTagIsSupported(              AudioUnitScope                  inScope, 
691                                                                                                                 AudioUnitElement                inElement, 
692                                                                                                                 AudioChannelLayoutTag   inTag)
693 {
694         UInt32 numTags = GetChannelLayoutTags(inScope, inElement, NULL);
695         CAAutoFree<AudioChannelLayoutTag> tags(numTags);
696         GetChannelLayoutTags(inScope, inElement, tags());
697         
698         for (UInt32 i = 0; i < numTags; ++i)
699         {
700                 if (tags[i] == inTag)
701                         return true;
702         }
703         
704         return false;
705 }
706