/*
     File: AUBase.cpp
 Abstract: AUBase.h
  Version: 1.1

 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 Inc. ("Apple") in consideration of your agreement to the following
 terms, and your use, installation, modification or redistribution of
 this Apple software constitutes acceptance of these terms.  If you do
 not agree with these terms, please do not use, install, modify or
 redistribute this Apple software.

 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software.
 Neither the name, trademarks, service marks or logos of Apple Inc. may
 be used to endorse or promote products derived from the Apple Software
 without specific prior written permission from Apple.  Except as
 expressly stated in this notice, no other rights or licenses, express or
 implied, are granted by Apple herein, including but not limited to any
 patent rights that may be infringed by your derivative works or by other
 works in which the Apple Software may be incorporated.

 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.

 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

 Copyright (C) 2014 Apple Inc. All Rights Reserved.

*/
#include "AUBase.h"
#if !CA_USE_AUDIO_PLUGIN_ONLY
    #include "AUDispatch.h"
#endif
#include "AUInputElement.h"
#include "AUOutputElement.h"
#include <algorithm>
#include <syslog.h>
#include "CAAudioChannelLayout.h"
#include "CAHostTimeBase.h"
#include "CAVectorUnit.h"
#include "CAXException.h"



#if TARGET_OS_MAC && (TARGET_CPU_X86 || TARGET_CPU_X86_64)
    // our compiler does ALL floating point with SSE
    inline int  GETCSR ()    { int _result; asm volatile ("stmxcsr %0" : "=m" (*&_result) ); return _result; }
    inline void SETCSR (int a)    { int _temp = a; asm volatile( "ldmxcsr %0" : : "m" (*&_temp ) ); }

    #define DISABLE_DENORMALS int _savemxcsr = GETCSR(); SETCSR(_savemxcsr | 0x8040);
    #define RESTORE_DENORMALS SETCSR(_savemxcsr);
#else
    #define DISABLE_DENORMALS
    #define RESTORE_DENORMALS
#endif

static bool sAUBaseCFStringsInitialized = false;
// this is used for the presets
static CFStringRef kUntitledString = NULL;
//these are the current keys for the class info document
static CFStringRef kVersionString = NULL;
static CFStringRef kTypeString = NULL;
static CFStringRef kSubtypeString = NULL;
static CFStringRef kManufacturerString = NULL;
static CFStringRef kDataString = NULL;
static CFStringRef kNameString = NULL;
static CFStringRef kRenderQualityString = NULL;
static CFStringRef kCPULoadString = NULL;
static CFStringRef kElementNameString = NULL;
static CFStringRef kPartString = NULL;

SInt32 AUBase::sVectorUnitType = kVecUninitialized;

//_____________________________________________________________________________
//
AUBase::AUBase( AudioComponentInstance          inInstance,
                UInt32                          numInputElements,
                UInt32                          numOutputElements,
                UInt32                          numGroupElements) :
    ComponentBase(inInstance),
    mElementsCreated(false),
    mInitialized(false),
    mHasBegunInitializing(false),
    mInitNumInputEls(numInputElements), mInitNumOutputEls(numOutputElements),
#if !CA_BASIC_AU_FEATURES
    mInitNumGroupEls(numGroupElements),
#endif
    mRenderCallbacksTouched(false),
    mRenderThreadID (NULL),
    mWantsRenderThreadID (false),
    mLastRenderError(0),
    mUsesFixedBlockSize(false),
    mBuffersAllocated(false),
    mLogString (NULL),
    mNickName (NULL),
    mAUMutex(NULL)
    #if !CA_NO_AU_UI_FEATURES
        ,
        mContextName(NULL)
    #endif
{
    ResetRenderTime ();

    if(!sAUBaseCFStringsInitialized)
    {
        kUntitledString = CFSTR("Untitled");
        kVersionString = CFSTR(kAUPresetVersionKey);
        kTypeString = CFSTR(kAUPresetTypeKey);
        kSubtypeString = CFSTR(kAUPresetSubtypeKey);
        kManufacturerString = CFSTR(kAUPresetManufacturerKey);
        kDataString = CFSTR(kAUPresetDataKey);
        kNameString = CFSTR(kAUPresetNameKey);
        kRenderQualityString = CFSTR(kAUPresetRenderQualityKey);
        kCPULoadString = CFSTR(kAUPresetCPULoadKey);
        kElementNameString = CFSTR(kAUPresetElementNameKey);
        kPartString = CFSTR(kAUPresetPartKey);
        sAUBaseCFStringsInitialized = true;
    }

    if (sVectorUnitType == kVecUninitialized) {
        sVectorUnitType = CAVectorUnit::GetVectorUnitType() ;
    }

    mAudioUnitAPIVersion = 2;

    SetMaxFramesPerSlice(kAUDefaultMaxFramesPerSlice);

    GlobalScope().Initialize(this, kAudioUnitScope_Global, 1);

#if !CA_NO_AU_UI_FEATURES
    memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo));
#endif


    mCurrentPreset.presetNumber = -1;
    mCurrentPreset.presetName = kUntitledString;
    CFRetain (mCurrentPreset.presetName);
}

//_____________________________________________________________________________
//
AUBase::~AUBase()
{
    if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName);
#if !CA_NO_AU_UI_FEATURES
    if (mContextName) CFRelease (mContextName);
#endif
    if (mLogString) delete [] mLogString;
    if (mNickName) CFRelease(mNickName);
}

//_____________________________________________________________________________
//
void    AUBase::CreateElements()
{
    if (!mElementsCreated) {
        Inputs().Initialize(this, kAudioUnitScope_Input, mInitNumInputEls);
        Outputs().Initialize(this, kAudioUnitScope_Output, mInitNumOutputEls);
#if !CA_BASIC_AU_FEATURES
        Groups().Initialize(this, kAudioUnitScope_Group, mInitNumGroupEls);
#endif
        CreateExtendedElements();

        mElementsCreated = true;
    }
}

//_____________________________________________________________________________
//
void    AUBase::SetMaxFramesPerSlice(UInt32 nFrames)
{
    mMaxFramesPerSlice = nFrames;
    if (mBuffersAllocated)
        ReallocateBuffers();
    PropertyChanged(kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0);
}

//_____________________________________________________________________________
//
OSStatus            AUBase::CanSetMaxFrames() const
{
    return IsInitialized() ? kAudioUnitErr_Initialized : OSStatus(noErr);
}

//_____________________________________________________________________________
//
void                AUBase::ReallocateBuffers()
{
    CreateElements();

    UInt32 nOutputs = Outputs().GetNumberOfElements();
    for (UInt32 i = 0; i < nOutputs; ++i) {
        AUOutputElement *output = GetOutput(i);
        output->AllocateBuffer();   // does no work if already allocated
    }
    UInt32 nInputs = Inputs().GetNumberOfElements();
    for (UInt32 i = 0; i < nInputs; ++i) {
        AUInputElement *input = GetInput(i);
        input->AllocateBuffer();    // does no work if already allocated
    }
    mBuffersAllocated = true;
}

