diff DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginInternal.hpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginInternal.hpp	Mon Oct 16 21:53:34 2023 +0200
@@ -0,0 +1,1061 @@
+/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with
+ * or without fee is hereby granted, provided that the above copyright notice and this
+ * permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED
+#define DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED
+
+#include "../DistrhoPlugin.hpp"
+
+#ifdef DISTRHO_PLUGIN_TARGET_VST3
+# include "DistrhoPluginVST.hpp"
+#endif
+
+#include <set>
+
+START_NAMESPACE_DISTRHO
+
+// -----------------------------------------------------------------------
+// Maxmimum values
+
+static const uint32_t kMaxMidiEvents = 512;
+
+// -----------------------------------------------------------------------
+// Static data, see DistrhoPlugin.cpp
+
+extern uint32_t    d_nextBufferSize;
+extern double      d_nextSampleRate;
+extern const char* d_nextBundlePath;
+extern bool        d_nextPluginIsDummy;
+extern bool        d_nextPluginIsSelfTest;
+extern bool        d_nextCanRequestParameterValueChanges;
+
+// -----------------------------------------------------------------------
+// DSP callbacks
+
+typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent);
+typedef bool (*requestParameterValueChangeFunc) (void* ptr, uint32_t index, float value);
+typedef bool (*updateStateValueFunc) (void* ptr, const char* key, const char* value);
+
+// -----------------------------------------------------------------------
+// Helpers
+
+struct AudioPortWithBusId : AudioPort {
+    uint32_t busId;
+
+    AudioPortWithBusId()
+        : AudioPort(),
+          busId(0) {}
+};
+
+struct PortGroupWithId : PortGroup {
+    uint32_t groupId;
+
+    PortGroupWithId()
+        : PortGroup(),
+          groupId(kPortGroupNone) {}
+};
+
+static inline
+void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
+{
+    switch (groupId)
+    {
+    case kPortGroupNone:
+        portGroup.name.clear();
+        portGroup.symbol.clear();
+        break;
+    case kPortGroupMono:
+        portGroup.name = "Mono";
+        portGroup.symbol = "dpf_mono";
+        break;
+    case kPortGroupStereo:
+        portGroup.name = "Stereo";
+        portGroup.symbol = "dpf_stereo";
+        break;
+    }
+}
+
+static inline
+void d_strncpy(char* const dst, const char* const src, const size_t length)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
+
+    if (const size_t len = std::min(std::strlen(src), length-1U))
+    {
+        std::memcpy(dst, src, len);
+        dst[len] = '\0';
+    }
+    else
+    {
+        dst[0] = '\0';
+    }
+}
+
+template<typename T>
+static inline
+void snprintf_t(char* const dst, const T value, const char* const format, const size_t size)
+{
+    DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
+    std::snprintf(dst, size-1, format, value);
+    dst[size-1] = '\0';
+}
+
+static inline
+void snprintf_f32(char* const dst, const float value, const size_t size)
+{
+    return snprintf_t<float>(dst, value, "%f", size);
+}
+
+static inline
+void snprintf_f32(char* const dst, const double value, const size_t size)
+{
+    return snprintf_t<double>(dst, value, "%f", size);
+}
+
+static inline
+void snprintf_i32(char* const dst, const int32_t value, const size_t size)
+{
+    return snprintf_t<int32_t>(dst, value, "%d", size);
+}
+
+static inline
+void snprintf_u32(char* const dst, const uint32_t value, const size_t size)
+{
+    return snprintf_t<uint32_t>(dst, value, "%u", size);
+}
+
+// -----------------------------------------------------------------------
+// Plugin private data
+
+struct Plugin::PrivateData {
+    const bool canRequestParameterValueChanges;
+    const bool isDummy;
+    const bool isSelfTest;
+    bool isProcessing;
+
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+    AudioPortWithBusId* audioPorts;
+#endif
+
+    uint32_t   parameterCount;
+    uint32_t   parameterOffset;
+    Parameter* parameters;
+
+    uint32_t         portGroupCount;
+    PortGroupWithId* portGroups;
+
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+    uint32_t programCount;
+    String*  programNames;
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
+    uint32_t stateCount;
+    State*   states;
+#endif
+
+#if DISTRHO_PLUGIN_WANT_LATENCY
+    uint32_t latency;
+#endif
+
+#if DISTRHO_PLUGIN_WANT_TIMEPOS
+    TimePosition timePosition;
+#endif
+
+    // Callbacks
+    void*         callbacksPtr;
+    writeMidiFunc writeMidiCallbackFunc;
+    requestParameterValueChangeFunc requestParameterValueChangeCallbackFunc;
+    updateStateValueFunc updateStateValueCallbackFunc;
+
+    uint32_t bufferSize;
+    double   sampleRate;
+    char*    bundlePath;
+
+    PrivateData() noexcept
+        : canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges),
+          isDummy(d_nextPluginIsDummy),
+          isSelfTest(d_nextPluginIsSelfTest),
+          isProcessing(false),
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+          audioPorts(nullptr),
+#endif
+          parameterCount(0),
+          parameterOffset(0),
+          parameters(nullptr),
+          portGroupCount(0),
+          portGroups(nullptr),
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+          programCount(0),
+          programNames(nullptr),
+#endif
+#if DISTRHO_PLUGIN_WANT_STATE
+          stateCount(0),
+          states(nullptr),
+#endif
+#if DISTRHO_PLUGIN_WANT_LATENCY
+          latency(0),
+#endif
+          callbacksPtr(nullptr),
+          writeMidiCallbackFunc(nullptr),
+          requestParameterValueChangeCallbackFunc(nullptr),
+          updateStateValueCallbackFunc(nullptr),
+          bufferSize(d_nextBufferSize),
+          sampleRate(d_nextSampleRate),
+          bundlePath(d_nextBundlePath != nullptr ? strdup(d_nextBundlePath) : nullptr)
+    {
+        DISTRHO_SAFE_ASSERT(bufferSize != 0);
+        DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
+
+#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
+        parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
+# if DISTRHO_PLUGIN_WANT_LATENCY
+        parameterOffset += 1;
+# endif
+#endif
+
+#ifdef DISTRHO_PLUGIN_TARGET_LV2
+# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_STATE || DISTRHO_PLUGIN_WANT_TIMEPOS)
+        parameterOffset += 1;
+# endif
+# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
+        parameterOffset += 1;
+# endif
+#endif
+
+#ifdef DISTRHO_PLUGIN_TARGET_VST3
+        parameterOffset += kVst3InternalParameterCount;
+#endif
+    }
+
+    ~PrivateData() noexcept
+    {
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+        if (audioPorts != nullptr)
+        {
+            delete[] audioPorts;
+            audioPorts = nullptr;
+        }
+#endif
+
+        if (parameters != nullptr)
+        {
+            delete[] parameters;
+            parameters = nullptr;
+        }
+
+        if (portGroups != nullptr)
+        {
+            delete[] portGroups;
+            portGroups = nullptr;
+        }
+
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+        if (programNames != nullptr)
+        {
+            delete[] programNames;
+            programNames = nullptr;
+        }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
+        if (states != nullptr)
+        {
+            delete[] states;
+            states = nullptr;
+        }
+#endif
+
+        if (bundlePath != nullptr)
+        {
+            std::free(bundlePath);
+            bundlePath = nullptr;
+        }
+    }
+
+#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+    bool writeMidiCallback(const MidiEvent& midiEvent)
+    {
+        if (writeMidiCallbackFunc != nullptr)
+            return writeMidiCallbackFunc(callbacksPtr, midiEvent);
+
+        return false;
+    }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
+    bool requestParameterValueChangeCallback(const uint32_t index, const float value)
+    {
+        if (requestParameterValueChangeCallbackFunc != nullptr)
+            return requestParameterValueChangeCallbackFunc(callbacksPtr, index, value);
+
+        return false;
+    }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
+    bool updateStateValueCallback(const char* const key, const char* const value)
+    {
+        d_stdout("updateStateValueCallback %p", updateStateValueCallbackFunc);
+        if (updateStateValueCallbackFunc != nullptr)
+            return updateStateValueCallbackFunc(callbacksPtr, key, value);
+
+        return false;
+    }
+#endif
+};
+
+// -----------------------------------------------------------------------
+// Plugin exporter class
+
+class PluginExporter
+{
+public:
+    PluginExporter(void* const callbacksPtr,
+                   const writeMidiFunc writeMidiCall,
+                   const requestParameterValueChangeFunc requestParameterValueChangeCall,
+                   const updateStateValueFunc updateStateValueCall)
+        : fPlugin(createPlugin()),
+          fData((fPlugin != nullptr) ? fPlugin->pData : nullptr),
+          fIsActive(false)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+
+#if defined(DPF_RUNTIME_TESTING) && defined(__GNUC__) && !defined(__clang__)
+        /* Run-time testing build.
+         * Verify that virtual functions are overriden if parameters, programs or states are in use.
+         * This does not work on all compilers, but we use it purely as informational check anyway. */
+        if (fData->parameterCount != 0)
+        {
+            if ((void*)(fPlugin->*(&Plugin::initParameter)) == (void*)&Plugin::initParameter)
+            {
+                d_stderr2("DPF warning: Plugins with parameters must implement `initParameter`");
+                abort();
+            }
+            if ((void*)(fPlugin->*(&Plugin::getParameterValue)) == (void*)&Plugin::getParameterValue)
+            {
+                d_stderr2("DPF warning: Plugins with parameters must implement `getParameterValue`");
+                abort();
+            }
+            if ((void*)(fPlugin->*(&Plugin::setParameterValue)) == (void*)&Plugin::setParameterValue)
+            {
+                d_stderr2("DPF warning: Plugins with parameters must implement `setParameterValue`");
+                abort();
+            }
+        }
+
+# if DISTRHO_PLUGIN_WANT_PROGRAMS
+        if (fData->programCount != 0)
+        {
+            if ((void*)(fPlugin->*(&Plugin::initProgramName)) == (void*)&Plugin::initProgramName)
+            {
+                d_stderr2("DPF warning: Plugins with programs must implement `initProgramName`");
+                abort();
+            }
+            if ((void*)(fPlugin->*(&Plugin::loadProgram)) == (void*)&Plugin::loadProgram)
+            {
+                d_stderr2("DPF warning: Plugins with programs must implement `loadProgram`");
+                abort();
+            }
+        }
+# endif
+
+# if DISTRHO_PLUGIN_WANT_STATE
+        if (fData->stateCount != 0)
+        {
+            if ((void*)(fPlugin->*(static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))) ==
+                (void*)static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))
+            {
+                d_stderr2("DPF warning: Plugins with state must implement `initState`");
+                abort();
+            }
+
+            if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState)
+            {
+                d_stderr2("DPF warning: Plugins with state must implement `setState`");
+                abort();
+            }
+        }
+# endif
+
+# if DISTRHO_PLUGIN_WANT_FULL_STATE
+        if (fData->stateCount != 0)
+        {
+            if ((void*)(fPlugin->*(&Plugin::getState)) == (void*)&Plugin::getState)
+            {
+                d_stderr2("DPF warning: Plugins with full state must implement `getState`");
+                abort();
+            }
+        }
+        else
+        {
+            d_stderr2("DPF warning: Plugins with full state must have at least 1 state");
+            abort();
+        }
+# endif
+#endif
+
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+        {
+            uint32_t j=0;
+# if DISTRHO_PLUGIN_NUM_INPUTS > 0
+            for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++j)
+                fPlugin->initAudioPort(true, i, fData->audioPorts[j]);
+# endif
+# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+            for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++j)
+                fPlugin->initAudioPort(false, i, fData->audioPorts[j]);
+# endif
+        }
+#endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+
+        for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
+            fPlugin->initParameter(i, fData->parameters[i]);
+
+        {
+            std::set<uint32_t> portGroupIndices;
+
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+            for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
+                portGroupIndices.insert(fData->audioPorts[i].groupId);
+#endif
+            for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
+                portGroupIndices.insert(fData->parameters[i].groupId);
+
+            portGroupIndices.erase(kPortGroupNone);
+
+            if (const uint32_t portGroupSize = static_cast<uint32_t>(portGroupIndices.size()))
+            {
+                fData->portGroups = new PortGroupWithId[portGroupSize];
+                fData->portGroupCount = portGroupSize;
+
+                uint32_t index = 0;
+                for (std::set<uint32_t>::iterator it = portGroupIndices.begin(); it != portGroupIndices.end(); ++it, ++index)
+                {
+                    PortGroupWithId& portGroup(fData->portGroups[index]);
+                    portGroup.groupId = *it;
+
+                    if (portGroup.groupId < portGroupSize)
+                        fPlugin->initPortGroup(portGroup.groupId, portGroup);
+                    else
+                        fillInPredefinedPortGroupData(portGroup.groupId, portGroup);
+                }
+            }
+        }
+
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+        for (uint32_t i=0, count=fData->programCount; i < count; ++i)
+            fPlugin->initProgramName(i, fData->programNames[i]);
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
+        for (uint32_t i=0, count=fData->stateCount; i < count; ++i)
+            fPlugin->initState(i, fData->states[i]);
+#endif
+
+        fData->callbacksPtr = callbacksPtr;
+        fData->writeMidiCallbackFunc = writeMidiCall;
+        fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall;
+        fData->updateStateValueCallbackFunc = updateStateValueCall;
+    }
+
+    ~PluginExporter()
+    {
+        delete fPlugin;
+    }
+
+    // -------------------------------------------------------------------
+
+    const char* getName() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getName();
+    }
+
+    const char* getLabel() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getLabel();
+    }
+
+    const char* getDescription() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getDescription();
+    }
+
+    const char* getMaker() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getMaker();
+    }
+
+    const char* getHomePage() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getHomePage();
+    }
+
+    const char* getLicense() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, "");
+
+        return fPlugin->getLicense();
+    }
+
+    uint32_t getVersion() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0);
+
+        return fPlugin->getVersion();
+    }
+
+    long getUniqueId() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0);
+
+        return fPlugin->getUniqueId();
+    }
+
+    void* getInstancePointer() const noexcept
+    {
+        return fPlugin;
+    }
+
+    // -------------------------------------------------------------------
+
+#if DISTRHO_PLUGIN_WANT_LATENCY
+    uint32_t getLatency() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->latency;
+    }
+#endif
+
+#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+    AudioPortWithBusId& getAudioPort(const bool input, const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort);
+
+        if (input)
+        {
+# if DISTRHO_PLUGIN_NUM_INPUTS > 0
+            DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_INPUTS,  sFallbackAudioPort);
+# endif
+        }
+        else
+        {
+# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+            DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_OUTPUTS, sFallbackAudioPort);
+# endif
+        }
+
+        return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)];
+    }
+
+    uint32_t getAudioPortHints(const bool input, const uint32_t index) const noexcept
+    {
+        return getAudioPort(input, index).hints;
+    }
+    
+    uint32_t getAudioPortCountWithGroupId(const bool input, const uint32_t groupId) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        uint32_t numPorts = 0;
+
+        if (input)
+        {
+           #if DISTRHO_PLUGIN_NUM_INPUTS > 0
+            for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
+            {
+                if (fData->audioPorts[i].groupId == groupId)
+                    ++numPorts;
+            }
+           #endif
+        }
+        else
+        {
+           #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+            for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
+            {
+                if (fData->audioPorts[i + DISTRHO_PLUGIN_NUM_INPUTS].groupId == groupId)
+                    ++numPorts;
+            }
+           #endif
+        }
+
+        return numPorts;
+    }
+#endif
+
+    uint32_t getParameterCount() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->parameterCount;
+    }
+
+    uint32_t getParameterOffset() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->parameterOffset;
+    }
+
+    uint32_t getParameterHints(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0x0);
+
+        return fData->parameters[index].hints;
+    }
+
+    ParameterDesignation getParameterDesignation(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, kParameterDesignationNull);
+
+        return fData->parameters[index].designation;
+    }
+
+    bool isParameterInput(const uint32_t index) const noexcept
+    {
+        return (getParameterHints(index) & kParameterIsOutput) == 0x0;
+    }
+
+    bool isParameterOutput(const uint32_t index) const noexcept
+    {
+        return (getParameterHints(index) & kParameterIsOutput) != 0x0;
+    }
+
+    bool isParameterInteger(const uint32_t index) const noexcept
+    {
+        return (getParameterHints(index) & kParameterIsInteger) != 0x0;
+    }
+
+    bool isParameterTrigger(const uint32_t index) const noexcept
+    {
+        return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger;
+    }
+
+    bool isParameterOutputOrTrigger(const uint32_t index) const noexcept
+    {
+        const uint32_t hints = getParameterHints(index);
+
+        if (hints & kParameterIsOutput)
+            return true;
+        if ((hints & kParameterIsTrigger) == kParameterIsTrigger)
+            return true;
+
+        return false;
+    }
+
+    const String& getParameterName(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
+
+        return fData->parameters[index].name;
+    }
+
+    const String& getParameterShortName(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
+
+        return fData->parameters[index].shortName;
+    }
+
+    const String& getParameterSymbol(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
+
+        return fData->parameters[index].symbol;
+    }
+
+    const String& getParameterUnit(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
+
+        return fData->parameters[index].unit;
+    }
+
+    const String& getParameterDescription(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackString);
+
+        return fData->parameters[index].description;
+    }
+
+    const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues);
+
+        return fData->parameters[index].enumValues;
+    }
+
+    const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackRanges);
+
+        return fData->parameters[index].ranges;
+    }
+
+    uint8_t getParameterMidiCC(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0);
+
+        return fData->parameters[index].midiCC;
+    }
+
+    uint32_t getParameterGroupId(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, kPortGroupNone);
+
+        return fData->parameters[index].groupId;
+    }
+
+    float getParameterDefault(const uint32_t index) const
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f);
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f);
+
+        return fData->parameters[index].ranges.def;
+    }
+
+    float getParameterValue(const uint32_t index) const
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr, 0.0f);
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, 0.0f);
+
+        return fPlugin->getParameterValue(index);
+    }
+
+    void setParameterValue(const uint32_t index, const float value)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount,);
+
+        fPlugin->setParameterValue(index, value);
+    }
+
+    uint32_t getPortGroupCount() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->portGroupCount;
+    }
+
+    const PortGroupWithId& getPortGroupById(const uint32_t groupId) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && fData->portGroupCount != 0, sFallbackPortGroup);
+
+        for (uint32_t i=0; i < fData->portGroupCount; ++i)
+        {
+            const PortGroupWithId& portGroup(fData->portGroups[i]);
+
+            if (portGroup.groupId == groupId)
+                return portGroup;
+        }
+
+        return sFallbackPortGroup;
+    }
+
+    const PortGroupWithId& getPortGroupByIndex(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->portGroupCount, sFallbackPortGroup);
+
+        return fData->portGroups[index];
+    }
+
+    const String& getPortGroupSymbolForId(const uint32_t groupId) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString);
+
+        return getPortGroupById(groupId).symbol;
+    }
+
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+    uint32_t getProgramCount() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->programCount;
+    }
+
+    const String& getProgramName(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->programCount, sFallbackString);
+
+        return fData->programNames[index];
+    }
+
+    void loadProgram(const uint32_t index)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->programCount,);
+
+        fPlugin->loadProgram(index);
+    }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
+    uint32_t getStateCount() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+
+        return fData->stateCount;
+    }
+
+    uint32_t getStateHints(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, 0x0);
+
+        return fData->states[index].hints;
+    }
+
+    const String& getStateKey(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+        return fData->states[index].key;
+    }
+
+    const String& getStateDefaultValue(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+        return fData->states[index].defaultValue;
+    }
+
+    const String& getStateLabel(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+        return fData->states[index].label;
+    }
+
+    const String& getStateDescription(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+        return fData->states[index].description;
+    }
+
+   #ifdef __MOD_DEVICES__
+    const String& getStateFileTypes(const uint32_t index) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, sFallbackString);
+
+        return fData->states[index].fileTypes;
+    }
+   #endif
+
+# if DISTRHO_PLUGIN_WANT_FULL_STATE
+    String getStateValue(const char* const key) const
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackString);
+        DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', sFallbackString);
+
+        return fPlugin->getState(key);
+    }
+# endif
+
+    void setState(const char* const key, const char* const value)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
+        DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,);
+
+        fPlugin->setState(key, value);
+    }
+
+    bool wantStateKey(const char* const key) const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, false);
+        DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0', false);
+
+        for (uint32_t i=0; i < fData->stateCount; ++i)
+        {
+            if (fData->states[i].key == key)
+                return true;
+        }
+
+        return false;
+    }
+#endif
+
+#if DISTRHO_PLUGIN_WANT_TIMEPOS
+    void setTimePosition(const TimePosition& timePosition) noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+
+        std::memcpy(&fData->timePosition, &timePosition, sizeof(TimePosition));
+    }
+#endif
+
+    // -------------------------------------------------------------------
+
+    bool isActive() const noexcept
+    {
+        return fIsActive;
+    }
+
+    void activate()
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(! fIsActive,);
+
+        fIsActive = true;
+        fPlugin->activate();
+    }
+
+    void deactivate()
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fIsActive,);
+
+        fIsActive = false;
+        fPlugin->deactivate();
+    }
+
+    void deactivateIfNeeded()
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+
+        if (fIsActive)
+        {
+            fIsActive = false;
+            fPlugin->deactivate();
+        }
+    }
+
+#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
+    void run(const float** const inputs, float** const outputs, const uint32_t frames,
+             const MidiEvent* const midiEvents, const uint32_t midiEventCount)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+
+        if (! fIsActive)
+        {
+            fIsActive = true;
+            fPlugin->activate();
+        }
+
+        fData->isProcessing = true;
+        fPlugin->run(inputs, outputs, frames, midiEvents, midiEventCount);
+        fData->isProcessing = false;
+    }
+#else
+    void run(const float** const inputs, float** const outputs, const uint32_t frames)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+
+        if (! fIsActive)
+        {
+            fIsActive = true;
+            fPlugin->activate();
+        }
+
+        fData->isProcessing = true;
+        fPlugin->run(inputs, outputs, frames);
+        fData->isProcessing = false;
+    }
+#endif
+
+    // -------------------------------------------------------------------
+
+    uint32_t getBufferSize() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
+        return fData->bufferSize;
+    }
+
+    double getSampleRate() const noexcept
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0.0);
+        return fData->sampleRate;
+    }
+
+    void setBufferSize(const uint32_t bufferSize, const bool doCallback = false)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT(bufferSize >= 2);
+
+        if (fData->bufferSize == bufferSize)
+            return;
+
+        fData->bufferSize = bufferSize;
+
+        if (doCallback)
+        {
+            if (fIsActive) fPlugin->deactivate();
+            fPlugin->bufferSizeChanged(bufferSize);
+            if (fIsActive) fPlugin->activate();
+        }
+    }
+
+    void setSampleRate(const double sampleRate, const bool doCallback = false)
+    {
+        DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
+        DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
+        DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
+
+        if (d_isEqual(fData->sampleRate, sampleRate))
+            return;
+
+        fData->sampleRate = sampleRate;
+
+        if (doCallback)
+        {
+            if (fIsActive) fPlugin->deactivate();
+            fPlugin->sampleRateChanged(sampleRate);
+            if (fIsActive) fPlugin->activate();
+        }
+    }
+
+private:
+    // -------------------------------------------------------------------
+    // Plugin and DistrhoPlugin data
+
+    Plugin* const fPlugin;
+    Plugin::PrivateData* const fData;
+    bool fIsActive;
+
+    // -------------------------------------------------------------------
+    // Static fallback data, see DistrhoPlugin.cpp
+
+    static const String                     sFallbackString;
+    static /* */ AudioPortWithBusId         sFallbackAudioPort;
+    static const ParameterRanges            sFallbackRanges;
+    static const ParameterEnumerationValues sFallbackEnumValues;
+    static const PortGroupWithId            sFallbackPortGroup;
+
+    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter)
+};
+
+// -----------------------------------------------------------------------
+
+END_NAMESPACE_DISTRHO
+
+#endif // DISTRHO_PLUGIN_INTERNAL_HPP_INCLUDED