DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

DistrhoPluginAU.cpp (129594B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 // TODO
     18 // - g_nextBundlePath vs d_nextBundlePath cleanup
     19 // - scale points to kAudioUnitParameterFlag_ValuesHaveStrings
     20 // - report latency changes
     21 
     22 #include "DistrhoPluginInternal.hpp"
     23 #include "../DistrhoPluginUtils.hpp"
     24 
     25 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
     26 # include "../extra/RingBuffer.hpp"
     27 #endif
     28 
     29 #include <AudioUnit/AudioUnit.h>
     30 #include <AudioToolbox/AudioUnitUtilities.h>
     31 
     32 #include <map>
     33 #include <vector>
     34 
     35 #ifndef DISTRHO_PLUGIN_BRAND_ID
     36 # error DISTRHO_PLUGIN_BRAND_ID undefined!
     37 #endif
     38 
     39 #ifndef DISTRHO_PLUGIN_UNIQUE_ID
     40 # error DISTRHO_PLUGIN_UNIQUE_ID undefined!
     41 #endif
     42 
     43 START_NAMESPACE_DISTRHO
     44 
     45 // --------------------------------------------------------------------------------------------------------------------
     46 
     47 #ifndef __MAC_12_3
     48 enum {
     49     kAudioUnitProperty_MIDIOutputBufferSizeHint = 66,
     50 };
     51 #endif
     52 
     53 // --------------------------------------------------------------------------------------------------------------------
     54 
     55 static const char* AudioUnitPropertyID2Str(const AudioUnitPropertyID prop) noexcept
     56 {
     57     switch (prop)
     58     {
     59     #define PROP(s) case s: return #s;
     60     PROP(kAudioUnitProperty_ClassInfo)
     61     PROP(kAudioUnitProperty_MakeConnection)
     62     PROP(kAudioUnitProperty_SampleRate)
     63     PROP(kAudioUnitProperty_ParameterList)
     64     PROP(kAudioUnitProperty_ParameterInfo)
     65    #if !TARGET_OS_IPHONE
     66     PROP(kAudioUnitProperty_FastDispatch)
     67    #endif
     68     PROP(kAudioUnitProperty_CPULoad)
     69     PROP(kAudioUnitProperty_StreamFormat)
     70     PROP(kAudioUnitProperty_ElementCount)
     71     PROP(kAudioUnitProperty_Latency)
     72     PROP(kAudioUnitProperty_SupportedNumChannels)
     73     PROP(kAudioUnitProperty_MaximumFramesPerSlice)
     74     PROP(kAudioUnitProperty_ParameterValueStrings)
     75     PROP(kAudioUnitProperty_AudioChannelLayout)
     76     PROP(kAudioUnitProperty_TailTime)
     77     PROP(kAudioUnitProperty_BypassEffect)
     78     PROP(kAudioUnitProperty_LastRenderError)
     79     PROP(kAudioUnitProperty_SetRenderCallback)
     80     PROP(kAudioUnitProperty_FactoryPresets)
     81     PROP(kAudioUnitProperty_RenderQuality)
     82     PROP(kAudioUnitProperty_HostCallbacks)
     83     PROP(kAudioUnitProperty_InPlaceProcessing)
     84     PROP(kAudioUnitProperty_ElementName)
     85     PROP(kAudioUnitProperty_SupportedChannelLayoutTags)
     86     PROP(kAudioUnitProperty_PresentPreset)
     87     PROP(kAudioUnitProperty_DependentParameters)
     88     PROP(kAudioUnitProperty_InputSamplesInOutput)
     89     PROP(kAudioUnitProperty_ShouldAllocateBuffer)
     90     PROP(kAudioUnitProperty_FrequencyResponse)
     91     PROP(kAudioUnitProperty_ParameterHistoryInfo)
     92     PROP(kAudioUnitProperty_NickName)
     93     PROP(kAudioUnitProperty_OfflineRender)
     94     PROP(kAudioUnitProperty_ParameterIDName)
     95     PROP(kAudioUnitProperty_ParameterStringFromValue)
     96     PROP(kAudioUnitProperty_ParameterClumpName)
     97     PROP(kAudioUnitProperty_ParameterValueFromString)
     98     PROP(kAudioUnitProperty_PresentationLatency)
     99     PROP(kAudioUnitProperty_ClassInfoFromDocument)
    100     PROP(kAudioUnitProperty_RequestViewController)
    101     PROP(kAudioUnitProperty_ParametersForOverview)
    102     PROP(kAudioUnitProperty_SupportsMPE)
    103     PROP(kAudioUnitProperty_RenderContextObserver)
    104     PROP(kAudioUnitProperty_LastRenderSampleTime)
    105     PROP(kAudioUnitProperty_LoadedOutOfProcess)
    106    #if !TARGET_OS_IPHONE
    107     PROP(kAudioUnitProperty_SetExternalBuffer)
    108     PROP(kAudioUnitProperty_GetUIComponentList)
    109     PROP(kAudioUnitProperty_CocoaUI)
    110     PROP(kAudioUnitProperty_IconLocation)
    111     PROP(kAudioUnitProperty_AUHostIdentifier)
    112    #endif
    113     PROP(kAudioUnitProperty_MIDIOutputCallbackInfo)
    114     PROP(kAudioUnitProperty_MIDIOutputCallback)
    115     PROP(kAudioUnitProperty_MIDIOutputEventListCallback)
    116     PROP(kAudioUnitProperty_AudioUnitMIDIProtocol)
    117     PROP(kAudioUnitProperty_HostMIDIProtocol)
    118     PROP(kAudioUnitProperty_MIDIOutputBufferSizeHint)
    119     PROP(kMusicDeviceProperty_DualSchedulingMode)
    120     #undef PROP
    121     // DPF specific properties
    122     #define PROP(s) case d_cconst(#s): return #s;
    123     PROP(DPFi)
    124     PROP(DPFe)
    125     PROP(DPFp)
    126     PROP(DPFn)
    127     PROP(DPFo)
    128     PROP(DPFl)
    129     PROP(DPFs)
    130     PROP(DPFa)
    131     #undef PROP
    132     }
    133     return "[unknown]";
    134 }
    135 
    136 static const char* AudioUnitScope2Str(const AudioUnitScope scope) noexcept
    137 {
    138     switch (scope)
    139     {
    140     #define SCOPE(s) case s: return #s;
    141     SCOPE(kAudioUnitScope_Global)
    142     SCOPE(kAudioUnitScope_Input)
    143     SCOPE(kAudioUnitScope_Output)
    144     SCOPE(kAudioUnitScope_Group)
    145     SCOPE(kAudioUnitScope_Part)
    146     SCOPE(kAudioUnitScope_Note)
    147     SCOPE(kAudioUnitScope_Layer)
    148     SCOPE(kAudioUnitScope_LayerItem)
    149     #undef SCOPE
    150     }
    151     return "[unknown]";
    152 }
    153 
    154 static const char* AudioUnitSelector2Str(const SInt16 selector) noexcept
    155 {
    156     switch (selector)
    157     {
    158     #define SEL(s) case s: return #s;
    159     SEL(kAudioUnitInitializeSelect)
    160     SEL(kAudioUnitUninitializeSelect)
    161     SEL(kAudioUnitGetPropertyInfoSelect)
    162     SEL(kAudioUnitGetPropertySelect)
    163     SEL(kAudioUnitSetPropertySelect)
    164     SEL(kAudioUnitAddPropertyListenerSelect)
    165     SEL(kAudioUnitRemovePropertyListenerSelect)
    166     SEL(kAudioUnitRemovePropertyListenerWithUserDataSelect)
    167     SEL(kAudioUnitAddRenderNotifySelect)
    168     SEL(kAudioUnitRemoveRenderNotifySelect)
    169     SEL(kAudioUnitGetParameterSelect)
    170     SEL(kAudioUnitSetParameterSelect)
    171     SEL(kAudioUnitScheduleParametersSelect)
    172     SEL(kAudioUnitRenderSelect)
    173     SEL(kAudioUnitResetSelect)
    174     SEL(kAudioUnitComplexRenderSelect)
    175     SEL(kAudioUnitProcessSelect)
    176     SEL(kAudioUnitProcessMultipleSelect)
    177     SEL(kMusicDeviceMIDIEventSelect)
    178     SEL(kMusicDeviceSysExSelect)
    179     SEL(kMusicDevicePrepareInstrumentSelect)
    180     SEL(kMusicDeviceReleaseInstrumentSelect)
    181     SEL(kMusicDeviceStartNoteSelect)
    182     SEL(kMusicDeviceStopNoteSelect)
    183     SEL(kMusicDeviceMIDIEventListSelect)
    184     SEL(kAudioOutputUnitStartSelect)
    185     SEL(kAudioOutputUnitStopSelect)
    186     #undef SEL
    187     }
    188     return "[unknown]";
    189 }
    190 
    191 #if 0
    192 static OSStatus FastDispatchGetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32*);
    193 static OSStatus FastDispatchSetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32, UInt32);
    194 static OSStatus FastDispatchRender(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*);
    195 #endif
    196 
    197 // --------------------------------------------------------------------------------------------------------------------
    198 
    199 static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYPE));
    200 static constexpr const uint32_t kSubType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID));
    201 static constexpr const uint32_t kManufacturer = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID));
    202 
    203 static constexpr const uint32_t kWantedAudioFormat = kAudioFormatFlagsNativeFloatPacked
    204                                                    | kAudioFormatFlagIsNonInterleaved;
    205 
    206 
    207 // --------------------------------------------------------------------------------------------------------------------
    208 // clang `std::max` is not constexpr compatible, we need to define our own
    209 
    210 template<typename T>
    211 static inline constexpr T d_max(const T a, const T b) { return a > b ? a : b; }
    212 
    213 // --------------------------------------------------------------------------------------------------------------------
    214 
    215 static constexpr const AUChannelInfo kChannelInfo[] = {
    216     { DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS },
    217    #ifdef DISTRHO_PLUGIN_EXTRA_IO
    218     DISTRHO_PLUGIN_EXTRA_IO
    219    #endif
    220 };
    221 
    222 #ifdef DISTRHO_PLUGIN_EXTRA_IO
    223 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
    224 #error DISTRHO_PLUGIN_EXTRA_IO defined but no IO available
    225 #endif
    226 
    227 static inline
    228 bool isInputNumChannelsValid(const uint16_t numChannels)
    229 {
    230     for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i)
    231     {
    232         if (kChannelInfo[i].inChannels == numChannels)
    233             return true;
    234     }
    235     return false;
    236 }
    237 
    238 static inline
    239 bool isOutputNumChannelsValid(const uint16_t numChannels)
    240 {
    241     for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i)
    242     {
    243         if (kChannelInfo[i].outChannels == numChannels)
    244             return true;
    245     }
    246     return false;
    247 }
    248 
    249 static inline
    250 bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs)
    251 {
    252     for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i)
    253     {
    254         if (kChannelInfo[i].inChannels == numInputs && kChannelInfo[i].outChannels == numOutputs)
    255             return true;
    256     }
    257     return false;
    258 }
    259 #endif
    260 
    261 // --------------------------------------------------------------------------------------------------------------------
    262 
    263 struct PropertyListener {
    264     AudioUnitPropertyID	prop;
    265     AudioUnitPropertyListenerProc proc;
    266     void* userData;
    267 };
    268 
    269 struct RenderListener {
    270     AURenderCallback proc;
    271     void* userData;
    272 };
    273 
    274 typedef std::vector<PropertyListener> PropertyListeners;
    275 typedef std::vector<RenderListener> RenderListeners;
    276 
    277 // --------------------------------------------------------------------------------------------------------------------
    278 
    279 typedef struct {
    280     UInt32 numPackets;
    281     MIDIPacket packets[kMaxMidiEvents];
    282 } d_MIDIPacketList;
    283 
    284 // --------------------------------------------------------------------------------------------------------------------
    285 
    286 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    287 static constexpr const writeMidiFunc writeMidiCallback = nullptr;
    288 #endif
    289 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
    290 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
    291 #endif
    292 #if ! DISTRHO_PLUGIN_WANT_STATE
    293 static constexpr const updateStateValueFunc updateStateValueCallback = nullptr;
    294 #endif
    295 #if DISTRHO_PLUGIN_WANT_TIMEPOS
    296 static constexpr const double kDefaultTicksPerBeat = 1920.0;
    297 #endif
    298 
    299 typedef std::map<const String, String> StringMap;
    300 
    301 // --------------------------------------------------------------------------------------------------------------------
    302 
    303 class PluginAU
    304 {
    305 public:
    306     PluginAU(const AudioUnit component)
    307         : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback),
    308           fComponent(component),
    309           fLastRenderError(noErr),
    310           fPropertyListeners(),
    311           fRenderListeners(),
    312         #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    313           fInputConnectionBus(0),
    314           fInputConnectionUnit(nullptr),
    315           fSampleRateForInput(d_nextSampleRate),
    316          #ifdef DISTRHO_PLUGIN_EXTRA_IO
    317           fNumInputs(DISTRHO_PLUGIN_NUM_INPUTS),
    318          #endif
    319         #endif
    320         #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    321           fSampleRateForOutput(d_nextSampleRate),
    322          #ifdef DISTRHO_PLUGIN_EXTRA_IO
    323           fNumOutputs(DISTRHO_PLUGIN_NUM_OUTPUTS),
    324          #endif
    325         #endif
    326          #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    327           fAudioBufferList(nullptr),
    328          #endif
    329           fUsingRenderListeners(false),
    330           fParameterCount(fPlugin.getParameterCount()),
    331           fLastParameterValues(nullptr),
    332           fBypassParameterIndex(UINT32_MAX)
    333        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    334         , fMidiEventCount(0)
    335        #endif
    336        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    337         , fCurrentProgram(-1)
    338         , fLastFactoryProgram(0)
    339         , fProgramCount(fPlugin.getProgramCount())
    340         , fFactoryPresetsData(nullptr)
    341        #endif
    342        #if DISTRHO_PLUGIN_WANT_STATE
    343         , fStateCount(fPlugin.getStateCount())
    344        #endif
    345     {
    346 	    if (fParameterCount != 0)
    347         {
    348             fLastParameterValues = new float[fParameterCount];
    349             std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount);
    350 
    351             for (uint32_t i=0; i<fParameterCount; ++i)
    352             {
    353                 fLastParameterValues[i] = fPlugin.getParameterValue(i);
    354 
    355                 if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass)
    356                     fBypassParameterIndex = i;
    357             }
    358         }
    359 
    360        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    361         std::memset(&fInputRenderCallback, 0, sizeof(fInputRenderCallback));
    362         fInputRenderCallback.inputProc = nullptr;
    363         fInputRenderCallback.inputProcRefCon = nullptr;
    364        #endif
    365 
    366        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    367         std::memset(&fMidiEvents, 0, sizeof(fMidiEvents));
    368        #endif
    369 
    370        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    371         std::memset(&fMidiOutput, 0, sizeof(fMidiOutput));
    372         std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets));
    373        #endif
    374 
    375        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    376         if (fProgramCount != 0)
    377         {
    378             fFactoryPresetsData = new AUPreset[fProgramCount];
    379             std::memset(fFactoryPresetsData, 0, sizeof(AUPreset) * fProgramCount);
    380 
    381             for (uint32_t i=0; i<fProgramCount; ++i)
    382             {
    383                 fFactoryPresetsData[i].presetNumber = i;
    384 
    385                 if (const CFStringRef nameRef = CFStringCreateWithCString(nullptr,
    386                                                                           fPlugin.getProgramName(i),
    387                                                                           kCFStringEncodingUTF8))
    388                     fFactoryPresetsData[i].presetName = nameRef;
    389                 else
    390                     fFactoryPresetsData[i].presetName = CFSTR("");
    391             }
    392         }
    393         else
    394         {
    395             fFactoryPresetsData = new AUPreset;
    396             std::memset(fFactoryPresetsData, 0, sizeof(AUPreset));
    397 
    398             fFactoryPresetsData->presetNumber = 0;
    399             fFactoryPresetsData->presetName = CFSTR("Default");
    400         }
    401        #endif
    402 
    403         fUserPresetData.presetNumber = -1;
    404         fUserPresetData.presetName = CFSTR("");
    405 
    406        #if DISTRHO_PLUGIN_WANT_STATE
    407         for (uint32_t i=0; i<fStateCount; ++i)
    408         {
    409             const String& dkey(fPlugin.getStateKey(i));
    410             fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
    411         }
    412        #endif
    413 
    414        #if DISTRHO_PLUGIN_WANT_TIMEPOS
    415         std::memset(&fHostCallbackInfo, 0, sizeof(fHostCallbackInfo));
    416         fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat;
    417        #endif
    418     }
    419 
    420     ~PluginAU()
    421     {
    422         delete[] fLastParameterValues;
    423         CFRelease(fUserPresetData.presetName);
    424 
    425        #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    426         reallocAudioBufferList(false);
    427        #endif
    428 
    429        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    430         for (uint32_t i=0; i<fProgramCount; ++i)
    431             CFRelease(fFactoryPresetsData[i].presetName);
    432         delete[] fFactoryPresetsData;
    433        #endif
    434     }
    435 
    436     OSStatus auInitialize()
    437     {
    438        #if defined(DISTRHO_PLUGIN_EXTRA_IO) && DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    439         if (! isNumChannelsComboValid(fNumInputs, fNumOutputs))
    440             return kAudioUnitErr_FormatNotSupported;
    441        #endif
    442 
    443        #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    444         if (! reallocAudioBufferList(true))
    445             return kAudio_ParamError;
    446        #endif
    447 
    448        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    449         fMidiEventCount = 0;
    450        #endif
    451        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    452         fMidiOutputPackets.numPackets = 0;
    453        #endif
    454        #if DISTRHO_PLUGIN_WANT_TIMEPOS
    455         fTimePosition.clear();
    456         fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat;
    457        #endif
    458 
    459         fPlugin.activate();
    460         return noErr;
    461     }
    462 
    463     OSStatus auUninitialize()
    464     {
    465         fPlugin.deactivateIfNeeded();
    466 
    467        #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    468         reallocAudioBufferList(false);
    469        #endif
    470         return noErr;
    471     }
    472 
    473     OSStatus auGetPropertyInfo(const AudioUnitPropertyID inProp,
    474                                const AudioUnitScope inScope,
    475                                const AudioUnitElement inElement,
    476                                UInt32& outDataSize,
    477                                Boolean& outWritable)
    478     {
    479         switch (inProp)
    480         {
    481         case kAudioUnitProperty_ClassInfo:
    482             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    483             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    484             outDataSize = sizeof(CFPropertyListRef);
    485             outWritable = true;
    486             return noErr;
    487 
    488         case kAudioUnitProperty_MakeConnection:
    489             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
    490             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    491            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    492             outDataSize = sizeof(AudioUnitConnection);
    493             outWritable = true;
    494             return noErr;
    495            #else
    496             return kAudioUnitErr_InvalidProperty;
    497            #endif
    498 
    499         case kAudioUnitProperty_SampleRate:
    500             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    501            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    502             if (inScope == kAudioUnitScope_Input)
    503             {
    504                 outDataSize = sizeof(Float64);
    505                 outWritable = true;
    506                 return noErr;
    507             }
    508            #endif
    509            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    510             if (inScope == kAudioUnitScope_Output)
    511             {
    512                 outDataSize = sizeof(Float64);
    513                 outWritable = true;
    514                 return noErr;
    515             }
    516            #endif
    517             return kAudioUnitErr_InvalidScope;
    518 
    519         case kAudioUnitProperty_ParameterList:
    520             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    521             outDataSize = inScope == kAudioUnitScope_Global ? sizeof(AudioUnitParameterID) * fParameterCount : 0;
    522             outWritable = false;
    523             return noErr;
    524 
    525         case kAudioUnitProperty_ParameterInfo:
    526             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    527             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
    528             outDataSize = sizeof(AudioUnitParameterInfo);
    529             outWritable = false;
    530             return noErr;
    531 
    532        #if 0
    533         case kAudioUnitProperty_FastDispatch:
    534             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    535             outDataSize = sizeof(void*);
    536             outWritable = false;
    537             return noErr;
    538        #endif
    539 
    540         case kAudioUnitProperty_StreamFormat:
    541             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    542            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    543             if (inScope == kAudioUnitScope_Input)
    544             {
    545                 outDataSize = sizeof(AudioStreamBasicDescription);
    546                 outWritable = true;
    547                 return noErr;
    548             }
    549            #endif
    550            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    551             if (inScope == kAudioUnitScope_Output)
    552             {
    553                 outDataSize = sizeof(AudioStreamBasicDescription);
    554                 outWritable = true;
    555                 return noErr;
    556             }
    557            #endif
    558             return kAudioUnitErr_InvalidScope;
    559 
    560         case kAudioUnitProperty_ElementCount:
    561             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    562             outDataSize = sizeof(UInt32);
    563             outWritable = false;
    564             return noErr;
    565 
    566         case kAudioUnitProperty_Latency:
    567             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    568            #if DISTRHO_PLUGIN_WANT_LATENCY
    569             if (inScope == kAudioUnitScope_Global)
    570             {
    571                 outDataSize = sizeof(Float64);
    572                 outWritable = false;
    573                 return noErr;
    574             }
    575            #endif
    576             return kAudioUnitErr_InvalidProperty;
    577 
    578         case kAudioUnitProperty_SupportedNumChannels:
    579             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    580             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    581             outDataSize = sizeof(kChannelInfo);
    582             outWritable = false;
    583             return noErr;
    584 
    585         case kAudioUnitProperty_MaximumFramesPerSlice:
    586             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    587             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    588             outDataSize = sizeof(UInt32);
    589             outWritable = true;
    590             return noErr;
    591 
    592         case kAudioUnitProperty_BypassEffect:
    593             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    594             if (inScope == kAudioUnitScope_Global && fBypassParameterIndex != UINT32_MAX)
    595             {
    596                 outDataSize = sizeof(UInt32);
    597                 outWritable = true;
    598                 return noErr;
    599             }
    600             return kAudioUnitErr_InvalidProperty;
    601 
    602         case kAudioUnitProperty_LastRenderError:
    603             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    604             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    605             outDataSize = sizeof(OSStatus);
    606             outWritable = false;
    607             return noErr;
    608 
    609         case kAudioUnitProperty_SetRenderCallback:
    610             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
    611             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    612            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    613             outDataSize = sizeof(AURenderCallbackStruct);
    614             outWritable = true;
    615             return noErr;
    616            #else
    617             return kAudioUnitErr_InvalidProperty;
    618            #endif
    619 
    620         case kAudioUnitProperty_FactoryPresets:
    621             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    622             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    623            #if DISTRHO_PLUGIN_WANT_PROGRAMS
    624             outDataSize = sizeof(CFArrayRef);
    625             outWritable = false;
    626             return noErr;
    627            #else
    628             return kAudioUnitErr_InvalidProperty;
    629            #endif
    630 
    631         case kAudioUnitProperty_HostCallbacks:
    632             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    633             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    634            #if DISTRHO_PLUGIN_WANT_TIMEPOS
    635             outDataSize = sizeof(HostCallbackInfo);
    636             outWritable = false;
    637             return noErr;
    638            #else
    639             return kAudioUnitErr_InvalidProperty;
    640            #endif
    641 
    642         case kAudioUnitProperty_InPlaceProcessing:
    643             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    644             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    645            #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    646             outDataSize = sizeof(UInt32);
    647             outWritable = false;
    648             return noErr;
    649            #else
    650             return kAudioUnitErr_InvalidProperty;
    651            #endif
    652 
    653         case kAudioUnitProperty_PresentPreset:
    654             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    655             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    656             outDataSize = sizeof(AUPreset);
    657             outWritable = true;
    658             return noErr;
    659 
    660         case kAudioUnitProperty_CocoaUI:
    661             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    662             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    663            #if DISTRHO_PLUGIN_HAS_UI
    664             outDataSize = sizeof(AudioUnitCocoaViewInfo);
    665             outWritable = false;
    666             return noErr;
    667            #else
    668             return kAudioUnitErr_InvalidProperty;
    669            #endif
    670 
    671         case kAudioUnitProperty_MIDIOutputCallbackInfo:
    672             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    673             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    674            #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    675             outDataSize = sizeof(CFArrayRef);
    676             outWritable = false;
    677             return noErr;
    678            #else
    679             return kAudioUnitErr_InvalidProperty;
    680            #endif
    681 
    682         case kAudioUnitProperty_MIDIOutputCallback:
    683             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    684             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    685            #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    686             outDataSize = sizeof(AUMIDIOutputCallbackStruct);
    687             outWritable = true;
    688             return noErr;
    689            #else
    690             return kAudioUnitErr_InvalidProperty;
    691            #endif
    692 
    693         case kAudioUnitProperty_AudioUnitMIDIProtocol:
    694             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    695             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    696             // FIXME implement the event list stuff
    697            #if 0 && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT)
    698             outDataSize = sizeof(SInt32);
    699             outWritable = false;
    700             return noErr;
    701            #else
    702             return kAudioUnitErr_InvalidProperty;
    703            #endif
    704 
    705         case 'DPFi':
    706             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    707             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    708             outDataSize = sizeof(uint16_t);
    709             outWritable = false;
    710             return noErr;
    711 
    712         case 'DPFe':
    713             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    714             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
    715             outDataSize = sizeof(uint8_t);
    716             outWritable = true;
    717             return noErr;
    718 
    719         case 'DPFp':
    720             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    721             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
    722             outDataSize = sizeof(float);
    723             outWritable = true;
    724             return noErr;
    725 
    726        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    727         case 'DPFn':
    728             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    729             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    730             outDataSize = sizeof(uint8_t) * 3;
    731             outWritable = true;
    732             return noErr;
    733        #endif
    734 
    735        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    736         case 'DPFo':
    737             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    738             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    739             outDataSize = sizeof(uint32_t);
    740             outWritable = false;
    741             return noErr;
    742        #endif
    743 
    744        #if DISTRHO_PLUGIN_WANT_STATE
    745         case 'DPFl':
    746             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    747             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    748             outDataSize = sizeof(CFArrayRef);
    749             outWritable = false;
    750             return noErr;
    751 
    752         case 'DPFs':
    753             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    754             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement);
    755             outDataSize = sizeof(CFStringRef);
    756             outWritable = true;
    757             return noErr;
    758        #endif
    759 
    760        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
    761         case 'DPFa':
    762             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
    763             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    764             outDataSize = sizeof(void*);
    765             outWritable = false;
    766             return noErr;
    767        #endif
    768 
    769         // unwanted properties
    770         case kAudioUnitProperty_CPULoad:
    771         case kAudioUnitProperty_RenderContextObserver:
    772         case kAudioUnitProperty_AudioChannelLayout:
    773         case kAudioUnitProperty_TailTime:
    774         case kAudioUnitProperty_SupportedChannelLayoutTags:
    775         case kMusicDeviceProperty_DualSchedulingMode:
    776             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
    777             return kAudioUnitErr_InvalidProperty;
    778         }
    779 
    780         d_stdout("TODO GetPropertyInfo(%d:%x:%s, %d:%s, %d, ...)",
    781                  inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
    782         return kAudioUnitErr_InvalidProperty;
    783     }
    784 
    785     OSStatus auGetProperty(const AudioUnitPropertyID inProp,
    786                            const AudioUnitScope inScope,
    787                            const AudioUnitElement inElement,
    788                            void* const outData)
    789     {
    790         switch (inProp)
    791         {
    792         case kAudioUnitProperty_ClassInfo:
    793             *static_cast<CFPropertyListRef*>(outData) = retrieveClassInfo();
    794             return noErr;
    795 
    796         case kAudioUnitProperty_SampleRate:
    797            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    798             if (inScope == kAudioUnitScope_Input)
    799             {
    800                 *static_cast<Float64*>(outData) = fSampleRateForInput;
    801             }
    802             else
    803            #endif
    804            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    805             if (inScope == kAudioUnitScope_Output)
    806             {
    807                 *static_cast<Float64*>(outData) = fSampleRateForOutput;
    808             }
    809             else
    810            #endif
    811             {
    812                 return kAudioUnitErr_InvalidScope;
    813             }
    814             return noErr;
    815 
    816         case kAudioUnitProperty_ParameterList:
    817             {
    818                 AudioUnitParameterID* const paramList = static_cast<AudioUnitParameterID*>(outData);
    819 
    820                 for (uint32_t i=0; i<fParameterCount; ++i)
    821                     paramList[i] = i;
    822             }
    823             return noErr;
    824 
    825         case kAudioUnitProperty_ParameterInfo:
    826             {
    827                 AudioUnitParameterInfo* const info = static_cast<AudioUnitParameterInfo*>(outData);
    828                 std::memset(info, 0, sizeof(*info));
    829 
    830                 const ParameterRanges& ranges(fPlugin.getParameterRanges(inElement));
    831 
    832                 info->flags = kAudioUnitParameterFlag_IsHighResolution
    833                             | kAudioUnitParameterFlag_IsReadable
    834                             | kAudioUnitParameterFlag_HasCFNameString;
    835 
    836                 if (fPlugin.getParameterDesignation(inElement) == kParameterDesignationBypass)
    837                 {
    838                     info->flags |= kAudioUnitParameterFlag_IsWritable;
    839                     info->unit = kAudioUnitParameterUnit_Boolean;
    840 
    841                     d_strncpy(info->name, "Bypass", sizeof(info->name));
    842                     info->cfNameString = CFSTR("Bypass");
    843                 }
    844                 else
    845                 {
    846                     const uint32_t hints = fPlugin.getParameterHints(inElement);
    847 
    848                     info->flags |= kAudioUnitParameterFlag_CFNameRelease;
    849 
    850                     if (hints & kParameterIsOutput)
    851                     {
    852                         info->flags |= kAudioUnitParameterFlag_MeterReadOnly;
    853                     }
    854                     else
    855                     {
    856                         info->flags |= kAudioUnitParameterFlag_IsWritable;
    857 
    858                         if ((hints & kParameterIsAutomatable) == 0x0)
    859                             info->flags |= kAudioUnitParameterFlag_NonRealTime;
    860                     }
    861 
    862                     if (hints & kParameterIsBoolean)
    863                         info->unit = kAudioUnitParameterUnit_Boolean;
    864                     else if (hints & kParameterIsInteger)
    865                         info->unit = kAudioUnitParameterUnit_Indexed;
    866                     else
    867                         info->unit = kAudioUnitParameterUnit_Generic;
    868 
    869                     // | kAudioUnitParameterFlag_ValuesHaveStrings;
    870 
    871                     const String& name(fPlugin.getParameterName(inElement));
    872                     d_strncpy(info->name, name, sizeof(info->name));
    873                     info->cfNameString = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8);
    874                 }
    875 
    876                 info->minValue = ranges.min;
    877                 info->maxValue = ranges.max;
    878                 info->defaultValue = ranges.def;
    879             }
    880             return noErr;
    881 
    882        #if 0
    883         case kAudioUnitProperty_FastDispatch:
    884             switch (inElement)
    885             {
    886 			case kAudioUnitGetParameterSelect:
    887                 *static_cast<AudioUnitGetParameterProc*>(outData) = FastDispatchGetParameter;
    888                 return noErr;
    889 			case kAudioUnitSetParameterSelect:
    890                 *static_cast<AudioUnitSetParameterProc*>(outData) = FastDispatchSetParameter;
    891                 return noErr;
    892 			case kAudioUnitRenderSelect:
    893                 *static_cast<AudioUnitRenderProc*>(outData) = FastDispatchRender;
    894                 return noErr;
    895             }
    896             d_stdout("WIP FastDispatch(%d:%x:%s)", inElement, inElement, AudioUnitPropertyID2Str(inElement));
    897             return kAudioUnitErr_InvalidElement;
    898        #endif
    899 
    900         case kAudioUnitProperty_StreamFormat:
    901             {
    902                 AudioStreamBasicDescription* const desc = static_cast<AudioStreamBasicDescription*>(outData);
    903                 std::memset(desc, 0, sizeof(*desc));
    904 
    905                 if (inElement != 0)
    906                     return kAudioUnitErr_InvalidElement;
    907 
    908                #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    909                 if (inScope == kAudioUnitScope_Input)
    910                 {
    911                    #ifdef DISTRHO_PLUGIN_EXTRA_IO
    912                     desc->mChannelsPerFrame = fNumInputs;
    913                    #else
    914                     desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_INPUTS;
    915                    #endif
    916                 }
    917                 else
    918                #endif
    919                #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    920                 if (inScope == kAudioUnitScope_Output)
    921                 {
    922                    #ifdef DISTRHO_PLUGIN_EXTRA_IO
    923                     desc->mChannelsPerFrame = fNumOutputs;
    924                    #else
    925                     desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_OUTPUTS;
    926                    #endif
    927                 }
    928                 else
    929                #endif
    930                 {
    931                     return kAudioUnitErr_InvalidScope;
    932                 }
    933 
    934                 desc->mFormatID         = kAudioFormatLinearPCM;
    935                 desc->mFormatFlags      = kWantedAudioFormat;
    936                 desc->mSampleRate       = fPlugin.getSampleRate();
    937                 desc->mBitsPerChannel   = 32;
    938                 desc->mBytesPerFrame    = sizeof(float);
    939                 desc->mBytesPerPacket   = sizeof(float);
    940                 desc->mFramesPerPacket  = 1;
    941             }
    942             return noErr;
    943 
    944         case kAudioUnitProperty_ElementCount:
    945             switch (inScope)
    946             {
    947             case kAudioUnitScope_Global:
    948                 *static_cast<UInt32*>(outData) = 1;
    949                 break;
    950             case kAudioUnitScope_Input:
    951                 *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_INPUTS != 0 ? 1 : 0;
    952                 break;
    953             case kAudioUnitScope_Output:
    954                 *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? 1 : 0;
    955                 break;
    956             default:
    957                 *static_cast<UInt32*>(outData) = 0;
    958                 break;
    959             }
    960             return noErr;
    961 
    962        #if DISTRHO_PLUGIN_WANT_LATENCY
    963         case kAudioUnitProperty_Latency:
    964             *static_cast<Float64*>(outData) = static_cast<double>(fPlugin.getLatency()) / fPlugin.getSampleRate();
    965             return noErr;
    966        #endif
    967 
    968         case kAudioUnitProperty_SupportedNumChannels:
    969             std::memcpy(outData, kChannelInfo, sizeof(kChannelInfo));
    970             return noErr;
    971 
    972         case kAudioUnitProperty_MaximumFramesPerSlice:
    973             *static_cast<UInt32*>(outData) = fPlugin.getBufferSize();
    974             return noErr;
    975 
    976         case kAudioUnitProperty_LastRenderError:
    977             *static_cast<OSStatus*>(outData) = fLastRenderError;
    978             fLastRenderError = noErr;
    979             return noErr;
    980 
    981         case kAudioUnitProperty_BypassEffect:
    982             *static_cast<OSStatus*>(outData) = fPlugin.getParameterValue(fBypassParameterIndex) > 0.5f ? 1 : 0;
    983             return noErr;
    984 
    985        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    986         case kAudioUnitProperty_SetRenderCallback:
    987             std::memcpy(outData, &fInputRenderCallback, sizeof(AURenderCallbackStruct));
    988             return noErr;
    989        #endif
    990 
    991        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    992         case kAudioUnitProperty_FactoryPresets:
    993             if (const CFMutableArrayRef presetsRef = CFArrayCreateMutable(nullptr, fProgramCount, nullptr))
    994             {
    995                 for (uint32_t i=0; i<fProgramCount; ++i)
    996                     CFArrayAppendValue(presetsRef, &fFactoryPresetsData[i]);
    997 
    998                 *static_cast<CFArrayRef*>(outData) = presetsRef;
    999                 return noErr;
   1000             }
   1001             return kAudio_ParamError;
   1002        #endif
   1003 
   1004        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1005         case kAudioUnitProperty_HostCallbacks:
   1006             std::memcpy(outData, &fHostCallbackInfo, sizeof(HostCallbackInfo));
   1007             return noErr;
   1008        #endif
   1009 
   1010        #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1011         case kAudioUnitProperty_InPlaceProcessing:
   1012             *static_cast<UInt32*>(outData) = 1;
   1013             return noErr;
   1014        #endif
   1015 
   1016         case kAudioUnitProperty_PresentPreset:
   1017            #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1018             if (fCurrentProgram >= 0)
   1019             {
   1020                 std::memcpy(outData, &fFactoryPresetsData[fCurrentProgram], sizeof(AUPreset));
   1021             }
   1022             else
   1023            #endif
   1024             {
   1025                 std::memcpy(outData, &fUserPresetData, sizeof(AUPreset));
   1026             }
   1027             return noErr;
   1028 
   1029        #if DISTRHO_PLUGIN_HAS_UI
   1030         case kAudioUnitProperty_CocoaUI:
   1031             {
   1032                 AudioUnitCocoaViewInfo* const info = static_cast<AudioUnitCocoaViewInfo*>(outData);
   1033                 std::memset(info, 0, sizeof(*info));
   1034 
   1035                 NSString* const bundlePathString = [[NSString alloc]
   1036                     initWithBytes:d_nextBundlePath
   1037                            length:strlen(d_nextBundlePath)
   1038                          encoding:NSUTF8StringEncoding];
   1039 
   1040                 info->mCocoaAUViewBundleLocation = static_cast<CFURLRef>([[NSURL fileURLWithPath: bundlePathString] retain]);
   1041 
   1042                 #define MACRO_STR3(a, b, c) a "_" b "_" c
   1043                 #define MACRO_STR2(a, b, c) MACRO_STR3(#a, #b, #c)
   1044                 #define MACRO_STR(a, b, c) MACRO_STR2(a, b, c)
   1045 
   1046                 info->mCocoaAUViewClass[0] = CFSTR("CocoaAUView_" MACRO_STR(DISTRHO_PLUGIN_AU_TYPE,
   1047                                                                             DISTRHO_PLUGIN_UNIQUE_ID,
   1048                                                                             DISTRHO_PLUGIN_BRAND_ID));
   1049 
   1050                 #undef MACRO_STR
   1051                 #undef MACRO_STR2
   1052                 #undef MACRO_STR3
   1053 
   1054                 [bundlePathString release];
   1055             }
   1056             return noErr;
   1057        #endif
   1058 
   1059        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1060         case kAudioUnitProperty_MIDIOutputCallbackInfo:
   1061             {
   1062                 CFStringRef refs[1] = { CFSTR("MIDI Output") };
   1063                 *static_cast<CFArrayRef*>(outData) = CFArrayCreate(nullptr,
   1064                                                                    reinterpret_cast<const void**>(refs),
   1065                                                                    1,
   1066                                                                    &kCFTypeArrayCallBacks);
   1067             }
   1068             return noErr;
   1069        #endif
   1070 
   1071        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1072         /* FIXME implement the event list stuff
   1073         case kAudioUnitProperty_AudioUnitMIDIProtocol:
   1074             *static_cast<SInt32*>(outData) = kMIDIProtocol_1_0;
   1075             return noErr;
   1076         */
   1077        #endif
   1078 
   1079         case 'DPFp':
   1080             *static_cast<float*>(outData) = fPlugin.getParameterValue(inElement);
   1081             return noErr;
   1082 
   1083        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1084         case 'DPFo':
   1085             *static_cast<uint32_t*>(outData) = fLastFactoryProgram;
   1086             return noErr;
   1087        #endif
   1088 
   1089        #if DISTRHO_PLUGIN_WANT_STATE
   1090         case 'DPFl':
   1091             if (const CFMutableArrayRef keysRef = CFArrayCreateMutable(nullptr,
   1092                                                                        fStateCount,
   1093                                                                        &kCFTypeArrayCallBacks))
   1094             {
   1095                 for (uint32_t i=0; i<fStateCount; ++i)
   1096                 {
   1097                     const CFStringRef keyRef = CFStringCreateWithCString(nullptr,
   1098                                                                          fPlugin.getStateKey(i),
   1099                                                                          kCFStringEncodingASCII);
   1100                     CFArrayAppendValue(keysRef, keyRef);
   1101                     CFRelease(keyRef);
   1102                 }
   1103 
   1104                 *static_cast<CFArrayRef*>(outData) = keysRef;
   1105                 return noErr;
   1106             }
   1107             return kAudio_ParamError;
   1108 
   1109         case 'DPFs':
   1110             {
   1111                 const String& key(fPlugin.getStateKey(inElement));
   1112                #if DISTRHO_PLUGIN_WANT_FULL_STATE
   1113                 fStateMap[key] = fPlugin.getStateValue(key);
   1114                #endif
   1115 
   1116                 *static_cast<CFStringRef*>(outData) = CFStringCreateWithCString(nullptr,
   1117                                                                                 fStateMap[key],
   1118                                                                                 kCFStringEncodingUTF8);
   1119             }
   1120             return noErr;
   1121        #endif
   1122 
   1123        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
   1124         case 'DPFa':
   1125             *static_cast<void**>(outData) = fPlugin.getInstancePointer();
   1126             return noErr;
   1127        #endif
   1128         }
   1129 
   1130         d_stdout("TODO GetProperty(%d:%x:%s, %d:%s, %d, ...)",
   1131                  inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
   1132         return kAudioUnitErr_InvalidProperty;
   1133     }
   1134 
   1135     OSStatus auSetProperty(const AudioUnitPropertyID inProp,
   1136                            const AudioUnitScope inScope,
   1137                            const AudioUnitElement inElement,
   1138                            const void* const inData,
   1139                            const UInt32 inDataSize)
   1140     {
   1141         switch (inProp)
   1142         {
   1143         case kAudioUnitProperty_ClassInfo:
   1144             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1145             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1146             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFPropertyListRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1147             {
   1148                 const CFPropertyListRef propList = *static_cast<const CFPropertyListRef*>(inData);
   1149                 DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue);
   1150 
   1151                 restoreClassInfo(static_cast<CFDictionaryRef>(propList));
   1152             }
   1153             return noErr;
   1154 
   1155         case kAudioUnitProperty_MakeConnection:
   1156             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
   1157             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1158             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioUnitConnection), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1159            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1160             {
   1161                 const AudioUnitConnection conn = *static_cast<const AudioUnitConnection*>(inData);
   1162 
   1163                 if (conn.sourceAudioUnit == nullptr)
   1164                 {
   1165                     fInputConnectionBus = 0;
   1166                     fInputConnectionUnit = nullptr;
   1167                     return noErr;
   1168                 }
   1169 
   1170                 AudioStreamBasicDescription desc;
   1171                 std::memset(&desc, 0, sizeof(desc));
   1172                 UInt32 dataSize = sizeof(AudioStreamBasicDescription);
   1173                 if (AudioUnitGetProperty(conn.sourceAudioUnit,
   1174                                          kAudioUnitProperty_StreamFormat,
   1175                                          kAudioUnitScope_Output,
   1176                                          conn.sourceOutputNumber, &desc, &dataSize) != noErr)
   1177                     return kAudioUnitErr_InvalidPropertyValue;
   1178 
   1179                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatID == kAudioFormatLinearPCM,
   1180                                                desc.mFormatID, kAudioUnitErr_FormatNotSupported);
   1181                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBitsPerChannel == 32,
   1182                                                desc.mBitsPerChannel, kAudioUnitErr_FormatNotSupported);
   1183                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerFrame == sizeof(float),
   1184                                                desc.mBytesPerFrame, kAudioUnitErr_FormatNotSupported);
   1185                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerPacket == sizeof(float),
   1186                                                desc.mBytesPerPacket, kAudioUnitErr_FormatNotSupported);
   1187                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFramesPerPacket == 1,
   1188                                                desc.mFramesPerPacket, kAudioUnitErr_FormatNotSupported);
   1189                 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatFlags == kWantedAudioFormat,
   1190                                                desc.mFormatFlags, kAudioUnitErr_FormatNotSupported);
   1191                #ifdef DISTRHO_PLUGIN_EXTRA_IO
   1192                 DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == fNumInputs,
   1193                                                 desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported);
   1194                #else
   1195                 DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == DISTRHO_PLUGIN_NUM_INPUTS,
   1196                                                 desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported);
   1197                #endif
   1198 
   1199                 fInputConnectionBus = conn.sourceOutputNumber;
   1200                 fInputConnectionUnit = conn.sourceAudioUnit;
   1201             }
   1202             return noErr;
   1203            #else
   1204             return kAudioUnitErr_PropertyNotInUse;
   1205            #endif
   1206 
   1207         case kAudioUnitProperty_SampleRate:
   1208            #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1209             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
   1210            #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1211             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
   1212            #else
   1213             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
   1214            #endif
   1215             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1216             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(Float64), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1217             {
   1218                #if DISTRHO_PLUGIN_NUM_INPUTS != 0 || DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1219                 const Float64 sampleRate = *static_cast<const Float64*>(inData);
   1220                #endif
   1221 
   1222                #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1223                 if (inScope == kAudioUnitScope_Input)
   1224                 {
   1225                     if (d_isNotEqual(fSampleRateForInput, sampleRate))
   1226                     {
   1227                         fSampleRateForInput = sampleRate;
   1228                         d_nextSampleRate = sampleRate;
   1229 
   1230                        #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1231                         if (d_isEqual(fSampleRateForOutput, sampleRate))
   1232                        #endif
   1233                         {
   1234                             fPlugin.setSampleRate(sampleRate, true);
   1235                         }
   1236 
   1237                         notifyPropertyListeners(inProp, inScope, inElement);
   1238                     }
   1239                     return noErr;
   1240                 }
   1241                #endif
   1242 
   1243                #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1244                 if (inScope == kAudioUnitScope_Output)
   1245                 {
   1246                     if (d_isNotEqual(fSampleRateForOutput, sampleRate))
   1247                     {
   1248                         fSampleRateForOutput = sampleRate;
   1249                         d_nextSampleRate = sampleRate;
   1250 
   1251                        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1252                         if (d_isEqual(fSampleRateForInput, sampleRate))
   1253                        #endif
   1254                         {
   1255                             fPlugin.setSampleRate(sampleRate, true);
   1256                         }
   1257 
   1258                         notifyPropertyListeners(inProp, inScope, inElement);
   1259                     }
   1260                     return noErr;
   1261                 }
   1262                #endif
   1263             }
   1264             return kAudioUnitErr_PropertyNotInUse;
   1265 
   1266         case kAudioUnitProperty_StreamFormat:
   1267            #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1268             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
   1269            #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1270             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
   1271            #else
   1272             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
   1273            #endif
   1274             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1275             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioStreamBasicDescription), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1276             {
   1277                 const AudioStreamBasicDescription* const desc = static_cast<const AudioStreamBasicDescription*>(inData);
   1278 
   1279                 if (desc->mFormatID != kAudioFormatLinearPCM)
   1280                     return kAudioUnitErr_FormatNotSupported;
   1281                 if (desc->mBitsPerChannel != 32)
   1282                     return kAudioUnitErr_FormatNotSupported;
   1283                 if (desc->mBytesPerFrame != sizeof(float))
   1284                     return kAudioUnitErr_FormatNotSupported;
   1285                 if (desc->mBytesPerPacket != sizeof(float))
   1286                     return kAudioUnitErr_FormatNotSupported;
   1287                 if (desc->mFramesPerPacket != 1)
   1288                     return kAudioUnitErr_FormatNotSupported;
   1289                 if (desc->mFormatFlags != kWantedAudioFormat)
   1290                     return kAudioUnitErr_FormatNotSupported;
   1291 
   1292                #ifndef DISTRHO_PLUGIN_EXTRA_IO
   1293                 if (desc->mChannelsPerFrame != (inScope == kAudioUnitScope_Input ? DISTRHO_PLUGIN_NUM_INPUTS
   1294                                                                                  : DISTRHO_PLUGIN_NUM_OUTPUTS))
   1295                     return kAudioUnitErr_FormatNotSupported;
   1296                #endif
   1297 
   1298                #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1299                 if (inScope == kAudioUnitScope_Input)
   1300                 {
   1301                     bool changed = false;
   1302 
   1303                    #ifdef DISTRHO_PLUGIN_EXTRA_IO
   1304                     if (! isInputNumChannelsValid(desc->mChannelsPerFrame))
   1305                         return kAudioUnitErr_FormatNotSupported;
   1306 
   1307                     if (fNumInputs != desc->mChannelsPerFrame)
   1308                     {
   1309                         changed = true;
   1310                         fNumInputs = desc->mChannelsPerFrame;
   1311 
   1312                        #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1313                         if (isNumChannelsComboValid(fNumInputs, fNumOutputs))
   1314                        #endif
   1315                         {
   1316                             fPlugin.setAudioPortIO(fNumInputs, fNumOutputs);
   1317                         }
   1318                     }
   1319                    #endif
   1320 
   1321                     if (d_isNotEqual(fSampleRateForInput, desc->mSampleRate))
   1322                     {
   1323                         changed = true;
   1324                         fSampleRateForInput = desc->mSampleRate;
   1325 
   1326                        #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1327                         if (d_isEqual(fSampleRateForOutput, desc->mSampleRate))
   1328                        #endif
   1329                         {
   1330                             fPlugin.setSampleRate(desc->mSampleRate, true);
   1331                         }
   1332 
   1333                         notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
   1334                     }
   1335 
   1336                     if (changed)
   1337                         notifyPropertyListeners(inProp, inScope, inElement);
   1338 
   1339                     return noErr;
   1340                 }
   1341                #endif
   1342 
   1343                #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1344                 if (inScope == kAudioUnitScope_Output)
   1345                 {
   1346                     bool changed = false;
   1347 
   1348                    #ifdef DISTRHO_PLUGIN_EXTRA_IO
   1349                     if (! isOutputNumChannelsValid(desc->mChannelsPerFrame))
   1350                         return kAudioUnitErr_FormatNotSupported;
   1351 
   1352                     if (fNumOutputs != desc->mChannelsPerFrame)
   1353                     {
   1354                         changed = true;
   1355                         fNumOutputs = desc->mChannelsPerFrame;
   1356 
   1357                        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1358                         if (isNumChannelsComboValid(fNumInputs, fNumOutputs))
   1359                        #endif
   1360                         {
   1361                             fPlugin.setAudioPortIO(fNumInputs, fNumOutputs);
   1362                         }
   1363                     }
   1364                    #endif
   1365 
   1366                     if (d_isNotEqual(fSampleRateForOutput, desc->mSampleRate))
   1367                     {
   1368                         changed = true;
   1369                         fSampleRateForOutput = desc->mSampleRate;
   1370 
   1371                        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1372                         if (d_isEqual(fSampleRateForInput, desc->mSampleRate))
   1373                        #endif
   1374                         {
   1375                             fPlugin.setSampleRate(desc->mSampleRate, true);
   1376                         }
   1377 
   1378                         notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
   1379                     }
   1380 
   1381                     if (changed)
   1382                         notifyPropertyListeners(inProp, inScope, inElement);
   1383 
   1384                     return noErr;
   1385                 }
   1386                #endif
   1387             }
   1388             return kAudioUnitErr_PropertyNotInUse;
   1389 
   1390         case kAudioUnitProperty_MaximumFramesPerSlice:
   1391             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1392             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1393             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1394             {
   1395                 const UInt32 bufferSize = *static_cast<const UInt32*>(inData);
   1396 
   1397                 if (fPlugin.setBufferSize(bufferSize, true))
   1398                     notifyPropertyListeners(inProp, inScope, inElement);
   1399             }
   1400             return noErr;
   1401 
   1402         case kAudioUnitProperty_BypassEffect:
   1403             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1404             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1405             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1406             DISTRHO_SAFE_ASSERT_RETURN(fBypassParameterIndex != UINT32_MAX, kAudioUnitErr_PropertyNotInUse);
   1407             {
   1408                 const bool bypass = *static_cast<const UInt32*>(inData) != 0;
   1409 
   1410                 if ((fLastParameterValues[fBypassParameterIndex] > 0.5f) != bypass)
   1411                 {
   1412                     const float value = bypass ? 1.f : 0.f;
   1413                     fLastParameterValues[fBypassParameterIndex] = value;
   1414                     fPlugin.setParameterValue(fBypassParameterIndex, value);
   1415 	                notifyPropertyListeners(inProp, inScope, inElement);
   1416                 }
   1417             }
   1418             return noErr;
   1419 
   1420         case kAudioUnitProperty_SetRenderCallback:
   1421             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
   1422             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1423             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AURenderCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1424            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1425             std::memcpy(&fInputRenderCallback, inData, sizeof(AURenderCallbackStruct));
   1426             return noErr;
   1427            #else
   1428             return kAudioUnitErr_PropertyNotInUse;
   1429            #endif
   1430 
   1431         case kAudioUnitProperty_HostCallbacks:
   1432             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1433             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1434            #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1435             {
   1436                 const UInt32 usableDataSize = std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo)));
   1437 		        const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0;
   1438 
   1439 		        std::memcpy(&fHostCallbackInfo, inData, usableDataSize);
   1440 
   1441                 if (sizeof(HostCallbackInfo) > usableDataSize)
   1442 		            std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize);
   1443 
   1444                 if (changed)
   1445                     notifyPropertyListeners(inProp, inScope, inElement);
   1446             }
   1447             return noErr;
   1448            #else
   1449             return kAudioUnitErr_PropertyNotInUse;
   1450            #endif
   1451 
   1452         case kAudioUnitProperty_PresentPreset:
   1453             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1454             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1455             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1456             {
   1457                 const int32_t presetNumber = static_cast<const AUPreset*>(inData)->presetNumber;
   1458 
   1459                #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1460                 if (presetNumber >= 0)
   1461                 {
   1462                     if (fCurrentProgram != presetNumber)
   1463                     {
   1464                         fCurrentProgram = presetNumber;
   1465                         fLastFactoryProgram = presetNumber;
   1466                         fPlugin.loadProgram(fLastFactoryProgram);
   1467                         notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
   1468                     }
   1469                 }
   1470                 else
   1471                 {
   1472                     fCurrentProgram = presetNumber;
   1473                     CFRelease(fUserPresetData.presetName);
   1474                     std::memcpy(&fUserPresetData, inData, sizeof(AUPreset));
   1475                 }
   1476                #else
   1477                 DISTRHO_SAFE_ASSERT_INT_RETURN(presetNumber < 0, presetNumber, kAudioUnitErr_InvalidPropertyValue);
   1478                 CFRelease(fUserPresetData.presetName);
   1479                 std::memcpy(&fUserPresetData, inData, sizeof(AUPreset));
   1480                #endif
   1481             }
   1482             return noErr;
   1483 
   1484         case kAudioUnitProperty_MIDIOutputCallback:
   1485             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1486             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1487             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUMIDIOutputCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1488            #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1489             std::memcpy(&fMidiOutput, inData, sizeof(AUMIDIOutputCallbackStruct));
   1490             return noErr;
   1491            #else
   1492             return kAudioUnitErr_PropertyNotInUse;
   1493            #endif
   1494 
   1495         case 'DPFi':
   1496             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1497             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1498             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint16_t), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1499             {
   1500                 const uint16_t magic = *static_cast<const uint16_t*>(inData);
   1501 
   1502                 if (magic != 1337)
   1503                     return noErr;
   1504 
   1505                #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1506                 notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
   1507                #endif
   1508 
   1509                #if DISTRHO_PLUGIN_WANT_STATE
   1510                 for (uint32_t i=0; i<fStateCount; ++i)
   1511                     notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i);
   1512                #endif
   1513 
   1514                 for (uint32_t i=0; i<fParameterCount; ++i)
   1515                     notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i);
   1516             }
   1517             return noErr;
   1518 
   1519         case 'DPFe':
   1520             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1521             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
   1522             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1523             {
   1524                 const uint8_t flag = *static_cast<const uint8_t*>(inData);
   1525 
   1526                 if (flag != 1 && flag != 2)
   1527                     return noErr;
   1528 
   1529                 AudioUnitEvent event;
   1530                 std::memset(&event, 0, sizeof(event));
   1531 
   1532                 event.mEventType                        = flag == 1 ? kAudioUnitEvent_BeginParameterChangeGesture
   1533                                                                     : kAudioUnitEvent_EndParameterChangeGesture;
   1534                 event.mArgument.mParameter.mAudioUnit   = fComponent;
   1535                 event.mArgument.mParameter.mParameterID = inElement;
   1536                 event.mArgument.mParameter.mScope       = kAudioUnitScope_Global;
   1537                 AUEventListenerNotify(NULL, NULL, &event);
   1538             }
   1539             return noErr;
   1540 
   1541         case 'DPFp':
   1542             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1543             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement);
   1544             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(float), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1545             {
   1546                 const float value = *static_cast<const float*>(inData);
   1547                 DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue);
   1548 
   1549                 if (d_isEqual(fLastParameterValues[inElement], value))
   1550                     return noErr;
   1551 
   1552                 fLastParameterValues[inElement] = value;
   1553                 fPlugin.setParameterValue(inElement, value);
   1554 
   1555                 AudioUnitEvent event;
   1556                 std::memset(&event, 0, sizeof(event));
   1557 
   1558                 event.mEventType                        = kAudioUnitEvent_ParameterValueChange;
   1559                 event.mArgument.mParameter.mAudioUnit   = fComponent;
   1560                 event.mArgument.mParameter.mParameterID = inElement;
   1561                 event.mArgument.mParameter.mScope       = kAudioUnitScope_Global;
   1562                 AUEventListenerNotify(NULL, NULL, &event);
   1563 
   1564                 if (fBypassParameterIndex == inElement)
   1565 	                notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
   1566             }
   1567             return noErr;
   1568 
   1569         case 'DPFn':
   1570             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1571             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
   1572             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t) * 3, inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1573            #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1574             {
   1575                 const uint8_t* const midiData = static_cast<const uint8_t*>(inData);
   1576 
   1577                 if (midiData[0] == 0)
   1578                     return noErr;
   1579 
   1580                 fNotesRingBuffer.writeCustomData(midiData, 3);
   1581                 fNotesRingBuffer.commitWrite();
   1582             }
   1583             return noErr;
   1584            #else
   1585             return kAudioUnitErr_PropertyNotInUse;
   1586            #endif
   1587 
   1588         case 'DPFs':
   1589             DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
   1590             DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
   1591            #if DISTRHO_PLUGIN_WANT_STATE
   1592             DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement);
   1593             {
   1594                 const CFStringRef valueRef = *static_cast<const CFStringRef*>(inData);
   1595                 DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(),
   1596                                            kAudioUnitErr_InvalidPropertyValue);
   1597 
   1598                 const CFIndex valueLen = CFStringGetLength(valueRef);
   1599                 char* const value = static_cast<char*>(std::malloc(valueLen + 1));
   1600                 DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError);
   1601                 DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8),
   1602                                            kAudioUnitErr_InvalidPropertyValue);
   1603 
   1604                 const String& key(fPlugin.getStateKey(inElement));
   1605 
   1606                 // save this key as needed
   1607                 if (fPlugin.wantStateKey(key))
   1608                     fStateMap[key] = value;
   1609 
   1610                 fPlugin.setState(key, value);
   1611 
   1612                 std::free(value);
   1613             }
   1614             return noErr;
   1615            #else
   1616             return kAudioUnitErr_PropertyNotInUse;
   1617            #endif
   1618 
   1619         // unwanted properties
   1620         case kMusicDeviceProperty_DualSchedulingMode:
   1621             return kAudioUnitErr_PropertyNotInUse;
   1622         }
   1623 
   1624         d_stdout("TODO SetProperty(%d:%x:%s, %d:%s, %d, %p, %u)",
   1625                  inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
   1626         return kAudioUnitErr_InvalidProperty;
   1627     }
   1628 
   1629     OSStatus auAddPropertyListener(const AudioUnitPropertyID prop,
   1630                                    const AudioUnitPropertyListenerProc proc,
   1631                                    void* const userData)
   1632     {
   1633         const PropertyListener pl = {
   1634             prop, proc, userData
   1635         };
   1636 
   1637         fPropertyListeners.push_back(pl);
   1638         return noErr;
   1639     }
   1640 
   1641     OSStatus auRemovePropertyListener(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc)
   1642     {
   1643         for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
   1644         {
   1645             const PropertyListener& pl(*it);
   1646 
   1647             if (pl.prop == prop && pl.proc == proc)
   1648             {
   1649                 fPropertyListeners.erase(it);
   1650                 return auRemovePropertyListener(prop, proc);
   1651             }
   1652         }
   1653 
   1654         return noErr;
   1655     }
   1656 
   1657     OSStatus auRemovePropertyListenerWithUserData(const AudioUnitPropertyID prop,
   1658                                                   const AudioUnitPropertyListenerProc proc,
   1659                                                   void* const userData)
   1660     {
   1661         for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
   1662         {
   1663             const PropertyListener& pl(*it);
   1664 
   1665             if (pl.prop == prop && pl.proc == proc && pl.userData == userData)
   1666             {
   1667                 fPropertyListeners.erase(it);
   1668                 return auRemovePropertyListenerWithUserData(prop, proc, userData);
   1669             }
   1670         }
   1671 
   1672         return noErr;
   1673     }
   1674 
   1675     OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData)
   1676     {
   1677         fUsingRenderListeners = true;
   1678 
   1679         const RenderListener rl = {
   1680             proc, userData
   1681         };
   1682 
   1683         fRenderListeners.push_back(rl);
   1684         return noErr;
   1685     }
   1686 
   1687     OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData)
   1688     {
   1689         for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it)
   1690         {
   1691             const RenderListener& rl(*it);
   1692 
   1693             if (rl.proc == proc && rl.userData == userData)
   1694             {
   1695                 fRenderListeners.erase(it);
   1696                 return auRemoveRenderNotify(proc, userData);
   1697             }
   1698         }
   1699 
   1700         if (fRenderListeners.empty())
   1701             fUsingRenderListeners = false;
   1702 
   1703         return noErr;
   1704     }
   1705 
   1706     OSStatus auGetParameter(const AudioUnitParameterID param,
   1707                             const AudioUnitScope scope,
   1708                             const AudioUnitElement elem,
   1709                             AudioUnitParameterValue* const value)
   1710     {
   1711         DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope);
   1712         DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement);
   1713         DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
   1714         DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError);
   1715 
   1716         *value = fPlugin.getParameterValue(param);
   1717         return noErr;
   1718     }
   1719 
   1720     OSStatus auSetParameter(const AudioUnitParameterID param,
   1721                             const AudioUnitScope scope,
   1722                             const AudioUnitElement elem,
   1723                             const AudioUnitParameterValue value,
   1724                             UInt32 /* bufferOffset */)
   1725     {
   1726         DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope);
   1727         DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement);
   1728         DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
   1729         DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue);
   1730 
   1731         if (d_isNotEqual(fLastParameterValues[param], value))
   1732         {
   1733             fLastParameterValues[param] = value;
   1734             fPlugin.setParameterValue(param, value);
   1735 
   1736             // TODO flag param only, notify listeners later on bg thread (sem_post etc)
   1737             notifyPropertyListeners('DPFp', kAudioUnitScope_Global, param);
   1738 
   1739             if (fBypassParameterIndex == elem)
   1740                 notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
   1741         }
   1742 
   1743         return noErr;
   1744     }
   1745 
   1746     OSStatus auScheduleParameters(const AudioUnitParameterEvent* const events, const UInt32 numEvents)
   1747     {
   1748         for (UInt32 i=0; i<numEvents; ++i)
   1749         {
   1750             const AudioUnitParameterEvent& event(events[i]);
   1751 
   1752             if (event.eventType == kParameterEvent_Immediate)
   1753             {
   1754                 auSetParameter(event.parameter,
   1755                                event.scope,
   1756                                event.element,
   1757                                event.eventValues.immediate.value,
   1758                                event.eventValues.immediate.bufferOffset);
   1759             }
   1760             else
   1761             {
   1762                 // TODO?
   1763                 d_stdout("WIP ScheduleParameters(%p, %u)", events, numEvents);
   1764             }
   1765         }
   1766         return noErr;
   1767     }
   1768 
   1769     OSStatus auReset(const AudioUnitScope scope, const AudioUnitElement elem)
   1770     {
   1771         DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope);
   1772         DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
   1773 
   1774         if (fPlugin.isActive())
   1775         {
   1776             fPlugin.deactivate();
   1777             fPlugin.activate();
   1778         }
   1779 
   1780        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1781         fMidiEventCount = 0;
   1782        #endif
   1783        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1784         fMidiOutputPackets.numPackets = 0;
   1785        #endif
   1786        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1787         fTimePosition.clear();
   1788         fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat;
   1789        #endif
   1790         return noErr;
   1791     }
   1792 
   1793     OSStatus auRender(const AudioUnitRenderActionFlags actionFlags,
   1794                       const AudioTimeStamp* const inTimeStamp,
   1795                       const UInt32 inBusNumber,
   1796                       const UInt32 inFramesToProcess,
   1797                       AudioBufferList* const ioData)
   1798     {
   1799         if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0)
   1800         {
   1801             DISTRHO_SAFE_ASSERT_RETURN(fPlugin.isActive(), kAudio_ParamError);
   1802             DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement);
   1803            #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1804             DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == fAudioBufferList->mNumberBuffers,
   1805                                             ioData->mNumberBuffers, kAudio_ParamError);
   1806            #else
   1807             DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == 0, ioData->mNumberBuffers, kAudio_ParamError);
   1808            #endif
   1809 
   1810             if (inFramesToProcess > fPlugin.getBufferSize())
   1811             {
   1812                 setLastRenderError(kAudioUnitErr_TooManyFramesToProcess);
   1813                 return kAudioUnitErr_TooManyFramesToProcess;
   1814             }
   1815 
   1816            #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1817             for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i)
   1818             {
   1819                 if (ioData->mBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess)
   1820                 {
   1821                     setLastRenderError(kAudio_ParamError);
   1822                     return kAudio_ParamError;
   1823                 }
   1824             }
   1825            #endif
   1826         }
   1827 
   1828       #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1829        #ifdef DISTRHO_PLUGIN_EXTRA_IO
   1830         const uint32_t numInputs = fNumInputs;
   1831        #else
   1832         constexpr const uint32_t numInputs = DISTRHO_PLUGIN_NUM_INPUTS;
   1833        #endif
   1834         const float* inputs[numInputs];
   1835       #else
   1836         constexpr const float** inputs = nullptr;
   1837       #endif
   1838 
   1839       #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1840        #ifdef DISTRHO_PLUGIN_EXTRA_IO
   1841         const uint32_t numOutputs = fNumOutputs;
   1842        #else
   1843         constexpr const uint32_t numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS;
   1844        #endif
   1845         float* outputs[numOutputs];
   1846       #else
   1847         constexpr float** outputs = nullptr;
   1848       #endif
   1849 
   1850        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1851         if (fInputConnectionUnit != nullptr)
   1852         {
   1853             AudioUnitRenderActionFlags ioActionFlags = 0;
   1854             const OSStatus err = AudioUnitRender(fInputConnectionUnit,
   1855                                                  &ioActionFlags,
   1856                                                  inTimeStamp,
   1857                                                  fInputConnectionBus,
   1858                                                  inFramesToProcess,
   1859                                                  fAudioBufferList);
   1860 
   1861             if (err != noErr)
   1862             {
   1863                 setLastRenderError(err);
   1864                 return err;
   1865             }
   1866 
   1867             for (uint16_t i = 0; i < numInputs; ++i)
   1868                 inputs[i] = static_cast<const float*>(fAudioBufferList->mBuffers[i].mData);
   1869 
   1870            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1871             for (uint16_t i = 0; i < numOutputs; ++i)
   1872             {
   1873                 if (ioData->mBuffers[i].mData == nullptr)
   1874                     ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData;
   1875 
   1876                 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
   1877             }
   1878            #endif
   1879         }
   1880         else if (fInputRenderCallback.inputProc != nullptr)
   1881         {
   1882             bool adjustDataByteSize, usingHostBuffer = true;
   1883             UInt32 prevDataByteSize;
   1884 
   1885             for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i)
   1886             {
   1887                 if (ioData->mBuffers[i].mData == nullptr)
   1888                 {
   1889                     usingHostBuffer = false;
   1890                     ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData;
   1891                 }
   1892             }
   1893 
   1894             if (! usingHostBuffer)
   1895             {
   1896                 prevDataByteSize = fAudioBufferList->mBuffers[0].mDataByteSize;
   1897                 adjustDataByteSize = prevDataByteSize != sizeof(float) * inFramesToProcess;
   1898 
   1899                 if (adjustDataByteSize)
   1900                 {
   1901                     for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i)
   1902                         fAudioBufferList->mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess;
   1903                 }
   1904             }
   1905             else
   1906             {
   1907                 adjustDataByteSize = false;
   1908             }
   1909 
   1910             AudioUnitRenderActionFlags rActionFlags = 0;
   1911             AudioBufferList* const rData = usingHostBuffer ? ioData : fAudioBufferList;
   1912             const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon,
   1913                                                                 &rActionFlags,
   1914                                                                 inTimeStamp,
   1915                                                                 inBusNumber,
   1916                                                                 inFramesToProcess,
   1917                                                                 rData);
   1918 
   1919             if (err != noErr)
   1920             {
   1921                 if (adjustDataByteSize)
   1922                 {
   1923                     for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i)
   1924                         fAudioBufferList->mBuffers[i].mDataByteSize = prevDataByteSize;
   1925                 }
   1926 
   1927                 setLastRenderError(err);
   1928                 return err;
   1929             }
   1930 
   1931             if (usingHostBuffer)
   1932             {
   1933                 for (uint16_t i = 0; i < numInputs; ++i)
   1934                     inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData);
   1935 
   1936                #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1937                 for (uint16_t i = 0; i < numOutputs; ++i)
   1938                     outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
   1939                #endif
   1940 
   1941             }
   1942             else
   1943             {
   1944                 for (uint16_t i = 0; i < numInputs; ++i)
   1945                     inputs[i] = static_cast<const float*>(fAudioBufferList->mBuffers[i].mData);
   1946 
   1947                #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1948                 for (uint16_t i = 0; i < numOutputs; ++i)
   1949                     outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
   1950                #endif
   1951             }
   1952         }
   1953         else
   1954        #endif // DISTRHO_PLUGIN_NUM_INPUTS != 0
   1955         {
   1956            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1957             for (uint16_t i = 0; i < numInputs; ++i)
   1958             {
   1959                 if (ioData->mBuffers[i].mData == nullptr)
   1960                 {
   1961                     ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData;
   1962                     std::memset(ioData->mBuffers[i].mData, 0, sizeof(float) * inFramesToProcess);
   1963                 }
   1964 
   1965                 inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData);
   1966             }
   1967            #endif
   1968 
   1969            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1970             for (uint16_t i = 0; i < numOutputs; ++i)
   1971             {
   1972                 if (ioData->mBuffers[i].mData == nullptr)
   1973                     ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData;
   1974 
   1975                 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
   1976             }
   1977            #endif
   1978         }
   1979 
   1980         if (fUsingRenderListeners)
   1981         {
   1982             AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PreRender;
   1983             notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData);
   1984         }
   1985 
   1986         run(inputs, outputs, inFramesToProcess, inTimeStamp);
   1987 
   1988         if (fUsingRenderListeners)
   1989         {
   1990             AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PostRender;
   1991             notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData);
   1992         }
   1993 
   1994         return noErr;
   1995     }
   1996 
   1997    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1998     OSStatus auMIDIEvent(const UInt32 inStatus,
   1999                          const UInt32 inData1,
   2000                          const UInt32 inData2,
   2001                          const UInt32 inOffsetSampleFrame)
   2002     {
   2003         if (fMidiEventCount >= kMaxMidiEvents)
   2004             return noErr;
   2005 
   2006         MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   2007         midiEvent.frame   = inOffsetSampleFrame;
   2008         midiEvent.data[0] = inStatus;
   2009         midiEvent.data[1] = inData1;
   2010         midiEvent.data[2] = inData2;
   2011 
   2012         // TODO
   2013         switch (inStatus)
   2014         {
   2015         case 0x80:
   2016         case 0x90:
   2017         case 0xA0:
   2018         case 0xB0:
   2019         case 0xD0:
   2020         case 0xE0:
   2021             midiEvent.size = 3;
   2022             break;
   2023         case 0xC0:
   2024             midiEvent.size = 2;
   2025             break;
   2026         default:
   2027             midiEvent.size = 1;
   2028             break;
   2029         }
   2030 
   2031        #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
   2032         // handle case of plugin having no working audio, simulate audio-side processing
   2033         run(nullptr, nullptr, std::max(1u, inOffsetSampleFrame), nullptr);
   2034        #endif
   2035 
   2036         return noErr;
   2037     }
   2038 
   2039     OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength)
   2040     {
   2041         if (fMidiEventCount >= kMaxMidiEvents)
   2042             return noErr;
   2043 
   2044         MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   2045         midiEvent.frame = fMidiEventCount != 1 ? fMidiEvents[fMidiEventCount - 1].frame : 0;
   2046         midiEvent.size  = inLength;
   2047 
   2048         // FIXME who owns inData ??
   2049         if (inLength > MidiEvent::kDataSize)
   2050         {
   2051             std::memset(midiEvent.data, 0, MidiEvent::kDataSize);
   2052             midiEvent.dataExt = inData;
   2053         }
   2054         else
   2055         {
   2056             std::memcpy(midiEvent.data, inData, inLength);
   2057         }
   2058 
   2059        #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
   2060         // handle case of plugin having no working audio, simulate audio-side processing
   2061         run(nullptr, nullptr, 1, nullptr);
   2062        #endif
   2063 
   2064         return noErr;
   2065     }
   2066    #endif
   2067 
   2068     // ----------------------------------------------------------------------------------------------------------------
   2069 
   2070 private:
   2071     PluginExporter fPlugin;
   2072 
   2073     // AU component
   2074     const AudioUnit fComponent;
   2075 
   2076     // AUv2 related fields
   2077     OSStatus fLastRenderError;
   2078     PropertyListeners fPropertyListeners;
   2079     RenderListeners fRenderListeners;
   2080   #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   2081     UInt32 fInputConnectionBus;
   2082     AudioUnit fInputConnectionUnit;
   2083     AURenderCallbackStruct fInputRenderCallback;
   2084     Float64 fSampleRateForInput;
   2085    #ifdef DISTRHO_PLUGIN_EXTRA_IO
   2086     uint32_t fNumInputs;
   2087    #endif
   2088   #endif
   2089   #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2090     Float64 fSampleRateForOutput;
   2091    #ifdef DISTRHO_PLUGIN_EXTRA_IO
   2092     uint32_t fNumOutputs;
   2093    #endif
   2094   #endif
   2095    #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2096     AudioBufferList* fAudioBufferList;
   2097    #endif
   2098     bool fUsingRenderListeners;
   2099 
   2100     // Caching
   2101     const uint32_t fParameterCount;
   2102     float* fLastParameterValues;
   2103     uint32_t fBypassParameterIndex;
   2104 
   2105    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2106     uint32_t fMidiEventCount;
   2107     MidiEvent fMidiEvents[kMaxMidiEvents];
   2108     SmallStackRingBuffer fNotesRingBuffer;
   2109    #endif
   2110 
   2111    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2112     AUMIDIOutputCallbackStruct fMidiOutput;
   2113     d_MIDIPacketList fMidiOutputPackets;
   2114    #endif
   2115 
   2116    #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2117     int32_t fCurrentProgram;
   2118     uint32_t fLastFactoryProgram;
   2119     uint32_t fProgramCount;
   2120     AUPreset* fFactoryPresetsData;
   2121    #endif
   2122     AUPreset fUserPresetData;
   2123 
   2124    #if DISTRHO_PLUGIN_WANT_STATE
   2125     const uint32_t fStateCount;
   2126     StringMap fStateMap;
   2127    #endif
   2128 
   2129    #if DISTRHO_PLUGIN_WANT_TIMEPOS
   2130     HostCallbackInfo fHostCallbackInfo;
   2131     TimePosition fTimePosition;
   2132    #endif
   2133 
   2134     // ----------------------------------------------------------------------------------------------------------------
   2135 
   2136     void notifyPropertyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem)
   2137     {
   2138         for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
   2139         {
   2140             const PropertyListener& pl(*it);
   2141 
   2142             if (pl.prop == prop)
   2143 			    pl.proc(pl.userData, fComponent, prop, scope, elem);
   2144         }
   2145     }
   2146 
   2147     void notifyRenderListeners(AudioUnitRenderActionFlags* const ioActionFlags,
   2148                                const AudioTimeStamp* const inTimeStamp,
   2149                                const UInt32 inBusNumber,
   2150                                const UInt32 inNumberFrames,
   2151                                AudioBufferList* const ioData)
   2152     {
   2153         for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it)
   2154         {
   2155             const RenderListener& rl(*it);
   2156 
   2157             rl.proc(rl.userData, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
   2158         }
   2159     }
   2160 
   2161     // ----------------------------------------------------------------------------------------------------------------
   2162 
   2163     void run(const float** inputs, float** outputs, const uint32_t frames, const AudioTimeStamp* const inTimeStamp)
   2164     {
   2165        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2166         if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
   2167         {
   2168             uint8_t midiData[3];
   2169             const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0;
   2170 
   2171             while (fNotesRingBuffer.isDataAvailableForReading())
   2172             {
   2173                 if (! fNotesRingBuffer.readCustomData(midiData, 3))
   2174                     break;
   2175 
   2176                 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   2177                 midiEvent.frame = frame;
   2178                 midiEvent.size  = 3;
   2179                 std::memcpy(midiEvent.data, midiData, 3);
   2180 
   2181                 if (fMidiEventCount == kMaxMidiEvents)
   2182                     break;
   2183             }
   2184         }
   2185        #endif
   2186 
   2187        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2188         fMidiOutputPackets.numPackets = 0;
   2189        #endif
   2190 
   2191        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   2192         if (fHostCallbackInfo.beatAndTempoProc != nullptr ||
   2193             fHostCallbackInfo.musicalTimeLocationProc != nullptr ||
   2194             fHostCallbackInfo.transportStateProc != nullptr)
   2195         {
   2196             // reused values so we can check null and return value together
   2197             Boolean b1 = false;
   2198             Float32 f1 = 4.f; // initial value for beats per bar
   2199             Float64 g1 = 0.0;
   2200             Float64 g2 = 0.0;
   2201             UInt32 u1 = 0;
   2202 
   2203             if (fHostCallbackInfo.musicalTimeLocationProc != nullptr
   2204                 && fHostCallbackInfo.musicalTimeLocationProc(fHostCallbackInfo.hostUserData,
   2205                                                              nullptr, &f1, &u1, nullptr) == noErr)
   2206             {
   2207                 fTimePosition.bbt.beatsPerBar = f1;
   2208                 fTimePosition.bbt.beatType    = u1;
   2209             }
   2210             else
   2211             {
   2212                 fTimePosition.bbt.beatsPerBar = 4.f;
   2213                 fTimePosition.bbt.beatType    = 4.f;
   2214             }
   2215 
   2216             if (fHostCallbackInfo.beatAndTempoProc != nullptr
   2217                 && fHostCallbackInfo.beatAndTempoProc(fHostCallbackInfo.hostUserData, &g1, &g2) == noErr)
   2218             {
   2219                 const double beat = static_cast<int32_t>(g1);
   2220 
   2221                 fTimePosition.bbt.valid = true;
   2222                 fTimePosition.bbt.bar   = static_cast<int32_t>(beat / f1) + 1;
   2223                 fTimePosition.bbt.beat  = static_cast<int32_t>(std::fmod(beat, f1)) + 1;
   2224                 fTimePosition.bbt.tick  = std::fmod(g1, 1.0) * 1920.0;
   2225                 fTimePosition.bbt.beatsPerMinute = g2;
   2226             }
   2227             else
   2228             {
   2229                 fTimePosition.bbt.valid = false;
   2230                 fTimePosition.bbt.bar   = 1;
   2231                 fTimePosition.bbt.beat  = 1;
   2232                 fTimePosition.bbt.tick  = 0.0;
   2233                 fTimePosition.bbt.beatsPerMinute = 120.0;
   2234             }
   2235 
   2236             if (fHostCallbackInfo.transportStateProc != nullptr
   2237                 && fHostCallbackInfo.transportStateProc(fHostCallbackInfo.hostUserData,
   2238                                                         &b1, nullptr, &g1, nullptr, nullptr, nullptr) == noErr)
   2239             {
   2240                 fTimePosition.playing = b1;
   2241                 fTimePosition.frame = static_cast<int64_t>(g1);
   2242             }
   2243             else
   2244             {
   2245                 fTimePosition.playing = false;
   2246                 fTimePosition.frame = 0;
   2247             }
   2248 
   2249             fTimePosition.bbt.barStartTick = kDefaultTicksPerBeat *
   2250                                              fTimePosition.bbt.beatsPerBar *
   2251                                              (fTimePosition.bbt.bar - 1);
   2252 
   2253             fPlugin.setTimePosition(fTimePosition);
   2254         }
   2255        #endif
   2256 
   2257        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2258         fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount);
   2259         fMidiEventCount = 0;
   2260        #else
   2261         fPlugin.run(inputs, outputs, frames);
   2262        #endif
   2263 
   2264        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2265         if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr)
   2266         {
   2267             fMidiOutput.midiOutputCallback(fMidiOutput.userData,
   2268                                            inTimeStamp,
   2269                                            0,
   2270                                            reinterpret_cast<const ::MIDIPacketList*>(&fMidiOutputPackets));
   2271         }
   2272        #else
   2273         // unused
   2274         (void)inTimeStamp;
   2275        #endif
   2276 
   2277         float value;
   2278         AudioUnitEvent event;
   2279         std::memset(&event, 0, sizeof(event));
   2280         event.mEventType                      = kAudioUnitEvent_ParameterValueChange;
   2281         event.mArgument.mParameter.mAudioUnit = fComponent;
   2282         event.mArgument.mParameter.mScope     = kAudioUnitScope_Global;
   2283 
   2284         for (uint32_t i=0; i<fParameterCount; ++i)
   2285         {
   2286             if (fPlugin.isParameterOutputOrTrigger(i))
   2287             {
   2288                 value = fPlugin.getParameterValue(i);
   2289 
   2290                 if (d_isEqual(fLastParameterValues[i], value))
   2291                     continue;
   2292 
   2293                 fLastParameterValues[i] = value;
   2294 
   2295                 // TODO flag param only, notify listeners later on bg thread (sem_post etc)
   2296                 event.mArgument.mParameter.mParameterID = i;
   2297                 AUEventListenerNotify(NULL, NULL, &event);
   2298                 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i);
   2299             }
   2300         }
   2301     }
   2302 
   2303     void setLastRenderError(const OSStatus err)
   2304     {
   2305         if (fLastRenderError != noErr)
   2306             return;
   2307 
   2308         fLastRenderError = err;
   2309         notifyPropertyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
   2310     }
   2311 
   2312     // ----------------------------------------------------------------------------------------------------------------
   2313 
   2314    #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2315     bool reallocAudioBufferList(const bool alloc)
   2316     {
   2317         if (fAudioBufferList != nullptr)
   2318         {
   2319             for (uint16_t i = 0; i < fAudioBufferList->mNumberBuffers; ++i)
   2320                 delete[] static_cast<float*>(fAudioBufferList->mBuffers[i].mData);
   2321         }
   2322 
   2323       #ifdef DISTRHO_PLUGIN_EXTRA_IO
   2324        #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2325         const uint16_t numBuffers = std::max(fNumInputs, fNumOutputs);
   2326        #elif DISTRHO_PLUGIN_NUM_INPUTS != 0
   2327         const uint16_t numBuffers = fNumInputs;
   2328        #else
   2329         const uint16_t numBuffers = fNumOutputs;
   2330        #endif
   2331       #else
   2332         constexpr const uint16_t numBuffers = d_max(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS);
   2333       #endif
   2334         const uint32_t bufferSize = fPlugin.getBufferSize();
   2335 
   2336         if (! alloc)
   2337         {
   2338             std::free(fAudioBufferList);
   2339             fAudioBufferList = nullptr;
   2340             return true;
   2341         }
   2342 
   2343         if (AudioBufferList* const abl = static_cast<AudioBufferList*>(
   2344             std::realloc(fAudioBufferList, sizeof(uint32_t) + sizeof(AudioBuffer) * numBuffers)))
   2345         {
   2346             abl->mNumberBuffers = numBuffers;
   2347 
   2348             for (uint16_t i = 0; i < numBuffers; ++i)
   2349             {
   2350                 abl->mBuffers[i].mNumberChannels = 1;
   2351                 abl->mBuffers[i].mData = new float[bufferSize];
   2352                 abl->mBuffers[i].mDataByteSize = sizeof(float) * bufferSize;
   2353             }
   2354 
   2355             fAudioBufferList = abl;
   2356             return true;
   2357         }
   2358 
   2359         std::free(fAudioBufferList);
   2360         fAudioBufferList = nullptr;
   2361         return false;
   2362     }
   2363    #endif
   2364 
   2365     // ----------------------------------------------------------------------------------------------------------------
   2366 
   2367     CFMutableDictionaryRef retrieveClassInfo()
   2368     {
   2369         CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr,
   2370                                                                    0,
   2371                                                                    &kCFTypeDictionaryKeyCallBacks,
   2372                                                                    &kCFTypeDictionaryValueCallBacks);
   2373         SInt32 value;
   2374 
   2375         value = 0;
   2376         if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
   2377         {
   2378             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num);
   2379             CFRelease(num);
   2380         }
   2381 
   2382         value = kType;
   2383         if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
   2384         {
   2385             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num);
   2386             CFRelease(num);
   2387         }
   2388 
   2389         value = kSubType;
   2390         if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
   2391         {
   2392             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num);
   2393             CFRelease(num);
   2394         }
   2395 
   2396         value = kManufacturer;
   2397         if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value))
   2398         {
   2399             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num);
   2400             CFRelease(num);
   2401         }
   2402 
   2403        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2404         if (fCurrentProgram >= 0)
   2405         {
   2406             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fFactoryPresetsData[fCurrentProgram].presetName);
   2407         }
   2408         else
   2409        #endif
   2410         {
   2411             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fUserPresetData.presetName);
   2412         }
   2413 
   2414         if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr,
   2415                                                                           0,
   2416                                                                           &kCFTypeDictionaryKeyCallBacks,
   2417                                                                           &kCFTypeDictionaryValueCallBacks))
   2418         {
   2419            #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2420             const SInt32 program = fCurrentProgram;
   2421             if (const CFNumberRef programRef = CFNumberCreate(nullptr, kCFNumberSInt32Type, &program))
   2422             {
   2423                CFDictionarySetValue(data, CFSTR("program"), programRef);
   2424                CFRelease(programRef);
   2425             }
   2426            #endif
   2427 
   2428            #if DISTRHO_PLUGIN_WANT_FULL_STATE
   2429             // Update current state
   2430             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   2431             {
   2432                 const String& key(cit->first);
   2433                 fStateMap[key] = fPlugin.getStateValue(key);
   2434             }
   2435            #endif
   2436 
   2437            #if DISTRHO_PLUGIN_WANT_STATE
   2438             if (const CFMutableArrayRef statesRef = CFArrayCreateMutable(nullptr,
   2439                                                                          fStateCount,
   2440                                                                          &kCFTypeArrayCallBacks))
   2441             {
   2442                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   2443                 {
   2444                     const String& key(cit->first);
   2445                     const String& value(cit->second);
   2446 
   2447                    #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && ! DISTRHO_PLUGIN_HAS_UI
   2448                     bool wantStateKey = true;
   2449 
   2450                     for (uint32_t i=0; i<fStateCount; ++i)
   2451                     {
   2452                         if (fPlugin.getStateKey(i) == key)
   2453                         {
   2454                             if (fPlugin.getStateHints(i) & kStateIsOnlyForUI)
   2455                                 wantStateKey = false;
   2456 
   2457                             break;
   2458                         }
   2459                     }
   2460 
   2461                     if (! wantStateKey)
   2462                         continue;
   2463                    #endif
   2464 
   2465                     CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII);
   2466                     CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8);
   2467 
   2468                     if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
   2469                                                                      reinterpret_cast<const void**>(&keyRef),
   2470                                                                      reinterpret_cast<const void**>(&valueRef),
   2471                                                                      1,
   2472                                                                      &kCFTypeDictionaryKeyCallBacks,
   2473                                                                      &kCFTypeDictionaryValueCallBacks))
   2474                     {
   2475                         CFArrayAppendValue(statesRef, dictRef);
   2476                         CFRelease(dictRef);
   2477                     }
   2478 
   2479                     CFRelease(keyRef);
   2480                     CFRelease(valueRef);
   2481                 }
   2482 
   2483                 CFDictionarySetValue(data, CFSTR("states"), statesRef);
   2484                 CFRelease(statesRef);
   2485             }
   2486            #endif
   2487 
   2488             if (const CFMutableArrayRef paramsRef = CFArrayCreateMutable(nullptr,
   2489                                                                          fParameterCount,
   2490                                                                          &kCFTypeArrayCallBacks))
   2491             {
   2492                 for (uint32_t i=0; i<fParameterCount; ++i)
   2493                 {
   2494                     if (fPlugin.isParameterOutputOrTrigger(i))
   2495                         continue;
   2496 
   2497                     const float value = fPlugin.getParameterValue(i);
   2498 
   2499                     CFStringRef keyRef = CFStringCreateWithCString(nullptr,
   2500                                                                    fPlugin.getParameterSymbol(i),
   2501                                                                    kCFStringEncodingASCII);
   2502                     CFNumberRef valueRef = CFNumberCreate(nullptr, kCFNumberFloat32Type, &value);
   2503 
   2504                     if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
   2505                                                                      reinterpret_cast<const void**>(&keyRef),
   2506                                                                      reinterpret_cast<const void**>(&valueRef),
   2507                                                                      1,
   2508                                                                      &kCFTypeDictionaryKeyCallBacks,
   2509                                                                      &kCFTypeDictionaryValueCallBacks))
   2510                     {
   2511                         CFArrayAppendValue(paramsRef, dictRef);
   2512                         CFRelease(dictRef);
   2513                     }
   2514 
   2515                     CFRelease(keyRef);
   2516                     CFRelease(valueRef);
   2517                 }
   2518 
   2519                 CFDictionarySetValue(data, CFSTR("params"), paramsRef);
   2520                 CFRelease(paramsRef);
   2521             }
   2522 
   2523             CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data);
   2524             CFRelease(data);
   2525         }
   2526 
   2527         return clsInfo;
   2528     }
   2529 
   2530     void restoreClassInfo(const CFDictionaryRef clsInfo)
   2531     {
   2532         CFDictionaryRef data = nullptr;
   2533         DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetValueIfPresent(clsInfo,
   2534                                                                  CFSTR(kAUPresetDataKey),
   2535                                                                  reinterpret_cast<const void**>(&data)),);
   2536         DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),);
   2537 
   2538        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2539         CFNumberRef programRef = nullptr;
   2540         if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast<const void**>(&programRef))
   2541             && CFGetTypeID(programRef) == CFNumberGetTypeID())
   2542         {
   2543             SInt32 program = -1;
   2544             if (CFNumberGetValue(programRef, kCFNumberSInt32Type, &program))
   2545             {
   2546                 fCurrentProgram = program;
   2547 
   2548                 if (program >= 0)
   2549                 {
   2550                     fLastFactoryProgram = program;
   2551                     fPlugin.loadProgram(fLastFactoryProgram);
   2552                     notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
   2553                 }
   2554 
   2555                 notifyPropertyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
   2556             }
   2557         }
   2558        #endif
   2559 
   2560        #if DISTRHO_PLUGIN_WANT_STATE
   2561         CFArrayRef statesRef = nullptr;
   2562         if (CFDictionaryGetValueIfPresent(data, CFSTR("states"), reinterpret_cast<const void**>(&statesRef))
   2563             && CFGetTypeID(statesRef) == CFArrayGetTypeID())
   2564         {
   2565             const CFIndex numStates = CFArrayGetCount(statesRef);
   2566             char* key = nullptr;
   2567             char* value = nullptr;
   2568             CFIndex keyLen = -1;
   2569             CFIndex valueLen = -1;
   2570 
   2571             for (CFIndex i=0; i<numStates; ++i)
   2572             {
   2573                 const CFDictionaryRef state = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(statesRef, i));
   2574                 DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(state) == CFDictionaryGetTypeID());
   2575                 DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(state) == 1);
   2576 
   2577                 CFStringRef keyRef = nullptr;
   2578                 CFStringRef valueRef = nullptr;
   2579                 CFDictionaryGetKeysAndValues(state,
   2580                                              reinterpret_cast<const void**>(&keyRef),
   2581                                              reinterpret_cast<const void**>(&valueRef));
   2582                 DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID());
   2583                 DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID());
   2584 
   2585                 const CFIndex keyRefLen = CFStringGetLength(keyRef);
   2586                 if (keyLen < keyRefLen)
   2587                 {
   2588                     keyLen = keyRefLen;
   2589                     key = static_cast<char*>(std::realloc(key, keyLen + 1));
   2590                 }
   2591                 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII));
   2592 
   2593                 if (! fPlugin.wantStateKey(key))
   2594                     continue;
   2595 
   2596                 const CFIndex valueRefLen = CFStringGetLength(valueRef);
   2597                 if (valueLen < valueRefLen)
   2598                 {
   2599                     valueLen = valueRefLen;
   2600                     value = static_cast<char*>(std::realloc(value, valueLen + 1));
   2601                 }
   2602                 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8));
   2603 
   2604                 const String dkey(key);
   2605                 fStateMap[dkey] = value;
   2606                 fPlugin.setState(key, value);
   2607 
   2608                 for (uint32_t j=0; j<fStateCount; ++j)
   2609                 {
   2610                     if (fPlugin.getStateKey(j) == key)
   2611                     {
   2612                         if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
   2613                             notifyPropertyListeners('DPFs', kAudioUnitScope_Global, j);
   2614 
   2615                         break;
   2616                     }
   2617                 }
   2618             }
   2619 
   2620             std::free(key);
   2621             std::free(value);
   2622         }
   2623        #endif
   2624 
   2625         CFArrayRef paramsRef = nullptr;
   2626         if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast<const void**>(&paramsRef))
   2627             && CFGetTypeID(paramsRef) == CFArrayGetTypeID())
   2628         {
   2629             const CFIndex numParams = CFArrayGetCount(paramsRef);
   2630             char* symbol = nullptr;
   2631             CFIndex symbolLen = -1;
   2632 
   2633             for (CFIndex i=0; i<numParams; ++i)
   2634             {
   2635                 const CFDictionaryRef param = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(paramsRef, i));
   2636                 DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID());
   2637                 DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1);
   2638 
   2639                 CFStringRef keyRef = nullptr;
   2640                 CFNumberRef valueRef = nullptr;
   2641                 CFDictionaryGetKeysAndValues(param,
   2642                                              reinterpret_cast<const void**>(&keyRef),
   2643                                              reinterpret_cast<const void**>(&valueRef));
   2644                 DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID());
   2645                 DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFNumberGetTypeID());
   2646 
   2647                 float value = 0.f;
   2648                 DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value));
   2649 
   2650                 const CFIndex keyRefLen = CFStringGetLength(keyRef);
   2651                 if (symbolLen < keyRefLen)
   2652                 {
   2653                     symbolLen = keyRefLen;
   2654                     symbol = static_cast<char*>(std::realloc(symbol, symbolLen + 1));
   2655                 }
   2656                 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII));
   2657 
   2658                 for (uint32_t j=0; j<fParameterCount; ++j)
   2659                 {
   2660                     if (fPlugin.isParameterOutputOrTrigger(j))
   2661                         continue;
   2662                     if (fPlugin.getParameterSymbol(j) != symbol)
   2663                         continue;
   2664 
   2665                     fLastParameterValues[j] = value;
   2666                     fPlugin.setParameterValue(j, value);
   2667                     notifyPropertyListeners('DPFp', kAudioUnitScope_Global, j);
   2668 
   2669                     if (fBypassParameterIndex == j)
   2670                         notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
   2671 
   2672                     break;
   2673                 }
   2674             }
   2675 
   2676             std::free(symbol);
   2677         }
   2678     }
   2679 
   2680     // ----------------------------------------------------------------------------------------------------------------
   2681     // DPF callbacks
   2682 
   2683    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2684     bool writeMidi(const MidiEvent& midiEvent)
   2685     {
   2686         DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fMidiOutput.midiOutputCallback != nullptr, false);
   2687 
   2688         if (midiEvent.size > sizeof(MIDIPacket::data))
   2689             return true;
   2690         if (fMidiOutputPackets.numPackets == kMaxMidiEvents)
   2691             return false;
   2692 
   2693         const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
   2694         MIDIPacket& packet(fMidiOutputPackets.packets[fMidiOutputPackets.numPackets++]);
   2695         packet.timeStamp = midiEvent.frame;
   2696         packet.length = midiEvent.size;
   2697         std::memcpy(packet.data, midiData, midiEvent.size);
   2698         return true;
   2699     }
   2700 
   2701     static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
   2702     {
   2703         return static_cast<PluginAU*>(ptr)->writeMidi(midiEvent);
   2704     }
   2705    #endif
   2706 
   2707    #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
   2708     bool requestParameterValueChange(const uint32_t index, const float value)
   2709     {
   2710         AudioUnitEvent event;
   2711         std::memset(&event, 0, sizeof(event));
   2712         event.mEventType                        = kAudioUnitEvent_ParameterValueChange;
   2713         event.mArgument.mParameter.mAudioUnit   = fComponent;
   2714         event.mArgument.mParameter.mParameterID = index;
   2715         event.mArgument.mParameter.mScope       = kAudioUnitScope_Global;
   2716 
   2717         fLastParameterValues[index] = value;
   2718         AUEventListenerNotify(NULL, NULL, &event);
   2719         notifyPropertyListeners('DPFp', kAudioUnitScope_Global, index);
   2720         return true;
   2721     }
   2722 
   2723     static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
   2724     {
   2725         return static_cast<PluginAU*>(ptr)->requestParameterValueChange(index, value);
   2726     }
   2727    #endif
   2728 
   2729    #if DISTRHO_PLUGIN_WANT_STATE
   2730     bool updateState(const char* const key, const char* const newValue)
   2731     {
   2732         fPlugin.setState(key, newValue);
   2733 
   2734         for (uint32_t i=0; i<fStateCount; ++i)
   2735         {
   2736             if (fPlugin.getStateKey(i) == key)
   2737             {
   2738                 const String dkey(key);
   2739                 fStateMap[dkey] = newValue;
   2740 
   2741                 if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
   2742                     notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i);
   2743 
   2744                 return true;
   2745             }
   2746         }
   2747 
   2748         d_stderr("Failed to find plugin state with key \"%s\"", key);
   2749         return false;
   2750     }
   2751 
   2752     static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const newValue)
   2753     {
   2754         return static_cast<PluginAU*>(ptr)->updateState(key, newValue);
   2755     }
   2756    #endif
   2757 
   2758     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginAU)
   2759 };
   2760 
   2761 // --------------------------------------------------------------------------------------------------------------------
   2762 
   2763 struct AudioComponentPlugInInstance {
   2764 	AudioComponentPlugInInterface acpi;
   2765     PluginAU* plugin;
   2766 
   2767     AudioComponentPlugInInstance() noexcept
   2768         : acpi(),
   2769           plugin(nullptr)
   2770     {
   2771         std::memset(&acpi, 0, sizeof(acpi));
   2772         acpi.Open = Open;
   2773 		acpi.Close = Close;
   2774 		acpi.Lookup = Lookup;
   2775 		acpi.reserved = nullptr;
   2776     }
   2777 
   2778     ~AudioComponentPlugInInstance()
   2779     {
   2780         delete plugin;
   2781     }
   2782 
   2783 	static OSStatus Open(void* const self, const AudioUnit component)
   2784     {
   2785         static_cast<AudioComponentPlugInInstance*>(self)->plugin = new PluginAU(component);
   2786         return noErr;
   2787     }
   2788 
   2789 	static OSStatus Close(void* const self)
   2790     {
   2791         delete static_cast<AudioComponentPlugInInstance*>(self);
   2792         return noErr;
   2793     }
   2794 
   2795     static AudioComponentMethod Lookup(const SInt16 selector)
   2796     {
   2797         d_debug("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector));
   2798 
   2799         switch (selector)
   2800         {
   2801         case kAudioUnitInitializeSelect:
   2802             return reinterpret_cast<AudioComponentMethod>(Initialize);
   2803         case kAudioUnitUninitializeSelect:
   2804             return reinterpret_cast<AudioComponentMethod>(Uninitialize);
   2805         case kAudioUnitGetPropertyInfoSelect:
   2806             return reinterpret_cast<AudioComponentMethod>(GetPropertyInfo);
   2807         case kAudioUnitGetPropertySelect:
   2808             return reinterpret_cast<AudioComponentMethod>(GetProperty);
   2809         case kAudioUnitSetPropertySelect:
   2810             return reinterpret_cast<AudioComponentMethod>(SetProperty);
   2811         case kAudioUnitAddPropertyListenerSelect:
   2812             return reinterpret_cast<AudioComponentMethod>(AddPropertyListener);
   2813         case kAudioUnitRemovePropertyListenerSelect:
   2814             return reinterpret_cast<AudioComponentMethod>(RemovePropertyListener);
   2815         case kAudioUnitRemovePropertyListenerWithUserDataSelect:
   2816             return reinterpret_cast<AudioComponentMethod>(RemovePropertyListenerWithUserData);
   2817         case kAudioUnitAddRenderNotifySelect:
   2818             return reinterpret_cast<AudioComponentMethod>(AddRenderNotify);
   2819         case kAudioUnitRemoveRenderNotifySelect:
   2820             return reinterpret_cast<AudioComponentMethod>(RemoveRenderNotify);
   2821         case kAudioUnitGetParameterSelect:
   2822             return reinterpret_cast<AudioComponentMethod>(GetParameter);
   2823         case kAudioUnitSetParameterSelect:
   2824             return reinterpret_cast<AudioComponentMethod>(SetParameter);
   2825         case kAudioUnitScheduleParametersSelect:
   2826             return reinterpret_cast<AudioComponentMethod>(ScheduleParameters);
   2827         case kAudioUnitRenderSelect:
   2828             return reinterpret_cast<AudioComponentMethod>(Render);
   2829         /*
   2830         case kAudioUnitComplexRenderSelect:
   2831             return reinterpret_cast<AudioComponentMethod>(ComplexRender);
   2832         */
   2833         case kAudioUnitResetSelect:
   2834             return reinterpret_cast<AudioComponentMethod>(Reset);
   2835         /*
   2836         case kAudioUnitProcessSelect:
   2837             return reinterpret_cast<AudioComponentMethod>(Process);
   2838         case kAudioUnitProcessMultipleSelect:
   2839             return reinterpret_cast<AudioComponentMethod>(ProcessMultiple);
   2840         */
   2841        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2842         case kMusicDeviceMIDIEventSelect:
   2843             return reinterpret_cast<AudioComponentMethod>(MIDIEvent);
   2844         case kMusicDeviceSysExSelect:
   2845             return reinterpret_cast<AudioComponentMethod>(SysEx);
   2846        #else
   2847         case kMusicDeviceMIDIEventSelect:
   2848         case kMusicDeviceSysExSelect:
   2849             return nullptr;
   2850        #endif
   2851         }
   2852 
   2853         d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector));
   2854         return nullptr;
   2855     }
   2856 
   2857     static OSStatus Initialize(AudioComponentPlugInInstance* const self)
   2858     {
   2859         d_debug("AudioComponentPlugInInstance::Initialize(%p)", self);
   2860         return self->plugin->auInitialize();
   2861     }
   2862 
   2863     static OSStatus Uninitialize(AudioComponentPlugInInstance* const self)
   2864     {
   2865         d_debug("AudioComponentPlugInInstance::Uninitialize(%p)", self);
   2866         return self->plugin->auUninitialize();
   2867     }
   2868 
   2869     static OSStatus GetPropertyInfo(AudioComponentPlugInInstance* const self,
   2870                                     const AudioUnitPropertyID inProp,
   2871                                     const AudioUnitScope inScope,
   2872                                     const AudioUnitElement inElement,
   2873                                     UInt32* const outDataSize,
   2874                                     Boolean* const outWritable)
   2875     {
   2876         d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %d:%x:%s, %d:%s, %d, ...)",
   2877                 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
   2878 
   2879 		UInt32 dataSize = 0;
   2880 		Boolean writable = false;
   2881         const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable);
   2882 
   2883 		if (outDataSize != nullptr)
   2884 			*outDataSize = dataSize;
   2885 
   2886 		if (outWritable != nullptr)
   2887 			*outWritable = writable;
   2888 
   2889         return res;
   2890     }
   2891 
   2892     static OSStatus GetProperty(AudioComponentPlugInInstance* const self,
   2893                                 const AudioUnitPropertyID inProp,
   2894                                 const AudioUnitScope inScope,
   2895                                 const AudioUnitElement inElement,
   2896                                 void* const outData,
   2897                                 UInt32* const ioDataSize)
   2898     {
   2899         d_debug("AudioComponentPlugInInstance::GetProperty(%p, %d:%x:%s, %d:%s, %d, ...)",
   2900                 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
   2901         DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError);
   2902 
   2903         Boolean writable;
   2904         UInt32 outDataSize = 0;
   2905         OSStatus res;
   2906 
   2907         if (outData == nullptr)
   2908         {
   2909             res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable);
   2910             *ioDataSize = outDataSize;
   2911             return res;
   2912         }
   2913 
   2914         const UInt32 inDataSize = *ioDataSize;
   2915         if (inDataSize == 0)
   2916             return kAudio_ParamError;
   2917 
   2918         res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable);
   2919 
   2920         if (res != noErr)
   2921             return res;
   2922 
   2923 		void* outBuffer;
   2924         uint8_t* tmpBuffer;
   2925         if (inDataSize < outDataSize)
   2926 		{
   2927 			tmpBuffer = new uint8_t[outDataSize];
   2928 			outBuffer = tmpBuffer;
   2929 		}
   2930         else
   2931         {
   2932 			tmpBuffer = nullptr;
   2933 			outBuffer = outData;
   2934 		}
   2935 
   2936         res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer);
   2937 
   2938 		if (res != noErr)
   2939         {
   2940 			*ioDataSize = 0;
   2941             return res;
   2942         }
   2943 
   2944         if (tmpBuffer != nullptr)
   2945         {
   2946             memcpy(outData, tmpBuffer, inDataSize);
   2947             delete[] tmpBuffer;
   2948         }
   2949         else
   2950         {
   2951             *ioDataSize = outDataSize;
   2952         }
   2953 
   2954         return noErr;
   2955     }
   2956 
   2957     static OSStatus SetProperty(AudioComponentPlugInInstance* const self,
   2958                                 const AudioUnitPropertyID inProp,
   2959                                 const AudioUnitScope inScope,
   2960                                 const AudioUnitElement inElement,
   2961                                 const void* const inData,
   2962                                 const UInt32 inDataSize)
   2963     {
   2964         d_debug("AudioComponentPlugInInstance::SetProperty(%p, %d:%x:%s, %d:%s, %d, %p, %u)",
   2965                 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize);
   2966         return self->plugin->auSetProperty(inProp, inScope, inElement, inData, inDataSize);
   2967     }
   2968 
   2969     static OSStatus AddPropertyListener(AudioComponentPlugInInstance* const self,
   2970                                         const AudioUnitPropertyID prop,
   2971                                         const AudioUnitPropertyListenerProc proc,
   2972                                         void* const userData)
   2973     {
   2974         d_debug("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%x:%s, %p, %p)",
   2975                 self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData);
   2976         return self->plugin->auAddPropertyListener(prop, proc, userData);
   2977     }
   2978 
   2979     static OSStatus RemovePropertyListener(AudioComponentPlugInInstance* const self,
   2980                                            const AudioUnitPropertyID prop,
   2981                                            const AudioUnitPropertyListenerProc proc)
   2982     {
   2983         d_debug("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%x:%s, %p)",
   2984                 self, prop, prop, AudioUnitPropertyID2Str(prop), proc);
   2985         return self->plugin->auRemovePropertyListener(prop, proc);
   2986     }
   2987 
   2988     static OSStatus RemovePropertyListenerWithUserData(AudioComponentPlugInInstance* const self,
   2989                                                        const AudioUnitPropertyID prop,
   2990                                                        const AudioUnitPropertyListenerProc proc,
   2991                                                        void* const userData)
   2992     {
   2993         d_debug("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%x:%s, %p, %p)",
   2994                 self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData);
   2995         return self->plugin->auRemovePropertyListenerWithUserData(prop, proc, userData);
   2996     }
   2997 
   2998     static OSStatus AddRenderNotify(AudioComponentPlugInInstance* const self,
   2999                                     const AURenderCallback proc,
   3000                                     void* const userData)
   3001     {
   3002         d_debug("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", self, proc, userData);
   3003         return self->plugin->auAddRenderNotify(proc, userData);
   3004     }
   3005 
   3006     static OSStatus RemoveRenderNotify(AudioComponentPlugInInstance* const self,
   3007                                        const AURenderCallback proc,
   3008                                        void* const userData)
   3009     {
   3010         d_debug("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", self, proc, userData);
   3011         return self->plugin->auRemoveRenderNotify(proc, userData);
   3012     }
   3013 
   3014     static OSStatus GetParameter(AudioComponentPlugInInstance* const self,
   3015                                  const AudioUnitParameterID param,
   3016                                  const AudioUnitScope scope,
   3017                                  const AudioUnitElement elem,
   3018                                  AudioUnitParameterValue* const value)
   3019     {
   3020         d_debug("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)",
   3021                 self, param, scope, AudioUnitScope2Str(scope), elem, value);
   3022         return self->plugin->auGetParameter(param, scope, elem, value);
   3023     }
   3024 
   3025     static OSStatus SetParameter(AudioComponentPlugInInstance* const self,
   3026                                  const AudioUnitParameterID param,
   3027                                  const AudioUnitScope scope,
   3028                                  const AudioUnitElement elem,
   3029                                  const AudioUnitParameterValue value,
   3030                                  const UInt32 bufferOffset)
   3031     {
   3032         d_debug("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)",
   3033                 self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset);
   3034         return self->plugin->auSetParameter(param, scope, elem, value, bufferOffset);
   3035     }
   3036 
   3037     static OSStatus ScheduleParameters(AudioComponentPlugInInstance* const self,
   3038                                        const AudioUnitParameterEvent* const events,
   3039                                        const UInt32 numEvents)
   3040     {
   3041         d_debug("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", self, events, numEvents);
   3042         return self->plugin->auScheduleParameters(events, numEvents);
   3043     }
   3044 
   3045     static OSStatus Reset(AudioComponentPlugInInstance* const self,
   3046                           const AudioUnitScope scope,
   3047                           const AudioUnitElement elem)
   3048     {
   3049         d_debug("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", self, scope, AudioUnitScope2Str(scope), elem);
   3050         return self->plugin->auReset(scope, elem);
   3051     }
   3052 
   3053     static OSStatus Render(AudioComponentPlugInInstance* const self,
   3054                            AudioUnitRenderActionFlags* ioActionFlags,
   3055                            const AudioTimeStamp* const inTimeStamp,
   3056                            const UInt32 inOutputBusNumber,
   3057                            const UInt32 inNumberFrames,
   3058                            AudioBufferList* const ioData)
   3059     {
   3060         const AudioUnitRenderActionFlags actionFlags = ioActionFlags != nullptr ? *ioActionFlags : 0;
   3061 
   3062         if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0)
   3063         {
   3064             DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError);
   3065             DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError);
   3066         }
   3067 
   3068         return self->plugin->auRender(actionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
   3069     }
   3070 
   3071    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   3072     static OSStatus MIDIEvent(AudioComponentPlugInInstance* const self,
   3073                               const UInt32 inStatus,
   3074                               const UInt32 inData1,
   3075                               const UInt32 inData2,
   3076                               const UInt32 inOffsetSampleFrame)
   3077     {
   3078         return self->plugin->auMIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
   3079     }
   3080 
   3081     static OSStatus SysEx(AudioComponentPlugInInstance* const self, const UInt8* const inData, const UInt32 inLength)
   3082     {
   3083         return self->plugin->auSysEx(inData, inLength);
   3084     }
   3085    #endif
   3086 
   3087     DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance)
   3088 };
   3089 
   3090 #if 0
   3091 static OSStatus FastDispatchGetParameter(void* const self,
   3092                                          const AudioUnitParameterID param,
   3093                                          const AudioUnitScope scope,
   3094                                          const AudioUnitElement elem,
   3095                                          Float32* const value)
   3096 {
   3097     d_debug("FastDispatchGetParameter(%p, %d, %d:%s, %d, %p)",
   3098             self, param, scope, AudioUnitScope2Str(scope), elem, value);
   3099     return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auGetParameter(param, scope, elem, value);
   3100 }
   3101 
   3102 static OSStatus FastDispatchSetParameter(void* const self,
   3103                                          const AudioUnitParameterID param,
   3104                                          const AudioUnitScope scope,
   3105                                          const AudioUnitElement elem,
   3106                                          const Float32 value,
   3107                                          const UInt32 bufferOffset)
   3108 {
   3109     d_debug("FastDispatchSetParameter(%p, %d %d:%s, %d, %f, %u)",
   3110             self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset);
   3111     return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auSetParameter(param, scope, elem, value, bufferOffset);
   3112 }
   3113 
   3114 static OSStatus FastDispatchRender(void* const self,
   3115                                    AudioUnitRenderActionFlags* const ioActionFlags,
   3116                                    const AudioTimeStamp* const inTimeStamp,
   3117                                    const UInt32 inBusNumber,
   3118                                    const UInt32 inNumberFrames,
   3119                                    AudioBufferList* const ioData)
   3120 {
   3121     DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError);
   3122     DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError);
   3123 
   3124     return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, *ioData);
   3125 }
   3126 #endif
   3127 
   3128 // --------------------------------------------------------------------------------------------------------------------
   3129 
   3130 END_NAMESPACE_DISTRHO
   3131 
   3132 DISTRHO_PLUGIN_EXPORT
   3133 void* PluginAUFactory(const AudioComponentDescription* const desc)
   3134 {
   3135     USE_NAMESPACE_DISTRHO
   3136 
   3137     DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentType == kType, desc->componentType, kType, nullptr);
   3138     DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentSubType == kSubType, desc->componentSubType, kSubType, nullptr);
   3139     DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentManufacturer == kManufacturer, desc->componentManufacturer, kManufacturer, nullptr);
   3140 
   3141     if (d_nextBufferSize == 0)
   3142         d_nextBufferSize = 1156;
   3143 
   3144     if (d_isZero(d_nextSampleRate))
   3145         d_nextSampleRate = 44100.0;
   3146 
   3147     if (d_nextBundlePath == nullptr)
   3148     {
   3149         static String bundlePath;
   3150 
   3151         String tmpPath(getBinaryFilename());
   3152         tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   3153         tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   3154 
   3155         if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents"))
   3156         {
   3157             tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   3158             bundlePath = tmpPath;
   3159         }
   3160         else
   3161         {
   3162             bundlePath = "error";
   3163         }
   3164 
   3165         d_nextBundlePath = bundlePath.buffer();
   3166     }
   3167 
   3168     d_nextCanRequestParameterValueChanges = true;
   3169 
   3170     return new AudioComponentPlugInInstance();
   3171 }
   3172 
   3173 // --------------------------------------------------------------------------------------------------------------------