//_____________________________________________________________________________
//
void                AUBase::DeallocateIOBuffers()
{
    if (!mBuffersAllocated)
        return;

    UInt32 nOutputs = Outputs().GetNumberOfElements();
    for (UInt32 i = 0; i < nOutputs; ++i) {
        AUOutputElement *output = GetOutput(i);
        output->DeallocateBuffer();
    }
    UInt32 nInputs = Inputs().GetNumberOfElements();
    for (UInt32 i = 0; i < nInputs; ++i) {
        AUInputElement *input = GetInput(i);
        input->DeallocateBuffer();
    }
    mBuffersAllocated = false;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::DoInitialize()
{
    OSStatus result = noErr;

    if (!mInitialized) {
        result = Initialize();
        if (result == noErr) {
            if (CanScheduleParameters())
                mParamList.reserve(24);
            mHasBegunInitializing = true;
            ReallocateBuffers();    // calls CreateElements()
            mInitialized = true;    // signal that it's okay to render
            CAMemoryBarrier();
        }
    }

    return result;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::Initialize()
{
    return noErr;
}

//_____________________________________________________________________________
//
void                AUBase::PreDestructor()
{
    // this is called from the ComponentBase dispatcher, which doesn't know anything about our (optional) lock
    CAMutex::Locker lock(mAUMutex);
    DoCleanup();
}

//_____________________________________________________________________________
//
void                AUBase::DoCleanup()
{
    if (mInitialized)
        Cleanup();

    DeallocateIOBuffers();
    ResetRenderTime ();

    mInitialized = false;
    mHasBegunInitializing = false;
}

//_____________________________________________________________________________
//
void                AUBase::Cleanup()
{
}

//_____________________________________________________________________________
//
OSStatus            AUBase::Reset(                  AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement)
{
    ResetRenderTime ();
    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::DispatchGetPropertyInfo(AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    UInt32 &                        outDataSize,
                                                    Boolean &                       outWritable)
{
    OSStatus result = noErr;
    bool validateElement = true;

    switch (inID) {
    case kAudioUnitProperty_MakeConnection:
        ca_require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(AudioUnitConnection);
        outWritable = true;
        break;


    case kAudioUnitProperty_SetRenderCallback:
        ca_require(AudioUnitAPIVersion() > 1, InvalidProperty);
        ca_require(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(AURenderCallbackStruct);
        outWritable = true;
        break;

    case kAudioUnitProperty_StreamFormat:
        outDataSize = sizeof(CAStreamBasicDescription);
        outWritable = IsStreamFormatWritable(inScope, inElement);
        break;

    case kAudioUnitProperty_SampleRate:
        outDataSize = sizeof(Float64);
        outWritable = IsStreamFormatWritable(inScope, inElement);
        break;

    case kAudioUnitProperty_ClassInfo:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(CFPropertyListRef);
        outWritable = true;
        break;

    case kAudioUnitProperty_FactoryPresets:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        result = GetPresets(NULL);
        if (!result) {
            outDataSize = sizeof(CFArrayRef);
            outWritable = false;
        }
        break;

    case kAudioUnitProperty_PresentPreset:
#if !CA_USE_AUDIO_PLUGIN_ONLY
#ifndef __LP64__
    case kAudioUnitProperty_CurrentPreset:
#endif
#endif
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(AUPreset);
        outWritable = true;
        break;

    case kAudioUnitProperty_ElementName:
        outDataSize = sizeof (CFStringRef);
        outWritable = true;
        break;

    case kAudioUnitProperty_ParameterList:
        {
            UInt32 nparams = 0;
            result = GetParameterList(inScope, NULL, nparams);

            outDataSize = sizeof(AudioUnitParameterID) * nparams;
            outWritable = false;
            validateElement = false;
        }
        break;

    case kAudioUnitProperty_ParameterInfo:
        outDataSize = sizeof(AudioUnitParameterInfo);
        outWritable = false;
        validateElement = false;
        break;

    case kAudioUnitProperty_ParameterHistoryInfo:
        outDataSize = sizeof(AudioUnitParameterHistoryInfo);
        outWritable = false;
        validateElement = false;
        break;

    case kAudioUnitProperty_ElementCount:
        outDataSize = sizeof(UInt32);
        outWritable = BusCountWritable(inScope);
        validateElement = false;
        break;

    case kAudioUnitProperty_Latency:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(Float64);
        outWritable = false;
        break;

    case kAudioUnitProperty_TailTime:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        if (SupportsTail()) {
            outDataSize = sizeof(Float64);
            outWritable = false;
        } else
            goto InvalidProperty;
        break;

    case kAudioUnitProperty_MaximumFramesPerSlice:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(UInt32);
        outWritable = true;
        break;

    case kAudioUnitProperty_LastRenderError:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(OSStatus);
        outWritable = false;
        break;

    case kAudioUnitProperty_SupportedNumChannels:
    {
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        UInt32 num = SupportedNumChannels (NULL);
        if (num) {
            outDataSize = sizeof (AUChannelInfo) * num;
            result = noErr;
        } else
            goto InvalidProperty;
        outWritable = false;
        break;
    }

    case kAudioUnitProperty_SupportedChannelLayoutTags:
    {
        UInt32 numLayouts = GetChannelLayoutTags(inScope, inElement, NULL);
        if (numLayouts) {
            outDataSize = numLayouts * sizeof(AudioChannelLayoutTag);
            result = noErr;
        } else
            goto InvalidProperty;
        outWritable = false;
        validateElement = false; //already done it
        break;
    }

    case kAudioUnitProperty_AudioChannelLayout:
    {
        outWritable = false;
        outDataSize = GetAudioChannelLayout(inScope, inElement, NULL, outWritable);
        if (outDataSize) {
            result = noErr;
        } else {
            if (GetChannelLayoutTags(inScope, inElement, NULL) == 0)
                goto InvalidProperty;
            else
                result = kAudioUnitErr_InvalidPropertyValue;
        }
        validateElement = false; //already done it
        break;
    }

#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE
    case kAudioUnitProperty_ShouldAllocateBuffer:
        ca_require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output), InvalidScope);
        outWritable = true;
        outDataSize = sizeof(UInt32);
        break;
#endif

#if !CA_USE_AUDIO_PLUGIN_ONLY
    case kAudioUnitProperty_FastDispatch:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        if (!IsCMgrObject()) goto InvalidProperty;
        outDataSize = sizeof(void *);
        outWritable = false;
        validateElement = false;
        break;

    case kAudioUnitProperty_GetUIComponentList:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = GetNumCustomUIComponents();
        if (outDataSize == 0)
            goto InvalidProperty;
        outDataSize *= sizeof (AudioComponentDescription);

        outWritable = false;
        break;
#endif

    case kAudioUnitProperty_ParameterValueStrings:
        result = GetParameterValueStrings(inScope, inElement, NULL);
        if (result == noErr) {
            outDataSize = sizeof(CFArrayRef);
            outWritable = false;
            validateElement = false;
        }
        break;

#if !CA_NO_AU_HOST_CALLBACKS
    case kAudioUnitProperty_HostCallbacks:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(mHostCallbackInfo);
        outWritable = true;
        break;
#endif
#if !CA_NO_AU_UI_FEATURES
    case kAudioUnitProperty_ContextName:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(CFStringRef);
        outWritable = true;
        break;

    case kAudioUnitProperty_IconLocation:
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outWritable = false;
        if (!HasIcon())
            goto InvalidProperty;
        outDataSize = sizeof(CFURLRef);
        break;

    case kAudioUnitProperty_ParameterClumpName:
        outDataSize = sizeof(AudioUnitParameterNameInfo );
        outWritable = false;
        break;

#endif // !CA_NO_AU_UI_FEATURES

    case 'lrst' :  // kAudioUnitProperty_LastRenderedSampleTime
        outDataSize = sizeof(Float64);
        outWritable = false;
        break;

    case 54:// kAudioUnitProperty_NickName -> is not defined in Xcode 4.5
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        outDataSize = sizeof(CFStringRef);
        outWritable = true;
        break;

    default:
        result = GetPropertyInfo(inID, inScope, inElement, outDataSize, outWritable);
        validateElement = false;
        break;
    }

    if (result == noErr && validateElement) {
        ca_require(GetElement(inScope, inElement) != NULL, InvalidElement);
    }

    return result;
InvalidProperty:
    return kAudioUnitErr_InvalidProperty;
InvalidScope:
    return kAudioUnitErr_InvalidScope;
InvalidElement:
    return kAudioUnitErr_InvalidElement;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::DispatchGetProperty(    AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    void *                          outData)
{
    // NOTE: We're currently only called from AUBase::ComponentEntryDispatch, which
    // calls DispatchGetPropertyInfo first, which performs validation of the scope/element,
    // and ensures that the outData buffer is non-null and large enough.
    OSStatus result = noErr;

    switch (inID) {
    case kAudioUnitProperty_StreamFormat:
        *(CAStreamBasicDescription *)outData = GetStreamFormat(inScope, inElement);
        break;

    case kAudioUnitProperty_SampleRate:
        *(Float64 *)outData = GetStreamFormat(inScope, inElement).mSampleRate;
        break;

    case kAudioUnitProperty_ParameterList:
        {
            UInt32 nparams = 0;
            result = GetParameterList(inScope, (AudioUnitParameterID *)outData, nparams);
        }
        break;

    case kAudioUnitProperty_ParameterInfo:
        result = GetParameterInfo(inScope, inElement, *(AudioUnitParameterInfo *)outData);
        break;

    case kAudioUnitProperty_ParameterHistoryInfo:
        {
            AudioUnitParameterHistoryInfo* info = (AudioUnitParameterHistoryInfo*)outData;
            result = GetParameterHistoryInfo(inScope, inElement, info->updatesPerSecond, info->historyDurationInSeconds);
        }
        break;

    case kAudioUnitProperty_ClassInfo:
        {
            *(CFPropertyListRef *)outData = NULL;
            result = SaveState((CFPropertyListRef *)outData);
        }
        break;

    case kAudioUnitProperty_FactoryPresets:
        {
            *(CFArrayRef *)outData = NULL;
            result = GetPresets ((CFArrayRef *)outData);
        }
        break;

    case kAudioUnitProperty_PresentPreset:
#if !CA_USE_AUDIO_PLUGIN_ONLY
#ifndef __LP64__
    case kAudioUnitProperty_CurrentPreset:
#endif
#endif
        {
            *(AUPreset *)outData = mCurrentPreset;

                // retain current string (as client owns a reference to it and will release it)
            if (inID == kAudioUnitProperty_PresentPreset && mCurrentPreset.presetName)
                CFRetain (mCurrentPreset.presetName);

            result = noErr;
        }
        break;

    case kAudioUnitProperty_ElementName:
        {
            AUElement * element = GetElement(inScope, inElement);
            if (element->HasName()) {
                *(CFStringRef *)outData = element->GetName();
                CFRetain (element->GetName());
                result = noErr;
            } else
                result = kAudioUnitErr_InvalidPropertyValue;
        }
        break;

    case kAudioUnitProperty_ElementCount:
        *(UInt32 *)outData = GetScope(inScope).GetNumberOfElements();
        break;

    case kAudioUnitProperty_Latency:
        *(Float64 *)outData = GetLatency();
        break;

    case kAudioUnitProperty_TailTime:
        if (SupportsTail())
            *(Float64 *)outData = GetTailTime();
        else
            result = kAudioUnitErr_InvalidProperty;
        break;

    case kAudioUnitProperty_MaximumFramesPerSlice:
        *(UInt32 *)outData = mMaxFramesPerSlice;
        break;

    case kAudioUnitProperty_LastRenderError:
        *(OSStatus *)outData = mLastRenderError;
        mLastRenderError = 0;
        break;

    case kAudioUnitProperty_SupportedNumChannels:
        {
            const AUChannelInfo* infoPtr = NULL;
            UInt32 num = SupportedNumChannels (&infoPtr);
            if(num != 0 && infoPtr != NULL)
                memcpy (outData, infoPtr, num * sizeof (AUChannelInfo));
        }
        break;

    case kAudioUnitProperty_SupportedChannelLayoutTags:
        {
            AudioChannelLayoutTag* ptr = outData ? static_cast<AudioChannelLayoutTag*>(outData) : NULL;
            UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, ptr);
            if (numLayouts == 0)
                result = kAudioUnitErr_InvalidProperty;
        }
        break;

    case kAudioUnitProperty_AudioChannelLayout:
    {
        AudioChannelLayout* ptr = outData ? static_cast<AudioChannelLayout*>(outData) : NULL;
        Boolean writable;
        UInt32 dataSize = GetAudioChannelLayout(inScope, inElement, ptr, writable);
        if (!dataSize) {
            result = kAudioUnitErr_InvalidProperty;
        }
        break;
    }

#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE
    case kAudioUnitProperty_ShouldAllocateBuffer:
    {
        AUIOElement * element = GetIOElement(inScope, inElement);
        *(UInt32*)outData = element->WillAllocateBuffer();
        break;
    }
#endif

    case kAudioUnitProperty_ParameterValueStrings:
        result = GetParameterValueStrings(inScope, inElement, (CFArrayRef *)outData);
        break;

#if !CA_USE_AUDIO_PLUGIN_ONLY
    case kAudioUnitProperty_FastDispatch:
        if (!IsCMgrObject()) result = kAudioUnitErr_InvalidProperty;
        else {
            switch (inElement) {
            case kAudioUnitGetParameterSelect:
                *(AudioUnitGetParameterProc *)outData = (AudioUnitGetParameterProc)CMgr_AudioUnitBaseGetParameter;
                break;
            case kAudioUnitSetParameterSelect:
                *(AudioUnitSetParameterProc *)outData = (AudioUnitSetParameterProc)CMgr_AudioUnitBaseSetParameter;
                break;
            case kAudioUnitRenderSelect:
                if (AudioUnitAPIVersion() > 1)
                    *(AudioUnitRenderProc *)outData = (AudioUnitRenderProc)CMgr_AudioUnitBaseRender;
                else result = kAudioUnitErr_InvalidElement;
                break;
            default:
                result = GetProperty(inID, inScope, inElement, outData);
                break;
            }
        }
        break;

    case kAudioUnitProperty_GetUIComponentList:
        GetUIComponentDescs ((ComponentDescription*)outData);
        break;
#endif

#if !CA_NO_AU_HOST_CALLBACKS
    case kAudioUnitProperty_HostCallbacks:
        memcpy(outData, &mHostCallbackInfo, sizeof(mHostCallbackInfo));
        break;
#endif
#if !CA_NO_AU_UI_FEATURES
    case kAudioUnitProperty_IconLocation:
        {
            CFURLRef iconLocation = CopyIconLocation();
            if (iconLocation) {
                *(CFURLRef*)outData = iconLocation;
            } else
                result = kAudioUnitErr_InvalidProperty;
        }
        break;

    case kAudioUnitProperty_ContextName:
        *(CFStringRef *)outData = mContextName;
        if (mContextName) {
            CFRetain(mContextName);
            // retain CFString (if exists) since client will be responsible for its release
            result = noErr;
        } else {
            result = kAudioUnitErr_InvalidPropertyValue;
        }
        break;

    case kAudioUnitProperty_ParameterClumpName:
        {
            AudioUnitParameterNameInfo * ioClumpInfo = (AudioUnitParameterNameInfo*) outData;
            if (ioClumpInfo->inID == kAudioUnitClumpID_System)  // this ID value is reserved
                result = kAudioUnitErr_InvalidPropertyValue;
            else
            {
                result = CopyClumpName(inScope, ioClumpInfo->inID, ioClumpInfo->inDesiredLength, &ioClumpInfo->outName);

                    // this is provided for compatbility with existing implementations that don't know
                    // about this new mechanism
                if (result == kAudioUnitErr_InvalidProperty)
                    result = GetProperty (inID, inScope, inElement, outData);
            }
        }
        break;

#endif  // !CA_NO_AU_UI_FEATURES

    case 'lrst' : // kAudioUnitProperty_LastRenderedSampleTime
        *(Float64*)outData = mCurrentRenderTime.mSampleTime;
        break;

    case 54:// kAudioUnitProperty_NickName -> is not defined in Xcode 4.5
        // Ownership follows Core Foundation's 'Copy Rule'
        if (mNickName) CFRetain(mNickName);
        *(CFStringRef*)outData = mNickName;
        break;

    default:
        result = GetProperty(inID, inScope, inElement, outData);
        break;
    }
    return result;
}


//_____________________________________________________________________________
//
OSStatus            AUBase::DispatchSetProperty(    AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    const void *                    inData,
                                                    UInt32                          inDataSize)
{
    OSStatus result = noErr;

    switch (inID) {
    case kAudioUnitProperty_MakeConnection:
        ca_require(inDataSize >= sizeof(AudioUnitConnection), InvalidPropertyValue);
        {
            AudioUnitConnection &connection = *(AudioUnitConnection *)inData;
            result = SetConnection(connection);
        }
        break;


    case kAudioUnitProperty_SetRenderCallback:
        {
            ca_require(inDataSize >= sizeof(AURenderCallbackStruct), InvalidPropertyValue);
            ca_require(AudioUnitAPIVersion() > 1, InvalidProperty);
            AURenderCallbackStruct &callback = *(AURenderCallbackStruct*)inData;
            result = SetInputCallback(kAudioUnitProperty_SetRenderCallback, inElement, callback.inputProc, callback.inputProcRefCon);
        }
        break;

    case kAudioUnitProperty_ElementCount:
        ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue);
        ca_require(BusCountWritable(inScope), NotWritable);
        result = SetBusCount(inScope, *(UInt32*)inData);
        if (result == noErr) {
            PropertyChanged(inID, inScope, inElement);
        }
        break;

    case kAudioUnitProperty_MaximumFramesPerSlice:
        ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue);
        result = CanSetMaxFrames();
        if (result) return result;
        SetMaxFramesPerSlice(*(UInt32 *)inData);
        break;

    case kAudioUnitProperty_StreamFormat:
        {
            if (inDataSize < 36) goto InvalidPropertyValue;
            ca_require(GetElement(inScope, inElement) != NULL, InvalidElement);

            CAStreamBasicDescription newDesc;
                // now we're going to be ultra conservative! because of discrepancies between
                // sizes of this struct based on aligment padding inconsistencies
            memset (&newDesc, 0, sizeof(newDesc));
            memcpy (&newDesc, inData, 36);

            ca_require(ValidFormat(inScope, inElement, newDesc), InvalidFormat);

            const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);

            if ( !curDesc.IsEqual(newDesc, false) ) {
                ca_require(IsStreamFormatWritable(inScope, inElement), NotWritable);
                result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
            }
        }
        break;

    case kAudioUnitProperty_SampleRate:
        {
            ca_require(inDataSize == sizeof(Float64), InvalidPropertyValue);
            ca_require(GetElement(inScope, inElement) != NULL, InvalidElement);

            const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);
            CAStreamBasicDescription newDesc = curDesc;
            newDesc.mSampleRate = *(Float64 *)inData;

            ca_require(ValidFormat(inScope, inElement, newDesc), InvalidFormat);

            if ( !curDesc.IsEqual(newDesc, false) ) {
                ca_require(IsStreamFormatWritable(inScope, inElement), NotWritable);
                result = ChangeStreamFormat(inScope, inElement, curDesc, newDesc);
            }
        }
        break;

    case kAudioUnitProperty_AudioChannelLayout:
        {
            const AudioChannelLayout *layout = static_cast<const AudioChannelLayout *>(inData);
            size_t headerSize = sizeof(AudioChannelLayout) - sizeof(AudioChannelDescription);

            ca_require(inDataSize >= headerSize + layout->mNumberChannelDescriptions * sizeof(AudioChannelDescription), InvalidPropertyValue);
            result = SetAudioChannelLayout(inScope, inElement, layout);
            if (result == noErr)
                PropertyChanged(inID, inScope, inElement);
            break;
        }

    case kAudioUnitProperty_ClassInfo:
        ca_require(inDataSize == sizeof(CFPropertyListRef *), InvalidPropertyValue);
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        result = RestoreState(*(CFPropertyListRef *)inData);
        break;

    case kAudioUnitProperty_PresentPreset:
