/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information

Abstract:
Part of Core Audio AUBase Classes
*/

#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 kAudioUnitProperty_NickName:
        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_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_IconLocation:
        {
            CFURLRef iconLocation = CopyIconLocation();
            if (iconLocation) {
                *(CFURLRef*)outData = iconLocation;
            } else
                result = kAudioUnitErr_InvalidProperty;
        }
        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 kAudioUnitProperty_NickName:
        // 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(SanityCheck(newDesc), InvalidFormat);

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

            const CAStreamBasicDescription curDesc = GetStreamFormat(inScope, inElement);

            if ( !curDesc.IsExactlyEqual(newDesc) ) {
                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.IsExactlyEqual(newDesc) ) {
                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 kAudioUnitProperty_NickName:
    {
        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 kAudioUnitProperty_NickName:
    {
        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 UInt64 lastTimeMessagePrinted = 0;
            UInt64 now = CAHostTimeBase::GetCurrentTime();
            if (now - lastTimeMessagePrinted > CAHostTimeBase::GetFrequency()) { // not more than once per second.
                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)
{
    bool isInterleaved = false;

    return inNewFormat.IsCommonFloat32(&isInterleaved) && !isInterleaved;
}

//_____________________________________________________________________________
//
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)
{
    if (inNewFormat.IsExactlyEqual(inPrevFormat))
        return noErr;

//#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;
}