#if !CA_USE_AUDIO_PLUGIN_ONLY
#ifndef __LP64__
    case kAudioUnitProperty_CurrentPreset:
#endif
#endif
        {
            ca_require(inDataSize == sizeof(AUPreset), InvalidPropertyValue);
            ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
            AUPreset & newPreset = *(AUPreset *)inData;

            if (newPreset.presetNumber >= 0)
            {
                result = NewFactoryPresetSet(newPreset);
                // NewFactoryPresetSet SHOULD call SetAFactoryPreset if the preset is valid
                // from its own list of preset number->name
                if (!result)
                    PropertyChanged(inID, inScope, inElement);
            }
            else if (newPreset.presetName)
            {
                result = NewCustomPresetSet(newPreset);
                if (!result)
                    PropertyChanged(inID, inScope, inElement);
            }
            else
                result = kAudioUnitErr_InvalidPropertyValue;
        }
        break;

    case kAudioUnitProperty_ElementName:
        {
            ca_require(GetElement(inScope, inElement) != NULL, InvalidElement);
            ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue);
            AUElement * element = GetScope(inScope).GetElement (inElement);
            element->SetName (*(CFStringRef *)inData);
            PropertyChanged(inID, inScope, inElement);
        }
        break;

#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || TARGET_OS_IPHONE
    case kAudioUnitProperty_ShouldAllocateBuffer:
        {
            ca_require((inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output), InvalidScope);
            ca_require(GetElement(inScope, inElement) != NULL, InvalidElement);
            ca_require(inDataSize == sizeof(UInt32), InvalidPropertyValue);
            ca_require(!IsInitialized(), Initialized);

            AUIOElement * element = GetIOElement(inScope, inElement);
            element->SetWillAllocateBuffer(*(UInt32 *)inData != 0);
        }
        break;
#endif

#if !CA_NO_AU_HOST_CALLBACKS
    case kAudioUnitProperty_HostCallbacks:
    {
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        UInt32 availSize = std::min(inDataSize, (UInt32)sizeof(HostCallbackInfo));
        bool hasChanged = !memcmp (&mHostCallbackInfo, inData, availSize);
        memset (&mHostCallbackInfo, 0, sizeof (mHostCallbackInfo));
        memcpy (&mHostCallbackInfo, inData, availSize);
        if (hasChanged)
            PropertyChanged(inID, inScope, inElement);
        break;
    }
#endif
#if !CA_NO_AU_UI_FEATURES
    case kAudioUnitProperty_SetExternalBuffer:
        ca_require(inDataSize >= sizeof(AudioUnitExternalBuffer), InvalidPropertyValue);
        ca_require(IsInitialized(), Uninitialized);
        {
            AudioUnitExternalBuffer &buf = *(AudioUnitExternalBuffer*)inData;
            if (intptr_t(buf.buffer) & 0x0F) result = kAudio_ParamError;
            else if (inScope == kAudioUnitScope_Input) {
                AUInputElement *input = GetInput(inElement);
                input->UseExternalBuffer(buf);
            } else {
                AUOutputElement *output = GetOutput(inElement);
                output->UseExternalBuffer(buf);
            }
        }
        break;

    case kAudioUnitProperty_ContextName:
        {
            ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue);
            ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
            CFStringRef inStr = *(CFStringRef *)inData;
            if (mContextName) CFRelease(mContextName);
            if (inStr) CFRetain(inStr);
            mContextName = inStr;
            PropertyChanged(inID, inScope, inElement);
        }
        break;

#endif // !CA_NO_AU_UI_FEATURES

    case 54:// kAudioUnitProperty_NickName -> is not defined in Xcode 4.5
    {
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        ca_require(inDataSize == sizeof(CFStringRef), InvalidPropertyValue);
        CFStringRef inStr = *(CFStringRef *)inData;
        if (mNickName) CFRelease(mNickName);
        if (inStr) CFRetain(inStr);
        mNickName = inStr;
        PropertyChanged(inID, inScope, inElement);
        break;
    }

    default:
        result = SetProperty(inID, inScope, inElement, inData, inDataSize);
        if (result == noErr)
            PropertyChanged(inID, inScope, inElement);

        break;
    }
    return result;
NotWritable:
    return kAudioUnitErr_PropertyNotWritable;
InvalidFormat:
    return kAudioUnitErr_FormatNotSupported;
#if !CA_NO_AU_UI_FEATURES
Uninitialized:
    return kAudioUnitErr_Uninitialized;
#endif
#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || CA_USE_AUDIO_PLUGIN_ONLY
Initialized:
    return kAudioUnitErr_Initialized;
#endif
InvalidScope:
    return kAudioUnitErr_InvalidScope;
InvalidProperty:
    return kAudioUnitErr_InvalidProperty;
InvalidPropertyValue:
    return kAudioUnitErr_InvalidPropertyValue;
InvalidElement:
    return kAudioUnitErr_InvalidElement;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::DispatchRemovePropertyValue (AudioUnitPropertyID        inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement)
{
    OSStatus result = noErr;
    switch (inID)
    {
    case kAudioUnitProperty_AudioChannelLayout:
    {
        result = RemoveAudioChannelLayout(inScope, inElement);
        if (result == noErr)
            PropertyChanged(inID, inScope, inElement);
        break;
    }

#if !CA_NO_AU_HOST_CALLBACKS
    case kAudioUnitProperty_HostCallbacks:
    {
        ca_require(inScope == kAudioUnitScope_Global, InvalidScope);
        bool hasValue = false;
        void* ptr = &mHostCallbackInfo;
        for (unsigned int i = 0; i <  sizeof (HostCallbackInfo); ++i) {
            if (static_cast<char*>(ptr)[i]) {
                hasValue = true;
                break;
            }
        }
        if (hasValue) {
            memset (&mHostCallbackInfo, 0, sizeof (HostCallbackInfo));
            PropertyChanged(inID, inScope, inElement);
        }
        break;
    }
#endif
#if !CA_NO_AU_UI_FEATURES
    case kAudioUnitProperty_ContextName:
        if (mContextName) CFRelease(mContextName);
        mContextName = NULL;
        result = noErr;
        break;

#endif // !CA_NO_AU_UI_FEATURES

    case 54:// kAudioUnitProperty_NickName -> is not defined in Xcode 4.5
    {
        if(inScope == kAudioUnitScope_Global) {
            if (mNickName) CFRelease(mNickName);
            mNickName = NULL;
            PropertyChanged(inID, inScope, inElement);
        } else {
            result = kAudioUnitErr_InvalidScope;
        }
        break;
    }

    default:
        result = RemovePropertyValue (inID, inScope, inElement);
        break;
    }

    return result;
#if !CA_NO_AU_UI_FEATURES || !CA_NO_AU_HOST_CALLBACKS
InvalidScope:
    return kAudioUnitErr_InvalidScope;
#endif
}

//_____________________________________________________________________________
//
OSStatus            AUBase::GetPropertyInfo(        AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    UInt32 &                        outDataSize,
                                                    Boolean &                       outWritable)
{
    return kAudioUnitErr_InvalidProperty;
}


//_____________________________________________________________________________
//
OSStatus            AUBase::GetProperty(            AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    void *                          outData)
{
    return kAudioUnitErr_InvalidProperty;
}


//_____________________________________________________________________________
//
OSStatus            AUBase::SetProperty(            AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    const void *                    inData,
                                                    UInt32                          inDataSize)
{
    return kAudioUnitErr_InvalidProperty;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::RemovePropertyValue (   AudioUnitPropertyID             inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement)
{
    return kAudioUnitErr_InvalidPropertyValue;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::AddPropertyListener(    AudioUnitPropertyID             inID,
                                                    AudioUnitPropertyListenerProc   inProc,
                                                    void *                          inProcRefCon)
{
    PropertyListener pl;

    pl.propertyID = inID;
    pl.listenerProc = inProc;
    pl.listenerRefCon = inProcRefCon;

    if (mPropertyListeners.empty())
        mPropertyListeners.reserve(32);
    mPropertyListeners.push_back(pl);

    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::RemovePropertyListener(     AudioUnitPropertyID             inID,
                                                        AudioUnitPropertyListenerProc   inProc,
                                                        void *                          inProcRefCon,
                                                        bool                            refConSpecified)
{
    // iterate in reverse so that it's safe to erase in the middle of the vector
    for (int i = (int)mPropertyListeners.size(); --i >=0; ) {
        PropertyListeners::iterator it = mPropertyListeners.begin() + i;
        if ((*it).propertyID == inID && (*it).listenerProc == inProc && (!refConSpecified || (*it).listenerRefCon == inProcRefCon))
            mPropertyListeners.erase(it);
    }
    return noErr;
}

//_____________________________________________________________________________
//
void                AUBase::PropertyChanged(            AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement)
{
    for (PropertyListeners::iterator it = mPropertyListeners.begin(); it != mPropertyListeners.end(); ++it)
        if ((*it).propertyID == inID)
            ((*it).listenerProc)((*it).listenerRefCon, mComponentInstance, inID, inScope, inElement);
}

//_____________________________________________________________________________
//
OSStatus            AUBase::SetRenderNotification(  AURenderCallback                inProc,
                                                    void *                          inRefCon)
{
    if (inProc == NULL)
        return kAudio_ParamError;

    mRenderCallbacksTouched = true;
    mRenderCallbacks.deferred_add(RenderCallback(inProc, inRefCon));
            // this will do nothing if it's already in the list
    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::RemoveRenderNotification(   AURenderCallback            inProc,
                                                        void *                      inRefCon)
{
    mRenderCallbacks.deferred_remove(RenderCallback(inProc, inRefCon));
    return noErr;   // error?
}

//_____________________________________________________________________________
//
OSStatus    AUBase::GetParameter(           AudioUnitParameterID            inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    AudioUnitParameterValue &       outValue)
{
    AUElement *elem = SafeGetElement(inScope, inElement);
    outValue = elem->GetParameter(inID);
    return noErr;
}


//_____________________________________________________________________________
//
OSStatus    AUBase::SetParameter(           AudioUnitParameterID            inID,
                                                    AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    AudioUnitParameterValue         inValue,
                                                    UInt32                          inBufferOffsetInFrames)
{
    AUElement *elem = SafeGetElement(inScope, inElement);
    elem->SetParameter(inID, inValue);
    return noErr;
}

//_____________________________________________________________________________
//
OSStatus    AUBase::ScheduleParameter ( const AudioUnitParameterEvent       *inParameterEvent,
                                                    UInt32                          inNumEvents)
{
    bool canScheduleParameters = CanScheduleParameters();

    for (UInt32 i = 0; i < inNumEvents; ++i)
    {
        if (inParameterEvent[i].eventType == kParameterEvent_Immediate)
        {
            SetParameter (inParameterEvent[i].parameter,
                            inParameterEvent[i].scope,
                            inParameterEvent[i].element,
                            inParameterEvent[i].eventValues.immediate.value,
                            inParameterEvent[i].eventValues.immediate.bufferOffset);
        }
        if (canScheduleParameters) {
            mParamList.push_back (inParameterEvent[i]);
        }
    }

    return noErr;
}

// ____________________________________________________________________________
//
static bool SortParameterEventList(const AudioUnitParameterEvent &ev1, const AudioUnitParameterEvent &ev2 )
{
    int offset1 = ev1.eventType == kParameterEvent_Immediate ?  ev1.eventValues.immediate.bufferOffset : ev1.eventValues.ramp.startBufferOffset;
    int offset2 = ev2.eventType == kParameterEvent_Immediate ?  ev2.eventValues.immediate.bufferOffset : ev2.eventValues.ramp.startBufferOffset;

    if(offset1 < offset2) return true;
    return false;
}


// ____________________________________________________________________________
//
OSStatus    AUBase::ProcessForScheduledParams(  ParameterEventList      &inParamList,
                                                        UInt32                  inFramesToProcess,
                                                        void                    *inUserData )
{
    OSStatus result = noErr;

    int totalFramesToProcess = inFramesToProcess;

    int framesRemaining = totalFramesToProcess;

    unsigned int currentStartFrame = 0; // start of the whole buffer



    // sort the ParameterEventList by startBufferOffset
    std::sort(inParamList.begin(), inParamList.end(), SortParameterEventList);

    ParameterEventList::iterator iter = inParamList.begin();


    while(framesRemaining > 0 )
    {
        // first of all, go through the ramped automation events and find out where the next
        // division of our whole buffer will be

        int currentEndFrame = totalFramesToProcess; // start out assuming we'll process all the way to
                                                    // the end of the buffer

        iter = inParamList.begin();

        // find the next break point
        while(iter != inParamList.end() )
        {
            AudioUnitParameterEvent &event = *iter;

            int offset = event.eventType == kParameterEvent_Immediate ?  event.eventValues.immediate.bufferOffset : event.eventValues.ramp.startBufferOffset;

            if(offset > (int)currentStartFrame && offset < currentEndFrame )
            {
                currentEndFrame = offset;
                break;
            }

            // consider ramp end to be a possible choice (there may be gaps in the supplied ramp events)
            if(event.eventType == kParameterEvent_Ramped )
            {
                offset = event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames;

                if(offset > (int)currentStartFrame && offset < currentEndFrame )
                {
                    currentEndFrame = offset;
                }
            }

            iter++;
        }

        int framesThisTime = currentEndFrame - currentStartFrame;

        // next, setup the parameter maps to be current for the ramp parameters active during
        // this time segment...

        for(ParameterEventList::iterator iter2 = inParamList.begin(); iter2 != inParamList.end(); iter2++ )
        {
            AudioUnitParameterEvent &event = *iter2;

            bool eventFallsInSlice;


            if(event.eventType == kParameterEvent_Ramped)
                eventFallsInSlice = event.eventValues.ramp.startBufferOffset < currentEndFrame
                    && event.eventValues.ramp.startBufferOffset + event.eventValues.ramp.durationInFrames > currentStartFrame;
            else /* kParameterEvent_Immediate */
                // actually, for the same parameter, there may be future immediate events which override this one,
                // but it's OK since the event list is sorted in time order, we're guaranteed to end up with the current one
                eventFallsInSlice = event.eventValues.immediate.bufferOffset <= currentStartFrame;

            if(eventFallsInSlice)
            {
                AUElement *element = GetElement(event.scope, event.element );

                if(element) element->SetScheduledEvent( event.parameter,
                                                        event,
                                                        currentStartFrame,
                                                        currentEndFrame - currentStartFrame );
            }
        }



        // Finally, actually do the processing for this slice.....

        result = ProcessScheduledSlice( inUserData,
                                        currentStartFrame,
                                        framesThisTime,
                                        inFramesToProcess );

        if(result != noErr) break;

        framesRemaining -= framesThisTime;
        currentStartFrame = currentEndFrame;    // now start from where we left off last time
    }

    return result;
}

//_____________________________________________________________________________
//
void                AUBase::SetWantsRenderThreadID (bool inFlag)
{
    if (inFlag == mWantsRenderThreadID)
        return;

    mWantsRenderThreadID = inFlag;
    if (!mWantsRenderThreadID)
        mRenderThreadID = NULL;
}

//_____________________________________________________________________________
//

//_____________________________________________________________________________
//
OSStatus            AUBase::DoRender(       AudioUnitRenderActionFlags &    ioActionFlags,
                                            const AudioTimeStamp &          inTimeStamp,
                                            UInt32                          inBusNumber,
                                            UInt32                          inFramesToProcess,
                                            AudioBufferList &               ioData)
{
    OSStatus theError;
    RenderCallbackList::iterator rcit;

    AUTRACE(kCATrace_AUBaseRenderStart, mComponentInstance, (uintptr_t)this, inBusNumber, inFramesToProcess, (uintptr_t)ioData.mBuffers[0].mData);
    DISABLE_DENORMALS

    try {
        ca_require(IsInitialized(), Uninitialized);
        ca_require(mAudioUnitAPIVersion >= 2, ParamErr);
        if (inFramesToProcess > mMaxFramesPerSlice) {
            static time_t lastTimeMessagePrinted = 0;
            time_t now = time(NULL);
            if (now != lastTimeMessagePrinted) {
                lastTimeMessagePrinted = now;
                syslog(LOG_ERR, "kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=%u, mMaxFramesPerSlice=%u", (unsigned)inFramesToProcess, (unsigned)mMaxFramesPerSlice);
                DebugMessageN4("%s:%d inFramesToProcess=%u, mMaxFramesPerSlice=%u; TooManyFrames", __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)mMaxFramesPerSlice);
            }
            goto TooManyFrames;
        }
        ca_require (!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr);

        AUOutputElement *output = GetOutput(inBusNumber);   // will throw if non-existant
        if (output->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) {
            DebugMessageN4("%s:%d ioData.mNumberBuffers=%u, output->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError",
                __FILE__, __LINE__, (unsigned)ioData.mNumberBuffers, (unsigned)output->GetStreamFormat().NumberChannelStreams());
            goto ParamErr;
        }

        unsigned expectedBufferByteSize = inFramesToProcess * output->GetStreamFormat().mBytesPerFrame;
        for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
            AudioBuffer &buf = ioData.mBuffers[ibuf];
            if (buf.mData != NULL) {
                // only care about the size if the buffer is non-null
                if (buf.mDataByteSize < expectedBufferByteSize) {
                    // if the buffer is too small, we cannot render safely. kAudio_ParamError.
                    DebugMessageN7("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
                        __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)output->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibuf, (unsigned)buf.mDataByteSize);
                    goto ParamErr;
                }
                // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
                // We will generally set the buffer size at the end of rendering, before we return.
                // However we should ensure that no one, DURING rendering, READS a
                // potentially incorrect size. This can lead to doing too much work, or
                // reading past the end of an input buffer into unmapped memory.
                buf.mDataByteSize = expectedBufferByteSize;
            }
        }

        if (WantsRenderThreadID())
        {
            #if TARGET_OS_MAC
                mRenderThreadID = pthread_self();
            #elif TARGET_OS_WIN32
                mRenderThreadID = GetCurrentThreadId();
            #endif
        }

        AudioUnitRenderActionFlags flags;
        if (mRenderCallbacksTouched) {
            mRenderCallbacks.update();
            flags = ioActionFlags | kAudioUnitRenderAction_PreRender;
            for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) {
                RenderCallback &rc = *rcit;
                AUTRACE(kCATrace_AUBaseRenderCallbackStart, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 1, 0);
                (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon,
                                &flags,
                                &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
                AUTRACE(kCATrace_AUBaseRenderCallbackEnd, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 1, 0);
            }
        }

        theError = DoRenderBus(ioActionFlags, inTimeStamp, inBusNumber, output, inFramesToProcess, ioData);

        if (mRenderCallbacksTouched) {
            flags = ioActionFlags | kAudioUnitRenderAction_PostRender;

            if (SetRenderError (theError)) {
                flags |= kAudioUnitRenderAction_PostRenderError;
            }

            for (rcit = mRenderCallbacks.begin(); rcit != mRenderCallbacks.end(); ++rcit) {
                RenderCallback &rc = *rcit;
                AUTRACE(kCATrace_AUBaseRenderCallbackStart, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 2, 0);
                (*(AURenderCallback)rc.mRenderNotify)(rc.mRenderNotifyRefCon,
                                &flags,
                                &inTimeStamp, inBusNumber, inFramesToProcess, &ioData);
                AUTRACE(kCATrace_AUBaseRenderCallbackEnd, mComponentInstance, (intptr_t)this, (intptr_t)rc.mRenderNotify, 2, 0);
            }
        }

        // The vector's being emptied
        // because these events should only apply to this Render cycle, so anything
        // left over is from a preceding cycle and should be dumped.  New scheduled
        // parameters must be scheduled from the next pre-render callback.
        if (!mParamList.empty())
            mParamList.clear();

    }
    catch (OSStatus err) {
        theError = err;
        goto errexit;
    }
    catch (...) {
        theError = -1;
        goto errexit;
    }
done:
    RESTORE_DENORMALS
    AUTRACE(kCATrace_AUBaseRenderEnd, mComponentInstance, (intptr_t)this, theError, ioActionFlags, CATrace::ablData(ioData));

    return theError;

Uninitialized:  theError = kAudioUnitErr_Uninitialized;             goto errexit;
ParamErr:       theError = kAudio_ParamError;                       goto errexit;
TooManyFrames:  theError = kAudioUnitErr_TooManyFramesToProcess;    goto errexit;
errexit:
    DebugMessageN2 ("  from %s, render err: %d", GetLoggingString(), (int)theError);
    SetRenderError(theError);
    goto done;
}

//_____________________________________________________________________________
//
OSStatus    AUBase::DoProcess ( AudioUnitRenderActionFlags  &       ioActionFlags,
                                const AudioTimeStamp &              inTimeStamp,
                                UInt32                              inFramesToProcess,
                                AudioBufferList &                   ioData)
{
    OSStatus theError;
    AUTRACE(kCATrace_AUBaseRenderStart, mComponentInstance, (intptr_t)this, -1, inFramesToProcess, 0);
    DISABLE_DENORMALS

    try {

        if (!(ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/)) {
            ca_require(IsInitialized(), Uninitialized);
            ca_require(inFramesToProcess <= mMaxFramesPerSlice, TooManyFrames);
            ca_require(!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr);

            AUInputElement *input = GetInput(0);    // will throw if non-existant
            if (input->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) {
                DebugMessageN4("%s:%d ioData.mNumberBuffers=%u, input->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError",
                    __FILE__, __LINE__, (unsigned)ioData.mNumberBuffers, (unsigned)input->GetStreamFormat().NumberChannelStreams());
                goto ParamErr;
            }

            unsigned expectedBufferByteSize = inFramesToProcess * input->GetStreamFormat().mBytesPerFrame;
            for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
                AudioBuffer &buf = ioData.mBuffers[ibuf];
                if (buf.mData != NULL) {
                    // only care about the size if the buffer is non-null
                    if (buf.mDataByteSize < expectedBufferByteSize) {
                        // if the buffer is too small, we cannot render safely. kAudio_ParamError.
                        DebugMessageN7("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioData.mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
                            __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)input->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibuf, (unsigned)buf.mDataByteSize);
                        goto ParamErr;
                    }
                    // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
                    // We will generally set the buffer size at the end of rendering, before we return.
                    // However we should ensure that no one, DURING rendering, READS a
                    // potentially incorrect size. This can lead to doing too much work, or
                    // reading past the end of an input buffer into unmapped memory.
                    buf.mDataByteSize = expectedBufferByteSize;
                }
            }
        }

        if (WantsRenderThreadID())
        {
            #if TARGET_OS_MAC
                mRenderThreadID = pthread_self();
            #elif TARGET_OS_WIN32
                mRenderThreadID = GetCurrentThreadId();
            #endif
        }

        if (NeedsToRender (inTimeStamp)) {
            theError = ProcessBufferLists (ioActionFlags, ioData, ioData, inFramesToProcess);
        } else
            theError = noErr;

    }
    catch (OSStatus err) {
        theError = err;
        goto errexit;
    }
    catch (...) {
        theError = -1;
        goto errexit;
    }
done:
    RESTORE_DENORMALS
    AUTRACE(kCATrace_AUBaseRenderEnd, mComponentInstance, (intptr_t)this, theError, ioActionFlags, CATrace::ablData(ioData));

    return theError;

Uninitialized:  theError = kAudioUnitErr_Uninitialized;             goto errexit;
ParamErr:       theError = kAudio_ParamError;                       goto errexit;
TooManyFrames:  theError = kAudioUnitErr_TooManyFramesToProcess;    goto errexit;
errexit:
    DebugMessageN2 ("  from %s, process err: %d", GetLoggingString(), (int)theError);
    SetRenderError(theError);
    goto done;
}

OSStatus    AUBase::DoProcessMultiple ( AudioUnitRenderActionFlags  & ioActionFlags,
                               const AudioTimeStamp &               inTimeStamp,
                               UInt32                               inFramesToProcess,
                               UInt32                               inNumberInputBufferLists,
                               const AudioBufferList **             inInputBufferLists,
                               UInt32                               inNumberOutputBufferLists,
                               AudioBufferList **                   ioOutputBufferLists)
{
    OSStatus theError;
    DISABLE_DENORMALS

    try {

        if (!(ioActionFlags & (1 << 9)/*kAudioUnitRenderAction_DoNotCheckRenderArgs*/)) {
            ca_require(IsInitialized(), Uninitialized);
            ca_require(inFramesToProcess <= mMaxFramesPerSlice, TooManyFrames);
            ca_require (!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr);

            for (unsigned ibl = 0; ibl < inNumberInputBufferLists; ++ibl) {
                if (inInputBufferLists[ibl] != NULL) {
                    AUInputElement *input = GetInput(ibl);  // will throw if non-existant
                    unsigned expectedBufferByteSize = inFramesToProcess * input->GetStreamFormat().mBytesPerFrame;

                    if (input->GetStreamFormat().NumberChannelStreams() != inInputBufferLists[ibl]->mNumberBuffers) {
                        DebugMessageN5("%s:%d inInputBufferLists[%u]->mNumberBuffers=%u, input->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError",
                                       __FILE__, __LINE__, ibl, (unsigned)inInputBufferLists[ibl]->mNumberBuffers, (unsigned)input->GetStreamFormat().NumberChannelStreams());
                        goto ParamErr;
                    }

                    for (unsigned ibuf = 0; ibuf < inInputBufferLists[ibl]->mNumberBuffers; ++ibuf) {
                        const AudioBuffer &buf = inInputBufferLists[ibl]->mBuffers[ibuf];
                        if (buf.mData != NULL) {
                            if (buf.mDataByteSize < expectedBufferByteSize) {
                                // the buffer is too small
                                DebugMessageN8("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; inInputBufferLists[%u].mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
                                               __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)input->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, ibl, ibuf, (unsigned)buf.mDataByteSize);
                                goto ParamErr;
                            }
                        } else {
                            // the buffer must exist
                            goto ParamErr;
                        }
                    }
                } else {
                    // skip NULL input audio buffer list
                }
            }

            for (unsigned obl = 0; obl < inNumberOutputBufferLists; ++obl) {
                if (ioOutputBufferLists[obl] != NULL) {
                    AUOutputElement *output = GetOutput(obl);   // will throw if non-existant
                    unsigned expectedBufferByteSize = inFramesToProcess * output->GetStreamFormat().mBytesPerFrame;

                    if (output->GetStreamFormat().NumberChannelStreams() != ioOutputBufferLists[obl]->mNumberBuffers) {
                        DebugMessageN5("%s:%d ioOutputBufferLists[%u]->mNumberBuffers=%u, output->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError",
                                       __FILE__, __LINE__, obl, (unsigned)ioOutputBufferLists[obl]->mNumberBuffers, (unsigned)output->GetStreamFormat().NumberChannelStreams());
                        goto ParamErr;
                    }

                    for (unsigned obuf = 0; obuf < ioOutputBufferLists[obl]->mNumberBuffers; ++obuf) {
                        AudioBuffer &buf = ioOutputBufferLists[obl]->mBuffers[obuf];
                        if (buf.mData != NULL) {
                            // only care about the size if the buffer is non-null
                            if (buf.mDataByteSize < expectedBufferByteSize) {
                                // if the buffer is too small, we cannot render safely. kAudio_ParamError.
                                DebugMessageN8("%s:%d %u frames, %u bytes/frame, expected %u-byte buffer; ioOutputBufferLists[%u]->mBuffers[%u].mDataByteSize=%u; kAudio_ParamError",
                                               __FILE__, __LINE__, (unsigned)inFramesToProcess, (unsigned)output->GetStreamFormat().mBytesPerFrame, expectedBufferByteSize, obl, obuf, (unsigned)buf.mDataByteSize);
                                goto ParamErr;
                            }
                            // Some clients incorrectly pass bigger buffers than expectedBufferByteSize.
                            // We will generally set the buffer size at the end of rendering, before we return.
                            // However we should ensure that no one, DURING rendering, READS a
                            // potentially incorrect size. This can lead to doing too much work, or
                            // reading past the end of an input buffer into unmapped memory.
                            buf.mDataByteSize = expectedBufferByteSize;
                        }
                    }
                } else {
                    // skip NULL output audio buffer list
                }
            }
        }

        if (WantsRenderThreadID())
        {
#if TARGET_OS_MAC
            mRenderThreadID = pthread_self();
#elif TARGET_OS_WIN32
            mRenderThreadID = GetCurrentThreadId();
#endif
        }

        if (NeedsToRender (inTimeStamp)) {
            theError = ProcessMultipleBufferLists (ioActionFlags, inFramesToProcess, inNumberInputBufferLists, inInputBufferLists, inNumberOutputBufferLists, ioOutputBufferLists);
        } else
            theError = noErr;
    }
    catch (OSStatus err) {
        theError = err;
        goto errexit;
    }
    catch (...) {
        theError = -1;
        goto errexit;
    }
done:
    RESTORE_DENORMALS

    return theError;

Uninitialized:  theError = kAudioUnitErr_Uninitialized;             goto errexit;
ParamErr:       theError = kAudio_ParamError;                       goto errexit;
TooManyFrames:  theError = kAudioUnitErr_TooManyFramesToProcess;    goto errexit;
errexit:
    DebugMessageN2 ("  from %s, processmultiple err: %d", GetLoggingString(), (int)theError);
    SetRenderError(theError);
    goto done;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::SetInputCallback(       UInt32                          inPropertyID,
                                                    AudioUnitElement                inElement,
                                                    AURenderCallback                inProc,
                                                    void *                          inRefCon)
{
    AUInputElement *input = GetInput(inElement);    // may throw

    input->SetInputCallback(inProc, inRefCon);
    PropertyChanged(inPropertyID, kAudioUnitScope_Input, inElement);

    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::SetConnection(          const AudioUnitConnection &     inConnection)
{

    OSStatus err;
    AUInputElement *input = GetInput(inConnection.destInputNumber); // may throw

    if (inConnection.sourceAudioUnit) {
        // connecting, not disconnecting
        CAStreamBasicDescription sourceDesc;
        UInt32 size = sizeof(CAStreamBasicDescription);
        ca_require_noerr(err = AudioUnitGetProperty(
                                        inConnection.sourceAudioUnit,
                                        kAudioUnitProperty_StreamFormat,
                                        kAudioUnitScope_Output,
                                        inConnection.sourceOutputNumber,
                                        &sourceDesc,
                                        &size), errexit);
        ca_require_noerr(err = DispatchSetProperty (kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input, inConnection.destInputNumber,
                                &sourceDesc, sizeof(CAStreamBasicDescription)), errexit);
    }
    input->SetConnection(inConnection);

    PropertyChanged(kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, inConnection.destInputNumber);
    return noErr;

errexit:
    return err;
}

//_____________________________________________________________________________
//
UInt32              AUBase::SupportedNumChannels (  const AUChannelInfo**           outInfo)
{
    return 0;
}

//_____________________________________________________________________________
//
bool                AUBase::ValidFormat(            AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    const CAStreamBasicDescription &        inNewFormat)
{
    return FormatIsCanonical(inNewFormat);
}

//_____________________________________________________________________________
//
bool                AUBase::IsStreamFormatWritable( AudioUnitScope                  scope,
                                                    AudioUnitElement                element)
{
    switch (scope) {
    case kAudioUnitScope_Input:
        {
            AUInputElement *input = GetInput(element);
            if (input->HasConnection()) return false;   // can't write format when input comes from connection
        }
        // ... fall ...
    case kAudioUnitScope_Output:
        return StreamFormatWritable(scope, element);

//#warning "aliasing of global scope format should be pushed to subclasses"
    case kAudioUnitScope_Global:
        return StreamFormatWritable(kAudioUnitScope_Output, 0);
    }
    return false;
}

//_____________________________________________________________________________
//
const CAStreamBasicDescription &
                    AUBase::GetStreamFormat(        AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement)
{
//#warning "aliasing of global scope format should be pushed to subclasses"
    AUIOElement *element;

    switch (inScope) {
    case kAudioUnitScope_Input:
        element = Inputs().GetIOElement(inElement);
        break;
    case kAudioUnitScope_Output:
        element = Outputs().GetIOElement(inElement);
        break;
    case kAudioUnitScope_Global:    // global stream description is an alias for that of output 0
        element = Outputs().GetIOElement(0);
        break;
    default:
        COMPONENT_THROW(kAudioUnitErr_InvalidScope);
    }
    return element->GetStreamFormat();
}

OSStatus            AUBase::SetBusCount(    AudioUnitScope                  inScope,
                                            UInt32                          inCount)
{
    if (IsInitialized())
        return kAudioUnitErr_Initialized;

    GetScope(inScope).SetNumberOfElements(inCount);
    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::ChangeStreamFormat(     AudioUnitScope                  inScope,
                                                    AudioUnitElement                inElement,
                                                    const CAStreamBasicDescription & inPrevFormat,
                                                    const CAStreamBasicDescription &    inNewFormat)
{
//#warning "aliasing of global scope format should be pushed to subclasses"
    AUIOElement *element;

    switch (inScope) {
    case kAudioUnitScope_Input:
        element = Inputs().GetIOElement(inElement);
        break;
    case kAudioUnitScope_Output:
        element = Outputs().GetIOElement(inElement);
        break;
    case kAudioUnitScope_Global:
        element = Outputs().GetIOElement(0);
        break;
    default:
        COMPONENT_THROW(kAudioUnitErr_InvalidScope);
    }
    element->SetStreamFormat(inNewFormat);
    PropertyChanged(kAudioUnitProperty_StreamFormat, inScope, inElement);
    return noErr;
}

UInt32      AUBase::GetChannelLayoutTags(   AudioUnitScope              inScope,
                                            AudioUnitElement            inElement,
                                            AudioChannelLayoutTag *     outLayoutTags)
{
    return GetIOElement(inScope, inElement)->GetChannelLayoutTags(outLayoutTags);
}

UInt32      AUBase::GetAudioChannelLayout(  AudioUnitScope              scope,
                                            AudioUnitElement            element,
                                            AudioChannelLayout *        outLayoutPtr,
                                            Boolean &                   outWritable)
{
    AUIOElement * el = GetIOElement(scope, element);
    return el->GetAudioChannelLayout(outLayoutPtr, outWritable);
}

OSStatus    AUBase::RemoveAudioChannelLayout(           AudioUnitScope              inScope,
                                                        AudioUnitElement            inElement)
{
    OSStatus result = noErr;
    AUIOElement * el = GetIOElement(inScope, inElement);
    Boolean writable;
    if (el->GetAudioChannelLayout(NULL, writable)) {
        result = el->RemoveAudioChannelLayout();
    }
    return result;
}

OSStatus    AUBase::SetAudioChannelLayout(              AudioUnitScope              inScope,
                                                        AudioUnitElement            inElement,
                                                        const AudioChannelLayout *  inLayout)
{
    AUIOElement* ioEl = GetIOElement (inScope, inElement);

    // the num channels of the layout HAS TO MATCH the current channels of the Element's stream format
    UInt32 currentChannels = ioEl->GetStreamFormat().NumberChannels();
    UInt32 numChannelsInLayout = CAAudioChannelLayout::NumberChannels(*inLayout);
    if (currentChannels != numChannelsInLayout)
        return kAudioUnitErr_InvalidPropertyValue;

    UInt32 numLayouts = GetChannelLayoutTags (inScope, inElement, NULL);
    if (numLayouts == 0)
        return kAudioUnitErr_InvalidProperty;
    AudioChannelLayoutTag *tags = (AudioChannelLayoutTag *)CA_malloc (numLayouts * sizeof (AudioChannelLayoutTag));
    GetChannelLayoutTags (inScope, inElement, tags);
    bool foundTag = false;
    for (unsigned int i = 0; i < numLayouts; ++i) {
        if (tags[i] == inLayout->mChannelLayoutTag || tags[i] == kAudioChannelLayoutTag_UseChannelDescriptions) {
            foundTag = true;
            break;
        }
    }
    free(tags);

    if (foundTag == false)
        return kAudioUnitErr_InvalidPropertyValue;

    return ioEl->SetAudioChannelLayout(*inLayout);
}

static void AddNumToDictionary (CFMutableDictionaryRef dict, CFStringRef key, SInt32 value)
{
    CFNumberRef num = CFNumberCreate (NULL, kCFNumberSInt32Type, &value);
    CFDictionarySetValue (dict, key, num);
    CFRelease (num);
}

#define kCurrentSavedStateVersion 0

OSStatus            AUBase::SaveState(      CFPropertyListRef * outData)
{
    AudioComponentDescription desc = GetComponentDescription();

    CFMutableDictionaryRef dict = CFDictionaryCreateMutable (NULL, 0,
                                &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

// first step -> save the version to the data ref
    SInt32 value = kCurrentSavedStateVersion;
    AddNumToDictionary (dict, kVersionString, value);

// second step -> save the component type, subtype, manu to the data ref
    value = desc.componentType;
    AddNumToDictionary (dict, kTypeString, value);

    value = desc.componentSubType;
    AddNumToDictionary (dict, kSubtypeString, value);

    value = desc.componentManufacturer;
    AddNumToDictionary (dict, kManufacturerString, value);

// fourth step -> save the state of all parameters on all scopes and elements
    CFMutableDataRef data = CFDataCreateMutable(NULL, 0);
    for (AudioUnitScope iscope = 0; iscope < 3; ++iscope) {
        AUScope &scope = GetScope(iscope);
        scope.SaveState (data);
    }

    SaveExtendedScopes(data);

// save all this in the data section of the dictionary
    CFDictionarySetValue(dict, kDataString, data);
    CFRelease (data);

//OK - now we're going to do some properties
//save the preset name...
    CFDictionarySetValue (dict, kNameString, mCurrentPreset.presetName);

// Does the unit support the RenderQuality property - if so, save it...
    value = 0;
    OSStatus result = DispatchGetProperty (kAudioUnitProperty_RenderQuality,
                                kAudioUnitScope_Global,
                                0,
                                &value);

    if (result == noErr) {
        AddNumToDictionary (dict, kRenderQualityString, value);
    }

// Does the unit support the CPULoad Quality property - if so, save it...
    Float32 cpuLoad;
    result = DispatchGetProperty (6/*kAudioUnitProperty_CPULoad*/,
                                kAudioUnitScope_Global,
                                0,
                                &cpuLoad);

    if (result == noErr) {
        CFNumberRef num = CFNumberCreate (NULL, kCFNumberFloatType, &cpuLoad);
        CFDictionarySetValue (dict, kCPULoadString, num);
        CFRelease (num);
    }

// Do we have any element names for any of our scopes?
    // first check to see if we have any names...
    bool foundName = false;
    for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
        foundName = GetScope (i).HasElementWithName();
        if (foundName)
            break;
    }
        // OK - we found a name away we go...
    if (foundName) {
        CFMutableDictionaryRef nameDict = CFDictionaryCreateMutable (NULL, 0,
                                &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        for (AudioUnitScope i = 0; i < kNumScopes; ++i) {
            GetScope (i).AddElementNamesToDict (nameDict);
        }

        CFDictionarySetValue (dict, kElementNameString, nameDict);
        CFRelease (nameDict);
    }

// we're done!!!
    *outData = dict;

    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::RestoreState(   CFPropertyListRef   plist)
{
    if (CFGetTypeID(plist) != CFDictionaryGetTypeID()) return kAudioUnitErr_InvalidPropertyValue;

    AudioComponentDescription desc = GetComponentDescription();

    CFDictionaryRef dict = static_cast<CFDictionaryRef>(plist);

// zeroeth step - make sure the Part key is NOT present, as this method is used
// to restore the GLOBAL state of the dictionary
    if (CFDictionaryContainsKey (dict, kPartString))
        return kAudioUnitErr_InvalidPropertyValue;

// first step -> check the saved version in the data ref
// at this point we're only dealing with version==0
    CFNumberRef cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kVersionString));
    if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
    SInt32 value;
    CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
    if (value != kCurrentSavedStateVersion) return kAudioUnitErr_InvalidPropertyValue;

// second step -> check that this data belongs to this kind of audio unit
// by checking the component subtype and manuID
// We're not checking the type, since there may be different versions (effect, format-converter, offline)
// of essentially the same AU
    cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kSubtypeString));
    if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
    CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
    if (UInt32(value) != desc.componentSubType) return kAudioUnitErr_InvalidPropertyValue;

    cfnum = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue (dict, kManufacturerString));
    if (cfnum == NULL) return kAudioUnitErr_InvalidPropertyValue;
    CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
    if (UInt32(value) != desc.componentManufacturer) return kAudioUnitErr_InvalidPropertyValue;

// fourth step -> restore the state of all of the parameters for each scope and element
    CFDataRef data = reinterpret_cast<CFDataRef>(CFDictionaryGetValue (dict, kDataString));
    if (data != NULL)
    {
        const UInt8 *p, *pend;

        p = CFDataGetBytePtr(data);
        pend = p + CFDataGetLength(data);

        // we have a zero length data, which may just mean there were no parameters to save!
        //  if (p >= pend) return noErr;

        while (p < pend) {
            UInt32 scopeIdx = CFSwapInt32BigToHost(*(UInt32 *)p);
            p += sizeof(UInt32);

            AUScope &scope = GetScope(scopeIdx);
            p = scope.RestoreState(p);
        }
    }

//OK - now we're going to do some properties
//restore the preset name...
    CFStringRef name = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (dict, kNameString));
    if (mCurrentPreset.presetName) CFRelease (mCurrentPreset.presetName);
    if (name)
    {
        mCurrentPreset.presetName = name;
        mCurrentPreset.presetNumber = -1;
    }
    else { // no name entry make the default one
        mCurrentPreset.presetName = kUntitledString;
        mCurrentPreset.presetNumber = -1;
    }

    CFRetain (mCurrentPreset.presetName);
#if !CA_USE_AUDIO_PLUGIN_ONLY
#ifndef __LP64__
    PropertyChanged(kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0);
#endif
#endif
    PropertyChanged(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);

// Does the dict contain render quality information?
    if (CFDictionaryGetValueIfPresent (dict, kRenderQualityString, reinterpret_cast<const void**>(&cfnum)))
    {
        CFNumberGetValue (cfnum, kCFNumberSInt32Type, &value);
        DispatchSetProperty (kAudioUnitProperty_RenderQuality,
                                kAudioUnitScope_Global,
                                0,
                                &value,
                                sizeof(value));
    }

// Does the unit support the CPULoad Quality property - if so, save it...
    if (CFDictionaryGetValueIfPresent (dict, kCPULoadString, reinterpret_cast<const void**>(&cfnum)))
    {
        Float32 floatValue;
        CFNumberGetValue (cfnum, kCFNumberFloatType, &floatValue);
        DispatchSetProperty (6/*kAudioUnitProperty_CPULoad*/,
                                kAudioUnitScope_Global,
                                0,
                                &floatValue,
                                sizeof(floatValue));
    }

// Do we have any element names for any of our scopes?
    CFDictionaryRef nameDict;
    if (CFDictionaryGetValueIfPresent (dict, kElementNameString, reinterpret_cast<const void**>(&nameDict)))
    {
        char string[64];
        for (int i = 0; i < kNumScopes; ++i)
        {
            snprintf (string, sizeof(string), "%d", i);
            CFStringRef key = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
            CFDictionaryRef elementDict;
            if (CFDictionaryGetValueIfPresent (nameDict, key, reinterpret_cast<const void**>(&elementDict)))
            {
                bool didAddElements = GetScope (i).RestoreElementNames (elementDict);
                if (didAddElements)
                    PropertyChanged (kAudioUnitProperty_ElementCount, i, 0);
            }
            CFRelease (key);
        }
    }

    return noErr;
}

OSStatus            AUBase::GetPresets (            CFArrayRef *                    outData) const
{
    return kAudioUnitErr_InvalidProperty;
}

OSStatus            AUBase::NewFactoryPresetSet (const AUPreset & inNewFactoryPreset)
{
    return kAudioUnitErr_InvalidProperty;
}

OSStatus            AUBase::NewCustomPresetSet (const AUPreset & inNewCustomPreset)
{
    CFRelease (mCurrentPreset.presetName);
    mCurrentPreset = inNewCustomPreset;
    CFRetain (mCurrentPreset.presetName);
    return noErr;
}

        // set the default preset for the unit -> the number of the preset MUST be >= 0
        // and the name should be valid, or the preset WON'T take
bool                AUBase::SetAFactoryPresetAsCurrent (const AUPreset & inPreset)
{
    if (inPreset.presetNumber < 0 || inPreset.presetName == NULL) return false;
    CFRelease (mCurrentPreset.presetName);
    mCurrentPreset = inPreset;
    CFRetain (mCurrentPreset.presetName);
    return true;
}

#if !CA_USE_AUDIO_PLUGIN_ONLY
int         AUBase::GetNumCustomUIComponents ()
{
    return 0;
}

void        AUBase::GetUIComponentDescs (ComponentDescription* inDescArray) {}
#endif

bool        AUBase::HasIcon ()
{
#if !CA_NO_AU_UI_FEATURES
    CFURLRef url = CopyIconLocation();
    if (url) {
        CFRelease (url);
        return true;
    }
#endif
    return false;
}

CFURLRef    AUBase::CopyIconLocation ()
{
    return NULL;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::GetParameterList(       AudioUnitScope                  inScope,
                                                    AudioUnitParameterID *          outParameterList,
                                                    UInt32 &                        outNumParameters)
{
    AUScope &scope = GetScope(inScope);
    AUElement *elementWithMostParameters = NULL;
    UInt32 maxNumParams = 0;

    int nElems = scope.GetNumberOfElements();
    for (int ielem = 0; ielem < nElems; ++ielem) {
        AUElement *element = scope.GetElement(ielem);
        UInt32 nParams = element->GetNumberOfParameters();
        if (nParams > maxNumParams) {
            maxNumParams = nParams;
            elementWithMostParameters = element;
        }
    }

    if (outParameterList != NULL && elementWithMostParameters != NULL)
        elementWithMostParameters->GetParameterList(outParameterList);

    outNumParameters = maxNumParams;
    return noErr;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::GetParameterInfo(       AudioUnitScope          inScope,
                                                    AudioUnitParameterID    inParameterID,
                                                    AudioUnitParameterInfo  &outParameterInfo )
{
    return kAudioUnitErr_InvalidParameter;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::GetParameterValueStrings(AudioUnitScope         inScope,
                                                    AudioUnitParameterID    inParameterID,
                                                    CFArrayRef *            outStrings)
{
    return kAudioUnitErr_InvalidProperty;
}

//_____________________________________________________________________________
//
OSStatus            AUBase::GetParameterHistoryInfo(    AudioUnitScope                  inScope,
                                                        AudioUnitParameterID            inParameterID,
                                                        Float32 &                       outUpdatesPerSecond,
                                                        Float32 &                       outHistoryDurationInSeconds)
{
    return kAudioUnitErr_InvalidProperty;
}


//_____________________________________________________________________________
//
OSStatus            AUBase::CopyClumpName(          AudioUnitScope          inScope,
                                                    UInt32                  inClumpID,
                                                    UInt32                  inDesiredNameLength,
                                                    CFStringRef *           outClumpName)
{
    return kAudioUnitErr_InvalidProperty;
}

//_____________________________________________________________________________
//
void                AUBase::SetNumberOfElements(    AudioUnitScope                  inScope,
                                                    UInt32                          numElements)
{
    if (inScope == kAudioUnitScope_Global && numElements != 1)
        COMPONENT_THROW(kAudioUnitErr_InvalidScope);

    GetScope(inScope).SetNumberOfElements(numElements);
}

//_____________________________________________________________________________
//
AUElement *         AUBase::CreateElement(          AudioUnitScope                  scope,
                                                    AudioUnitElement                element)
{
    switch (scope) {
    case kAudioUnitScope_Global:
        return new AUElement(this);
    case kAudioUnitScope_Input:
        return new AUInputElement(this);
    case kAudioUnitScope_Output:
        return new AUOutputElement(this);
#if !CA_BASIC_AU_FEATURES
    case kAudioUnitScope_Group:
        return new AUElement(this);
    case kAudioUnitScope_Part:
        return new AUElement(this);
#endif
    }
    COMPONENT_THROW(kAudioUnitErr_InvalidScope);

    return NULL;    // get rid of compiler warning
}

//_____________________________________________________________________________
//
bool    AUBase::FormatIsCanonical(      const CAStreamBasicDescription &f)
{
    return (f.mFormatID == kAudioFormatLinearPCM
        &&  f.mFramesPerPacket == 1
        &&  f.mBytesPerPacket == f.mBytesPerFrame
//      &&  f.mChannelsPerFrame >= 0    -- this is always true since it's unsigned
        // so far, it's a valid PCM format
#if CA_PREFER_FIXED_POINT
        &&  (f.mFormatFlags & kLinearPCMFormatFlagIsFloat) == 0
        &&  (((f.mFormatFlags & kLinearPCMFormatFlagsSampleFractionMask) >> kLinearPCMFormatFlagsSampleFractionShift) == kAudioUnitSampleFractionBits)
#else
        &&  (f.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0
#endif
        &&  ((f.mChannelsPerFrame == 1) || ((f.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0) == (mAudioUnitAPIVersion == 1))
#if TARGET_RT_BIG_ENDIAN
        &&  (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) != 0
#else
        &&  (f.mFormatFlags & kLinearPCMFormatFlagIsBigEndian) == 0
#endif
        &&  f.mBitsPerChannel == 8 * sizeof(AudioUnitSampleType)
        &&  f.mBytesPerFrame == f.NumberInterleavedChannels() * sizeof(AudioUnitSampleType)
        );
}

//_____________________________________________________________________________
//
void    AUBase::MakeCanonicalFormat(    CAStreamBasicDescription &      f,
                                        int                             nChannels)
{
    f.SetAUCanonical(nChannels, mAudioUnitAPIVersion < 2);  // interleaved for v1, non for v2
    f.mSampleRate = 0.0;
}

const Float64 AUBase::kNoLastRenderedSampleTime = -1.;

#include "AUBaseHelper.h"

char*   AUBase::GetLoggingString () const
{
    if (mLogString) return mLogString;

    AudioComponentDescription desc = GetComponentDescription();

    const size_t logStringSize = 256;
    const_cast<AUBase*>(this)->mLogString = new char[logStringSize];
    char str[24];
    char str1[24];
    char str2[24];
    snprintf (const_cast<AUBase*>(this)->mLogString, logStringSize, "AU (%p): %s %s %s",
        GetComponentInstance(),
        CAStringForOSType(desc.componentType, str, sizeof(str)),
        CAStringForOSType(desc.componentSubType, str1, sizeof(str1)),
        CAStringForOSType(desc.componentManufacturer, str2, sizeof(str2)));

    return mLogString;
}

