DPF

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

DistrhoPluginVST2.cpp (57357B)


      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 #include "DistrhoPluginInternal.hpp"
     18 #include "DistrhoPluginVST.hpp"
     19 #include "../DistrhoPluginUtils.hpp"
     20 #include "../extra/ScopedSafeLocale.hpp"
     21 #include "../extra/ScopedPointer.hpp"
     22 
     23 #if DISTRHO_PLUGIN_HAS_UI
     24 # include "DistrhoUIInternal.hpp"
     25 # include "../extra/RingBuffer.hpp"
     26 #endif
     27 
     28 #include <clocale>
     29 #include <map>
     30 #include <string>
     31 #include <vector>
     32 
     33 #ifndef __cdecl
     34 # define __cdecl
     35 #endif
     36 
     37 #include "xaymar-vst2/vst.h"
     38 
     39 START_NAMESPACE_DISTRHO
     40 
     41 // --------------------------------------------------------------------------------------------------------------------
     42 
     43 extern "C" {
     44 
     45 // define the midi stuff ourselves
     46 typedef struct _VstMidiEvent {
     47     int32_t type;
     48     int32_t byteSize;
     49     int32_t deltaFrames;
     50     int32_t _ignore1[3];
     51     char midiData[4];
     52     char _ignore2[4];
     53 } VstMidiEvent;
     54 
     55 typedef union _VstEvent {
     56     int32_t type;
     57     VstMidiEvent midi; // type 1
     58 } VstEvent;
     59 
     60 typedef struct _HostVstEvents {
     61     int32_t numEvents;
     62     void* reserved;
     63     const VstEvent* events[];
     64 } HostVstEvents;
     65 
     66 typedef struct _PluginVstEvents {
     67     int32_t numEvents;
     68     void* reserved;
     69     VstEvent* events[1];
     70 } PluginVstEvents;
     71 
     72 // info from online documentation of VST provided by Steinberg
     73 typedef struct _VstTimeInfo {
     74     double samplePos;
     75     double sampleRate;
     76     double nanoSeconds;
     77     double ppqPos;
     78     double tempo;
     79     double barStartPos;
     80     double cycleStartPos;
     81     double cycleEndPos;
     82     int32_t timeSigNumerator;
     83     int32_t timeSigDenominator;
     84     int32_t smpteOffset;
     85     int32_t smpteFrameRate;
     86     int32_t samplesToNextClock;
     87     int32_t flags;
     88 } VstTimeInfo;
     89 
     90 } // extern "C"
     91 
     92 // --------------------------------------------------------------------------------------------------------------------
     93 
     94 typedef std::map<const String, String> StringMap;
     95 
     96 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
     97 static constexpr const writeMidiFunc writeMidiCallback = nullptr;
     98 #endif
     99 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
    100 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
    101 #endif
    102 
    103 // --------------------------------------------------------------------------------------------------------------------
    104 
    105 struct ParameterAndNotesHelper
    106 {
    107     float* parameterValues;
    108   #if DISTRHO_PLUGIN_HAS_UI
    109     bool* parameterChecks;
    110    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    111     SmallStackBuffer notesRingBuffer;
    112    #endif
    113   #endif
    114 
    115     ParameterAndNotesHelper()
    116         : parameterValues(nullptr)
    117       #if DISTRHO_PLUGIN_HAS_UI
    118         , parameterChecks(nullptr)
    119        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    120         , notesRingBuffer(StackBuffer_INIT)
    121        #endif
    122       #endif
    123     {
    124        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT)
    125         std::memset(&notesRingBuffer, 0, sizeof(notesRingBuffer));
    126        #endif
    127     }
    128 
    129     virtual ~ParameterAndNotesHelper()
    130     {
    131         if (parameterValues != nullptr)
    132         {
    133             delete[] parameterValues;
    134             parameterValues = nullptr;
    135         }
    136        #if DISTRHO_PLUGIN_HAS_UI
    137         if (parameterChecks != nullptr)
    138         {
    139             delete[] parameterChecks;
    140             parameterChecks = nullptr;
    141         }
    142        #endif
    143     }
    144 
    145    #if DISTRHO_PLUGIN_WANT_STATE
    146     virtual void setStateFromUI(const char* key, const char* value) = 0;
    147    #endif
    148 };
    149 
    150 #if DISTRHO_PLUGIN_HAS_UI
    151 // --------------------------------------------------------------------------------------------------------------------
    152 
    153 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
    154 static const sendNoteFunc sendNoteCallback = nullptr;
    155 #endif
    156 #if ! DISTRHO_PLUGIN_WANT_STATE
    157 static const setStateFunc setStateCallback = nullptr;
    158 #endif
    159 
    160 class UIVst
    161 {
    162 public:
    163     UIVst(const vst_host_callback audioMaster,
    164           vst_effect* const effect,
    165           ParameterAndNotesHelper* const uiHelper,
    166           PluginExporter* const plugin,
    167           const intptr_t winId, const float scaleFactor)
    168         : fAudioMaster(audioMaster),
    169           fEffect(effect),
    170           fUiHelper(uiHelper),
    171           fPlugin(plugin),
    172           fUI(this, winId, plugin->getSampleRate(),
    173               editParameterCallback,
    174               setParameterCallback,
    175               setStateCallback,
    176               sendNoteCallback,
    177               setSizeCallback,
    178               nullptr, // TODO file request
    179               d_nextBundlePath,
    180               plugin->getInstancePointer(),
    181               scaleFactor)
    182        #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    183         , fKeyboardModifiers(0)
    184        #endif
    185        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    186         , fNotesRingBuffer()
    187        #endif
    188     {
    189        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    190         fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false);
    191        #endif
    192     }
    193 
    194     // ----------------------------------------------------------------------------------------------------------------
    195 
    196     void idle()
    197     {
    198         for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i)
    199         {
    200             if (fUiHelper->parameterChecks[i])
    201             {
    202                 fUiHelper->parameterChecks[i] = false;
    203                 fUI.parameterChanged(i, fUiHelper->parameterValues[i]);
    204             }
    205         }
    206 
    207         fUI.plugin_idle();
    208     }
    209 
    210     int16_t getWidth() const
    211     {
    212         return fUI.getWidth();
    213     }
    214 
    215     int16_t getHeight() const
    216     {
    217         return fUI.getHeight();
    218     }
    219 
    220     double getScaleFactor() const
    221     {
    222         return fUI.getScaleFactor();
    223     }
    224 
    225     void setSampleRate(const double newSampleRate)
    226     {
    227         fUI.setSampleRate(newSampleRate, true);
    228     }
    229 
    230     void notifyScaleFactorChanged(const double scaleFactor)
    231     {
    232         fUI.notifyScaleFactorChanged(scaleFactor);
    233     }
    234 
    235     // ----------------------------------------------------------------------------------------------------------------
    236     // functions called from the plugin side, may block
    237 
    238    #if DISTRHO_PLUGIN_WANT_STATE
    239     void setStateFromPlugin(const char* const key, const char* const value)
    240     {
    241         fUI.stateChanged(key, value);
    242     }
    243    #endif
    244 
    245    #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    246     int handlePluginKeyEvent(const bool down, const int32_t index, const intptr_t value)
    247     {
    248         d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value);
    249 
    250         using namespace DGL_NAMESPACE;
    251 
    252         bool special;
    253         const uint key = translateVstKeyCode(special, index, static_cast<int32_t>(value));
    254 
    255         switch (key)
    256         {
    257         case kKeyShiftL:
    258         case kKeyShiftR:
    259             if (down)
    260                 fKeyboardModifiers |= kModifierShift;
    261             else
    262                 fKeyboardModifiers &= ~kModifierShift;
    263             break;
    264         case kKeyControlL:
    265         case kKeyControlR:
    266             if (down)
    267                 fKeyboardModifiers |= kModifierControl;
    268             else
    269                 fKeyboardModifiers &= ~kModifierControl;
    270             break;
    271         case kKeyAltL:
    272         case kKeyAltR:
    273             if (down)
    274                 fKeyboardModifiers |= kModifierAlt;
    275             else
    276                 fKeyboardModifiers &= ~kModifierAlt;
    277             break;
    278         }
    279 
    280         return fUI.handlePluginKeyboardVST(down, special, key,
    281                                            value >= 0 ? static_cast<uint>(value) : 0,
    282                                            fKeyboardModifiers) ? 1 : 0;
    283     }
    284    #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    285 
    286     // ----------------------------------------------------------------------------------------------------------------
    287 
    288 protected:
    289     inline intptr_t hostCallback(const VST_HOST_OPCODE opcode,
    290                                  const int32_t index = 0,
    291                                  const intptr_t value = 0,
    292                                  void* const ptr = nullptr,
    293                                  const float opt = 0.0f) const
    294     {
    295         return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
    296     }
    297 
    298     void editParameter(const uint32_t index, const bool started) const
    299     {
    300         hostCallback(started ? VST_HOST_OPCODE_2B : VST_HOST_OPCODE_2C, index);
    301     }
    302 
    303     void setParameterValue(const uint32_t index, const float realValue)
    304     {
    305         const ParameterRanges& ranges(fPlugin->getParameterRanges(index));
    306         const float perValue = ranges.getNormalizedValue(realValue);
    307 
    308         fPlugin->setParameterValue(index, realValue);
    309         hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, perValue);
    310     }
    311 
    312     void setSize(uint width, uint height)
    313     {
    314        #ifdef DISTRHO_OS_MAC
    315         const double scaleFactor = fUI.getScaleFactor();
    316         width /= scaleFactor;
    317         height /= scaleFactor;
    318        #endif
    319         hostCallback(VST_HOST_OPCODE_0F, width, height);
    320     }
    321 
    322    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    323     void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
    324     {
    325         uint8_t midiData[3];
    326         midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel;
    327         midiData[1] = note;
    328         midiData[2] = velocity;
    329         fNotesRingBuffer.writeCustomData(midiData, 3);
    330         fNotesRingBuffer.commitWrite();
    331     }
    332    #endif
    333 
    334    #if DISTRHO_PLUGIN_WANT_STATE
    335     void setState(const char* const key, const char* const value)
    336     {
    337         fUiHelper->setStateFromUI(key, value);
    338     }
    339    #endif
    340 
    341 private:
    342     // Vst stuff
    343     const vst_host_callback fAudioMaster;
    344     vst_effect* const fEffect;
    345     ParameterAndNotesHelper* const fUiHelper;
    346     PluginExporter* const fPlugin;
    347 
    348     // Plugin UI
    349     UIExporter fUI;
    350    #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    351     uint16_t fKeyboardModifiers;
    352    #endif
    353    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    354     RingBufferControl<SmallStackBuffer> fNotesRingBuffer;
    355    #endif
    356 
    357     // ----------------------------------------------------------------------------------------------------------------
    358     // Callbacks
    359 
    360     static void editParameterCallback(void* const ptr, const uint32_t index, const bool started)
    361     {
    362         static_cast<UIVst*>(ptr)->editParameter(index, started);
    363     }
    364 
    365     static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
    366     {
    367         static_cast<UIVst*>(ptr)->setParameterValue(rindex, value);
    368     }
    369 
    370     static void setSizeCallback(void* const ptr, const uint width, const uint height)
    371     {
    372         static_cast<UIVst*>(ptr)->setSize(width, height);
    373     }
    374 
    375    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    376     static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
    377     {
    378         static_cast<UIVst*>(ptr)->sendNote(channel, note, velocity);
    379     }
    380    #endif
    381 
    382    #if DISTRHO_PLUGIN_WANT_STATE
    383     static void setStateCallback(void* const ptr, const char* const key, const char* const value)
    384     {
    385         static_cast<UIVst*>(ptr)->setState(key, value);
    386     }
    387    #endif
    388 };
    389 #endif // DISTRHO_PLUGIN_HAS_UI
    390 
    391 // --------------------------------------------------------------------------------------------------------------------
    392 
    393 class PluginVst : public ParameterAndNotesHelper
    394 {
    395 public:
    396     PluginVst(const vst_host_callback audioMaster, vst_effect* const effect)
    397         : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr),
    398           fAudioMaster(audioMaster),
    399           fEffect(effect)
    400     {
    401         std::memset(fProgramName, 0, sizeof(fProgramName));
    402         std::strcpy(fProgramName, "Default");
    403 
    404         const uint32_t parameterCount = fPlugin.getParameterCount();
    405 
    406         if (parameterCount != 0)
    407         {
    408             parameterValues = new float[parameterCount];
    409 
    410             for (uint32_t i=0; i < parameterCount; ++i)
    411                 parameterValues[i] = NAN;
    412         }
    413 
    414        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    415         fMidiEventCount = 0;
    416        #endif
    417 
    418       #if DISTRHO_PLUGIN_HAS_UI
    419         fVstUI           = nullptr;
    420         fVstRect.top     = 0;
    421         fVstRect.left    = 0;
    422         fVstRect.bottom  = 0;
    423         fVstRect.right   = 0;
    424         fLastScaleFactor = 0.0f;
    425 
    426         if (parameterCount != 0)
    427         {
    428             parameterChecks = new bool[parameterCount];
    429             memset(parameterChecks, 0, sizeof(bool)*parameterCount);
    430         }
    431 
    432       #ifdef DISTRHO_OS_MAC
    433        #ifdef __LP64__
    434         fUsingNsView = true;
    435        #else
    436         #ifndef DISTRHO_NO_WARNINGS
    437          #warning 32bit VST UIs on macOS only work if the host supports "hasCockosViewAsConfig"
    438         #endif
    439         fUsingNsView = false;
    440        #endif
    441       #endif // DISTRHO_OS_MAC
    442 
    443        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    444         fNotesRingBuffer.setRingBuffer(&notesRingBuffer, true);
    445        #endif
    446       #endif // DISTRHO_PLUGIN_HAS_UI
    447 
    448        #if DISTRHO_PLUGIN_WANT_STATE
    449         fStateChunk = nullptr;
    450 
    451         for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
    452         {
    453             const String& dkey(fPlugin.getStateKey(i));
    454             fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
    455         }
    456        #endif
    457     }
    458 
    459     ~PluginVst()
    460     {
    461        #if DISTRHO_PLUGIN_WANT_STATE
    462         if (fStateChunk != nullptr)
    463         {
    464             delete[] fStateChunk;
    465             fStateChunk = nullptr;
    466         }
    467 
    468         fStateMap.clear();
    469        #endif
    470     }
    471 
    472     intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
    473     {
    474        #if DISTRHO_PLUGIN_WANT_STATE
    475         intptr_t ret = 0;
    476        #endif
    477 
    478         switch (opcode)
    479         {
    480         case VST_EFFECT_OPCODE_03: // get program
    481             return 0;
    482 
    483         case VST_EFFECT_OPCODE_04: // set program name
    484             if (char* const programName = (char*)ptr)
    485             {
    486                 d_strncpy(fProgramName, programName, 32);
    487                 return 1;
    488             }
    489             break;
    490 
    491         case VST_EFFECT_OPCODE_05: // get program name
    492             if (char* const programName = (char*)ptr)
    493             {
    494                 d_strncpy(programName, fProgramName, 24);
    495                 return 1;
    496             }
    497             break;
    498 
    499         case VST_EFFECT_OPCODE_1D: // get program name indexed
    500             if (char* const programName = (char*)ptr)
    501             {
    502                 d_strncpy(programName, fProgramName, 24);
    503                 return 1;
    504             }
    505             break;
    506 
    507         case VST_EFFECT_OPCODE_PARAM_GETVALUE:
    508             if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount()))
    509             {
    510                 const uint32_t hints = fPlugin.getParameterHints(index);
    511                 float value = fPlugin.getParameterValue(index);
    512 
    513                 if (hints & kParameterIsBoolean)
    514                 {
    515                     const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
    516                     const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
    517 
    518                     value = value > midRange ? ranges.max : ranges.min;
    519                 }
    520                 else if (hints & kParameterIsInteger)
    521                 {
    522                     value = std::round(value);
    523                 }
    524 
    525                 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
    526 
    527                 for (uint8_t i = 0; i < enumValues.count; ++i)
    528                 {
    529                     if (d_isNotEqual(value, enumValues.values[i].value))
    530                         continue;
    531 
    532                     strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24);
    533                     return 1;
    534                 }
    535 
    536                 if (hints & kParameterIsInteger)
    537                     snprintf_i32((char*)ptr, (int32_t)value, 24);
    538                 else
    539                     snprintf_f32((char*)ptr, value, 24);
    540 
    541                 return 1;
    542             }
    543             break;
    544 
    545         case VST_EFFECT_OPCODE_SET_SAMPLE_RATE:
    546             fPlugin.setSampleRate(opt, true);
    547 
    548            #if DISTRHO_PLUGIN_HAS_UI
    549             if (fVstUI != nullptr)
    550                 fVstUI->setSampleRate(opt);
    551            #endif
    552             break;
    553 
    554         case VST_EFFECT_OPCODE_SET_BLOCK_SIZE:
    555             fPlugin.setBufferSize(value, true);
    556             break;
    557 
    558         case VST_EFFECT_OPCODE_SUSPEND:
    559             if (value != 0)
    560             {
    561                #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    562                 fMidiEventCount = 0;
    563 
    564                 // tell host we want MIDI events
    565                 hostCallback(VST_HOST_OPCODE_06);
    566                #endif
    567 
    568                 // deactivate for possible changes
    569                 fPlugin.deactivateIfNeeded();
    570 
    571                 // check if something changed
    572                 const uint32_t bufferSize = static_cast<uint32_t>(hostCallback(VST_HOST_OPCODE_11));
    573                 const double   sampleRate = static_cast<double>(hostCallback(VST_HOST_OPCODE_10));
    574 
    575                 if (bufferSize != 0)
    576                     fPlugin.setBufferSize(bufferSize, true);
    577 
    578                 if (sampleRate != 0.0)
    579                     fPlugin.setSampleRate(sampleRate, true);
    580 
    581                 fPlugin.activate();
    582             }
    583             else
    584             {
    585                 fPlugin.deactivate();
    586             }
    587             break;
    588 
    589       #if DISTRHO_PLUGIN_HAS_UI
    590         case VST_EFFECT_OPCODE_WINDOW_GETRECT:
    591             if (fVstUI != nullptr)
    592             {
    593                 fVstRect.right  = fVstUI->getWidth();
    594                 fVstRect.bottom = fVstUI->getHeight();
    595                #ifdef DISTRHO_OS_MAC
    596                 const double scaleFactor = fVstUI->getScaleFactor();
    597                 fVstRect.right /= scaleFactor;
    598                 fVstRect.bottom /= scaleFactor;
    599                #endif
    600             }
    601             else
    602             {
    603                 double scaleFactor = fLastScaleFactor;
    604                #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT)
    605                 if (d_isZero(scaleFactor))
    606                     scaleFactor = 1.0;
    607                 fVstRect.right = DISTRHO_UI_DEFAULT_WIDTH * scaleFactor;
    608                 fVstRect.bottom = DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor;
    609                #else
    610                 UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
    611                                  nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
    612                                  fPlugin.getInstancePointer(), scaleFactor);
    613                 fVstRect.right = tmpUI.getWidth();
    614                 fVstRect.bottom = tmpUI.getHeight();
    615                 scaleFactor = tmpUI.getScaleFactor();
    616                 tmpUI.quit();
    617                #endif
    618                #ifdef DISTRHO_OS_MAC
    619                 fVstRect.right /= scaleFactor;
    620                 fVstRect.bottom /= scaleFactor;
    621                #endif
    622             }
    623             *(vst_rect**)ptr = &fVstRect;
    624             return 1;
    625 
    626         case VST_EFFECT_OPCODE_WINDOW_CREATE:
    627             delete fVstUI; // for hosts which don't pair create/destroy calls (Minihost Modular)
    628             fVstUI = nullptr;
    629 
    630            #ifdef DISTRHO_OS_MAC
    631             if (! fUsingNsView)
    632             {
    633                 d_stderr("Host doesn't support hasCockosViewAsConfig, cannot use UI");
    634                 return 0;
    635             }
    636            #endif
    637             fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor);
    638 
    639            #if DISTRHO_PLUGIN_WANT_FULL_STATE
    640             // Update current state from plugin side
    641             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    642             {
    643                 const String& key = cit->first;
    644                 fStateMap[key] = fPlugin.getStateValue(key);
    645             }
    646            #endif
    647 
    648            #if DISTRHO_PLUGIN_WANT_STATE
    649             // Set state
    650             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    651             {
    652                 const String& key(cit->first);
    653                 const String& value(cit->second);
    654 
    655                 // TODO skip DSP only states
    656 
    657                 fVstUI->setStateFromPlugin(key, value);
    658             }
    659            #endif
    660 
    661             for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
    662                 setParameterValueFromPlugin(i, fPlugin.getParameterValue(i));
    663 
    664             fVstUI->idle();
    665             return 1;
    666 
    667         case VST_EFFECT_OPCODE_WINDOW_DESTROY:
    668             if (fVstUI != nullptr)
    669             {
    670                 delete fVstUI;
    671                 fVstUI = nullptr;
    672                 return 1;
    673             }
    674             break;
    675 
    676         case VST_EFFECT_OPCODE_13: // window idle
    677             if (fVstUI != nullptr)
    678                 fVstUI->idle();
    679             break;
    680 
    681        #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    682         case VST_EFFECT_OPCODE_3B: // key down
    683             if (fVstUI != nullptr)
    684                 return fVstUI->handlePluginKeyEvent(true, index, value);
    685             break;
    686 
    687         case VST_EFFECT_OPCODE_3C: // key up
    688             if (fVstUI != nullptr)
    689                 return fVstUI->handlePluginKeyEvent(false, index, value);
    690             break;
    691        #endif
    692       #endif // DISTRHO_PLUGIN_HAS_UI
    693 
    694        #if DISTRHO_PLUGIN_WANT_STATE
    695         case VST_EFFECT_OPCODE_17: // get chunk
    696         {
    697             if (ptr == nullptr)
    698                 return 0;
    699 
    700             if (fStateChunk != nullptr)
    701             {
    702                 delete[] fStateChunk;
    703                 fStateChunk = nullptr;
    704             }
    705 
    706             const uint32_t paramCount = fPlugin.getParameterCount();
    707 
    708             if (fPlugin.getStateCount() == 0 && paramCount == 0)
    709             {
    710                 fStateChunk    = new char[1];
    711                 fStateChunk[0] = '\0';
    712                 ret = 1;
    713             }
    714             else
    715             {
    716                #if DISTRHO_PLUGIN_WANT_FULL_STATE
    717                 // Update current state
    718                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    719                 {
    720                     const String& key = cit->first;
    721                     fStateMap[key] = fPlugin.getStateValue(key);
    722                 }
    723                #endif
    724 
    725                 String chunkStr;
    726 
    727                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    728                 {
    729                     const String& key   = cit->first;
    730                     const String& value = cit->second;
    731 
    732                     // join key and value
    733                     String tmpStr;
    734                     tmpStr  = key;
    735                     tmpStr += "\xff";
    736                     tmpStr += value;
    737                     tmpStr += "\xff";
    738 
    739                     chunkStr += tmpStr;
    740                 }
    741 
    742                 if (paramCount != 0)
    743                 {
    744                     // add another separator
    745                     chunkStr += "\xff";
    746 
    747                     for (uint32_t i=0; i<paramCount; ++i)
    748                     {
    749                         if (fPlugin.isParameterOutputOrTrigger(i))
    750                             continue;
    751 
    752                         // join key and value
    753                         String tmpStr;
    754                         tmpStr  = fPlugin.getParameterSymbol(i);
    755                         tmpStr += "\xff";
    756                         tmpStr += String(fPlugin.getParameterValue(i));
    757                         tmpStr += "\xff";
    758 
    759                         chunkStr += tmpStr;
    760                     }
    761                 }
    762 
    763                 const std::size_t chunkSize = chunkStr.length()+1;
    764 
    765                 fStateChunk = new char[chunkSize];
    766                 std::memcpy(fStateChunk, chunkStr.buffer(), chunkStr.length());
    767                 fStateChunk[chunkSize-1] = '\0';
    768 
    769                 for (std::size_t i=0; i<chunkSize; ++i)
    770                 {
    771                     if (fStateChunk[i] == '\xff')
    772                         fStateChunk[i] = '\0';
    773                 }
    774 
    775                 ret = chunkSize;
    776             }
    777 
    778             *(void**)ptr = fStateChunk;
    779             return ret;
    780         }
    781 
    782         case VST_EFFECT_OPCODE_18: // set chunk
    783         {
    784             if (value <= 1 || ptr == nullptr)
    785                 return 0;
    786 
    787             const size_t chunkSize = static_cast<size_t>(value);
    788 
    789             const char* key   = (const char*)ptr;
    790             const char* value = nullptr;
    791             size_t size, bytesRead = 0;
    792 
    793             while (bytesRead < chunkSize)
    794             {
    795                 if (key[0] == '\0')
    796                     break;
    797 
    798                 size  = std::strlen(key)+1;
    799                 value = key + size;
    800                 bytesRead += size;
    801 
    802                 setStateFromUI(key, value);
    803 
    804                #if DISTRHO_PLUGIN_HAS_UI
    805                 if (fVstUI != nullptr)
    806                 {
    807                     // TODO skip DSP only states
    808                     fVstUI->setStateFromPlugin(key, value);
    809                 }
    810                #endif
    811 
    812                 // get next key
    813                 size = std::strlen(value)+1;
    814                 key  = value + size;
    815                 bytesRead += size;
    816             }
    817 
    818             const uint32_t paramCount = fPlugin.getParameterCount();
    819 
    820             if (bytesRead+4 < chunkSize && paramCount != 0)
    821             {
    822                 ++key;
    823                 float fvalue;
    824 
    825                 while (bytesRead < chunkSize)
    826                 {
    827                     if (key[0] == '\0')
    828                         break;
    829 
    830                     size  = std::strlen(key)+1;
    831                     value = key + size;
    832                     bytesRead += size;
    833 
    834                     // find parameter with this symbol, and set its value
    835                     for (uint32_t i=0; i<paramCount; ++i)
    836                     {
    837                         if (fPlugin.isParameterOutputOrTrigger(i))
    838                             continue;
    839                         if (fPlugin.getParameterSymbol(i) != key)
    840                             continue;
    841 
    842                         if (fPlugin.getParameterHints(i) & kParameterIsInteger)
    843                         {
    844                             fvalue = std::atoi(value);
    845                         }
    846                         else
    847                         {
    848                             const ScopedSafeLocale ssl;
    849                             fvalue = std::atof(value);
    850                         }
    851 
    852                         fPlugin.setParameterValue(i, fvalue);
    853                        #if DISTRHO_PLUGIN_HAS_UI
    854                         if (fVstUI != nullptr)
    855                             setParameterValueFromPlugin(i, fvalue);
    856                        #endif
    857                         break;
    858                     }
    859 
    860                     // get next key
    861                     size = std::strlen(value)+1;
    862                     key  = value + size;
    863                     bytesRead += size;
    864                 }
    865             }
    866 
    867             return 1;
    868         }
    869        #endif // DISTRHO_PLUGIN_WANT_STATE
    870 
    871        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    872         case VST_EFFECT_OPCODE_19: // process events
    873             if (! fPlugin.isActive())
    874             {
    875                 // host has not activated the plugin yet, nasty!
    876                 vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f);
    877             }
    878 
    879             if (const HostVstEvents* const events = (const HostVstEvents*)ptr)
    880             {
    881                 if (events->numEvents == 0)
    882                     break;
    883 
    884                 for (int i=0, count=events->numEvents; i < count; ++i)
    885                 {
    886                     const VstEvent* const vstEvent = events->events[i];
    887 
    888                     if (vstEvent == nullptr)
    889                         break;
    890                     if (vstEvent->type != 1)
    891                         continue;
    892                     if (fMidiEventCount >= kMaxMidiEvents)
    893                         break;
    894 
    895                     const VstMidiEvent& vstMidiEvent(events->events[i]->midi);
    896 
    897                     MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
    898                     midiEvent.frame  = vstMidiEvent.deltaFrames;
    899                     midiEvent.size   = 3;
    900                     std::memcpy(midiEvent.data, vstMidiEvent.midiData, sizeof(uint8_t)*3);
    901                 }
    902             }
    903             break;
    904        #endif
    905 
    906         case VST_EFFECT_OPCODE_PARAM_ISAUTOMATABLE:
    907             if (index < static_cast<int32_t>(fPlugin.getParameterCount()))
    908             {
    909                 const uint32_t hints(fPlugin.getParameterHints(index));
    910 
    911                 // must be automatable, and not output
    912                 if ((hints & kParameterIsAutomatable) != 0 && (hints & kParameterIsOutput) == 0)
    913                     return 1;
    914             }
    915             break;
    916 
    917         case VST_EFFECT_OPCODE_SUPPORTS:
    918             if (const char* const canDo = (const char*)ptr)
    919             {
    920                #if defined(DISTRHO_OS_MAC) && DISTRHO_PLUGIN_HAS_UI
    921                 if (std::strcmp(canDo, "hasCockosViewAsConfig") == 0)
    922                 {
    923                     fUsingNsView = true;
    924                     return 0xbeef0000;
    925                 }
    926                #endif
    927                #ifndef DISTRHO_OS_MAC
    928                 if (std::strcmp(canDo, "supportsViewDpiScaling") == 0)
    929                     return 1;
    930                #endif
    931                 if (std::strcmp(canDo, "receiveVstEvents") == 0 ||
    932                     std::strcmp(canDo, "receiveVstMidiEvent") == 0)
    933                    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    934                     return 1;
    935                    #else
    936                     return -1;
    937                    #endif
    938                 if (std::strcmp(canDo, "sendVstEvents") == 0 ||
    939                     std::strcmp(canDo, "sendVstMidiEvent") == 0)
    940                    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    941                     return 1;
    942                    #else
    943                     return -1;
    944                    #endif
    945                 if (std::strcmp(canDo, "receiveVstTimeInfo") == 0)
    946                    #if DISTRHO_PLUGIN_WANT_TIMEPOS
    947                     return 1;
    948                    #else
    949                     return -1;
    950                    #endif
    951                 if (std::strcmp(canDo, "offline") == 0)
    952                     return -1;
    953             }
    954             break;
    955 
    956         case VST_EFFECT_OPCODE_CUSTOM:
    957            #if DISTRHO_PLUGIN_HAS_UI && !defined(DISTRHO_OS_MAC)
    958             if (index == d_cconst('P', 'r', 'e', 'S') && value == d_cconst('A', 'e', 'C', 's'))
    959             {
    960                 if (d_isEqual(fLastScaleFactor, opt))
    961                     break;
    962 
    963                 fLastScaleFactor = opt;
    964 
    965                 if (fVstUI != nullptr)
    966                     fVstUI->notifyScaleFactorChanged(opt);
    967             }
    968            #endif
    969             break;
    970 
    971         //case effStartProcess:
    972         //case effStopProcess:
    973         // unused
    974         //    break;
    975         }
    976 
    977         return 0;
    978     }
    979 
    980     float vst_getParameter(const uint32_t index)
    981     {
    982         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
    983         return ranges.getNormalizedValue(fPlugin.getParameterValue(index));
    984     }
    985 
    986     void vst_setParameter(const uint32_t index, const float value)
    987     {
    988         const uint32_t hints = fPlugin.getParameterHints(index);
    989         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
    990 
    991         // TODO figure out how to detect kVstParameterUsesIntegerMinMax host support, and skip normalization
    992         float realValue = ranges.getUnnormalizedValue(value);
    993 
    994         if (hints & kParameterIsBoolean)
    995         {
    996             const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
    997             realValue = realValue > midRange ? ranges.max : ranges.min;
    998         }
    999 
   1000         if (hints & kParameterIsInteger)
   1001         {
   1002             realValue = std::round(realValue);
   1003         }
   1004 
   1005         fPlugin.setParameterValue(index, realValue);
   1006 
   1007        #if DISTRHO_PLUGIN_HAS_UI
   1008         if (fVstUI != nullptr)
   1009             setParameterValueFromPlugin(index, realValue);
   1010        #endif
   1011     }
   1012 
   1013     void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames)
   1014     {
   1015         if (! fPlugin.isActive())
   1016         {
   1017             // host has not activated the plugin yet, nasty!
   1018             vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f);
   1019         }
   1020 
   1021         if (sampleFrames <= 0)
   1022         {
   1023             updateParameterOutputsAndTriggers();
   1024             return;
   1025         }
   1026 
   1027        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1028         static constexpr const int kWantVstTimeFlags = 0x2602;
   1029 
   1030         if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(VST_HOST_OPCODE_07, 0, kWantVstTimeFlags))
   1031         {
   1032             fTimePosition.frame   = vstTimeInfo->samplePos;
   1033             fTimePosition.playing = vstTimeInfo->flags & 0x2;
   1034 
   1035             // ticksPerBeat is not possible with VST2
   1036             fTimePosition.bbt.ticksPerBeat = 1920.0;
   1037 
   1038             if (vstTimeInfo->flags & 0x400)
   1039                 fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo;
   1040             else
   1041                 fTimePosition.bbt.beatsPerMinute = 120.0;
   1042 
   1043             if ((vstTimeInfo->flags & 0x2200) == 0x2200)
   1044             {
   1045                 const double ppqPos    = std::abs(vstTimeInfo->ppqPos);
   1046                 const int    ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
   1047                 const double barBeats  = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigNumerator;
   1048                 const double rest      =  std::fmod(barBeats, 1.0);
   1049 
   1050                 fTimePosition.bbt.valid       = true;
   1051                 fTimePosition.bbt.bar         = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
   1052                 fTimePosition.bbt.beat        = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
   1053                 fTimePosition.bbt.tick        = rest * fTimePosition.bbt.ticksPerBeat;
   1054                 fTimePosition.bbt.beatsPerBar = vstTimeInfo->timeSigNumerator;
   1055                 fTimePosition.bbt.beatType    = vstTimeInfo->timeSigDenominator;
   1056 
   1057                 if (vstTimeInfo->ppqPos < 0.0)
   1058                 {
   1059                     --fTimePosition.bbt.bar;
   1060                     fTimePosition.bbt.beat = vstTimeInfo->timeSigNumerator - fTimePosition.bbt.beat + 1;
   1061                     fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1;
   1062                 }
   1063             }
   1064             else
   1065             {
   1066                 fTimePosition.bbt.valid       = false;
   1067                 fTimePosition.bbt.bar         = 1;
   1068                 fTimePosition.bbt.beat        = 1;
   1069                 fTimePosition.bbt.tick        = 0.0;
   1070                 fTimePosition.bbt.beatsPerBar = 4.0f;
   1071                 fTimePosition.bbt.beatType    = 4.0f;
   1072             }
   1073 
   1074             fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
   1075                                              fTimePosition.bbt.beatsPerBar*
   1076                                              (fTimePosition.bbt.bar-1);
   1077 
   1078             fPlugin.setTimePosition(fTimePosition);
   1079         }
   1080        #endif
   1081 
   1082       #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1083        #if DISTRHO_PLUGIN_HAS_UI
   1084         if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
   1085         {
   1086             uint8_t midiData[3];
   1087             const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0;
   1088 
   1089             while (fNotesRingBuffer.isDataAvailableForReading())
   1090             {
   1091                 if (! fNotesRingBuffer.readCustomData(midiData, 3))
   1092                     break;
   1093 
   1094                 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   1095                 midiEvent.frame = frame;
   1096                 midiEvent.size  = 3;
   1097                 std::memcpy(midiEvent.data, midiData, 3);
   1098 
   1099                 if (fMidiEventCount == kMaxMidiEvents)
   1100                     break;
   1101             }
   1102         }
   1103        #endif
   1104 
   1105         fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount);
   1106         fMidiEventCount = 0;
   1107       #else
   1108         fPlugin.run(inputs, outputs, sampleFrames);
   1109       #endif
   1110 
   1111         updateParameterOutputsAndTriggers();
   1112     }
   1113 
   1114     // ----------------------------------------------------------------------------------------------------------------
   1115 
   1116     friend class UIVst;
   1117 
   1118 private:
   1119     // Plugin
   1120     PluginExporter fPlugin;
   1121 
   1122     // VST stuff
   1123     const vst_host_callback fAudioMaster;
   1124     vst_effect* const fEffect;
   1125 
   1126     // Temporary data
   1127     char fProgramName[32];
   1128 
   1129    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1130     uint32_t  fMidiEventCount;
   1131     MidiEvent fMidiEvents[kMaxMidiEvents];
   1132    #endif
   1133 
   1134    #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1135     TimePosition fTimePosition;
   1136    #endif
   1137 
   1138     // UI stuff
   1139   #if DISTRHO_PLUGIN_HAS_UI
   1140     UIVst*   fVstUI;
   1141     vst_rect fVstRect;
   1142     float    fLastScaleFactor;
   1143    #ifdef DISTRHO_OS_MAC
   1144     bool fUsingNsView;
   1145    #endif
   1146    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1147     RingBufferControl<SmallStackBuffer> fNotesRingBuffer;
   1148    #endif
   1149   #endif
   1150 
   1151    #if DISTRHO_PLUGIN_WANT_STATE
   1152     char*     fStateChunk;
   1153     StringMap fStateMap;
   1154    #endif
   1155 
   1156     // ----------------------------------------------------------------------------------------------------------------
   1157     // host callback
   1158 
   1159     intptr_t hostCallback(const VST_HOST_OPCODE opcode,
   1160                           const int32_t index = 0,
   1161                           const intptr_t value = 0,
   1162                           void* const ptr = nullptr,
   1163                           const float opt = 0.0f)
   1164     {
   1165         return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
   1166     }
   1167 
   1168     // ----------------------------------------------------------------------------------------------------------------
   1169     // functions called from the plugin side, RT no block
   1170 
   1171     void updateParameterOutputsAndTriggers()
   1172     {
   1173         float curValue, defValue;
   1174 
   1175         for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
   1176         {
   1177             if (fPlugin.isParameterOutput(i))
   1178             {
   1179                 // NOTE: no output parameter support in VST2, simulate it here
   1180                 curValue = fPlugin.getParameterValue(i);
   1181 
   1182                 if (d_isEqual(curValue, parameterValues[i]))
   1183                     continue;
   1184 
   1185                #if DISTRHO_PLUGIN_HAS_UI
   1186                 if (fVstUI != nullptr)
   1187                     setParameterValueFromPlugin(i, curValue);
   1188                 else
   1189                #endif
   1190                 parameterValues[i] = curValue;
   1191 
   1192                #ifndef DPF_VST_SHOW_PARAMETER_OUTPUTS
   1193                 // skip automating parameter outputs from plugin if we disable them on VST
   1194                 continue;
   1195                #endif
   1196             }
   1197             else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
   1198             {
   1199                 // NOTE: no trigger parameter support in VST2, simulate it here
   1200                 defValue = fPlugin.getParameterDefault(i);
   1201                 curValue = fPlugin.getParameterValue(i);
   1202 
   1203                 if (d_isEqual(curValue, defValue))
   1204                     continue;
   1205 
   1206                #if DISTRHO_PLUGIN_HAS_UI
   1207                 if (fVstUI != nullptr)
   1208                     setParameterValueFromPlugin(i, defValue);
   1209                #endif
   1210                 fPlugin.setParameterValue(i, defValue);
   1211             }
   1212             else
   1213             {
   1214                 continue;
   1215             }
   1216 
   1217             const ParameterRanges& ranges(fPlugin.getParameterRanges(i));
   1218             hostCallback(VST_HOST_OPCODE_00, i, 0, nullptr, ranges.getNormalizedValue(curValue));
   1219         }
   1220 
   1221        #if DISTRHO_PLUGIN_WANT_LATENCY
   1222         fEffect->delay = fPlugin.getLatency();
   1223        #endif
   1224     }
   1225 
   1226    #if DISTRHO_PLUGIN_HAS_UI
   1227     void setParameterValueFromPlugin(const uint32_t index, const float realValue)
   1228     {
   1229         parameterValues[index] = realValue;
   1230         parameterChecks[index] = true;
   1231     }
   1232    #endif
   1233 
   1234    #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
   1235     bool requestParameterValueChange(const uint32_t index, const float value)
   1236     {
   1237         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1238         hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, ranges.getNormalizedValue(value));
   1239         return true;
   1240     }
   1241 
   1242     static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
   1243     {
   1244         return ((PluginVst*)ptr)->requestParameterValueChange(index, value);
   1245     }
   1246    #endif
   1247 
   1248    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1249     bool writeMidi(const MidiEvent& midiEvent)
   1250     {
   1251         if (midiEvent.size > 4)
   1252             return true;
   1253 
   1254         PluginVstEvents vstEvents = {};
   1255         VstMidiEvent vstMidiEvent = {};
   1256 
   1257         vstEvents.numEvents = 1;
   1258         vstEvents.events[0] = (VstEvent*)&vstMidiEvent;
   1259 
   1260         vstMidiEvent.type = 1;
   1261         vstMidiEvent.byteSize    = static_cast<int32_t>(sizeof(VstMidiEvent));
   1262         vstMidiEvent.deltaFrames = midiEvent.frame;
   1263 
   1264         for (uint8_t i=0; i<midiEvent.size; ++i)
   1265             vstMidiEvent.midiData[i] = midiEvent.data[i];
   1266 
   1267         return hostCallback(VST_HOST_OPCODE_08, 0, 0, &vstEvents) == 1;
   1268     }
   1269 
   1270     static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
   1271     {
   1272         return static_cast<PluginVst*>(ptr)->writeMidi(midiEvent);
   1273     }
   1274    #endif
   1275 
   1276   #if DISTRHO_PLUGIN_WANT_STATE
   1277     // ----------------------------------------------------------------------------------------------------------------
   1278     // functions called from the UI side, may block
   1279 
   1280    #if DISTRHO_PLUGIN_HAS_UI
   1281     void setStateFromUI(const char* const key, const char* const value) override
   1282    #else
   1283     void setStateFromUI(const char* const key, const char* const value)
   1284    #endif
   1285     {
   1286         fPlugin.setState(key, value);
   1287 
   1288         // check if we want to save this key
   1289         if (fPlugin.wantStateKey(key))
   1290         {
   1291             const String dkey(key);
   1292             fStateMap[dkey] = value;
   1293         }
   1294     }
   1295   #endif
   1296 };
   1297 
   1298 // --------------------------------------------------------------------------------------------------------------------
   1299 
   1300 struct ExtendedAEffect : vst_effect {
   1301     char _padding[63];
   1302     char valid;
   1303     vst_host_callback audioMaster;
   1304     PluginVst* pluginPtr;
   1305 };
   1306 
   1307 static ScopedPointer<PluginExporter> sPlugin;
   1308 
   1309 static struct Cleanup {
   1310     std::vector<ExtendedAEffect*> effects;
   1311 
   1312     ~Cleanup()
   1313     {
   1314         for (std::vector<ExtendedAEffect*>::iterator it = effects.begin(), end = effects.end(); it != end; ++it)
   1315         {
   1316             ExtendedAEffect* const exteffect = *it;
   1317             delete exteffect->pluginPtr;
   1318             delete exteffect;
   1319         }
   1320 
   1321         sPlugin = nullptr;
   1322     }
   1323 } sCleanup;
   1324 
   1325 // --------------------------------------------------------------------------------------------------------------------
   1326 
   1327 static inline
   1328 ExtendedAEffect* getExtendedEffect(vst_effect* const effect)
   1329 {
   1330     if (effect == nullptr)
   1331         return nullptr;
   1332 
   1333     ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect);
   1334     DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr);
   1335     DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr);
   1336 
   1337     return exteffect;
   1338 }
   1339 
   1340 static inline
   1341 PluginVst* getEffectPlugin(vst_effect* const effect)
   1342 {
   1343     if (effect == nullptr)
   1344         return nullptr;
   1345 
   1346     ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect);
   1347     DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr);
   1348     DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr);
   1349 
   1350     return exteffect->pluginPtr;
   1351 }
   1352 
   1353 // --------------------------------------------------------------------------------------------------------------------
   1354 
   1355 static intptr_t VST_FUNCTION_INTERFACE vst_dispatcherCallback(vst_effect* const effect,
   1356                                                               const VST_EFFECT_OPCODE opcode,
   1357                                                               const int32_t index,
   1358                                                               const intptr_t value,
   1359                                                               void* const ptr,
   1360                                                               const float opt)
   1361 {
   1362     // handle base opcodes
   1363     switch (opcode)
   1364     {
   1365     case VST_EFFECT_OPCODE_CREATE:
   1366         if (ExtendedAEffect* const exteffect = getExtendedEffect(effect))
   1367         {
   1368             // some hosts call open/create twice
   1369             if (exteffect->pluginPtr != nullptr)
   1370                 return 1;
   1371 
   1372             const vst_host_callback audioMaster = exteffect->audioMaster;
   1373 
   1374             d_nextBufferSize = audioMaster(effect, VST_HOST_OPCODE_11, 0, 0, nullptr, 0.0f);
   1375             d_nextSampleRate = audioMaster(effect, VST_HOST_OPCODE_10, 0, 0, nullptr, 0.0f);
   1376             d_nextCanRequestParameterValueChanges = true;
   1377 
   1378             // some hosts are not ready at this point or return 0 buffersize/samplerate
   1379             if (d_nextBufferSize == 0)
   1380                 d_nextBufferSize = 2048;
   1381             if (d_nextSampleRate <= 0.0)
   1382                 d_nextSampleRate = 44100.0;
   1383 
   1384             exteffect->pluginPtr = new PluginVst(audioMaster, effect);
   1385             return 1;
   1386         }
   1387         return 0;
   1388 
   1389     case VST_EFFECT_OPCODE_DESTROY:
   1390         if (ExtendedAEffect* const exteffect = getExtendedEffect(effect))
   1391         {
   1392             // delete plugin object
   1393             if (exteffect->pluginPtr != nullptr)
   1394             {
   1395                 delete exteffect->pluginPtr;
   1396                 exteffect->pluginPtr = nullptr;
   1397             }
   1398 
   1399             // delete effect too, if it comes from us
   1400             const std::vector<ExtendedAEffect*>::iterator it = std::find(sCleanup.effects.begin(), sCleanup.effects.end(), exteffect);
   1401             if (it != sCleanup.effects.end())
   1402             {
   1403                 delete exteffect;
   1404                 sCleanup.effects.erase(it);
   1405             }
   1406 
   1407             // delete global plugin instance too if this is the last loaded effect
   1408             if (sCleanup.effects.empty())
   1409                 sPlugin = nullptr;
   1410             return 1;
   1411         }
   1412         return 0;
   1413 
   1414     case VST_EFFECT_OPCODE_PARAM_GETLABEL:
   1415         if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount()))
   1416         {
   1417             d_strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8);
   1418             return 1;
   1419         }
   1420         return 0;
   1421 
   1422     case VST_EFFECT_OPCODE_PARAM_GETNAME:
   1423         if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount()))
   1424         {
   1425             const String& shortName(sPlugin->getParameterShortName(index));
   1426             if (shortName.isNotEmpty())
   1427                 d_strncpy((char*)ptr, shortName, 16);
   1428             else
   1429                 d_strncpy((char*)ptr, sPlugin->getParameterName(index), 16);
   1430             return 1;
   1431         }
   1432         return 0;
   1433 
   1434     case VST_EFFECT_OPCODE_38: // FIXME VST_EFFECT_OPCODE_GET_PARAMETER_PROPERTIES is wrong by 1
   1435         if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount()))
   1436         {
   1437             if (vst_parameter_properties* const properties = (vst_parameter_properties*)ptr)
   1438             {
   1439                 memset(properties, 0, sizeof(vst_parameter_properties));
   1440 
   1441                 // full name
   1442                 d_strncpy(properties->name,
   1443                           sPlugin->getParameterName(index),
   1444                           sizeof(properties->name));
   1445 
   1446                 // short name
   1447                 const String& shortName(sPlugin->getParameterShortName(index));
   1448 
   1449                 if (shortName.isNotEmpty())
   1450                     d_strncpy(properties->label,
   1451                               sPlugin->getParameterShortName(index),
   1452                               sizeof(properties->label));
   1453 
   1454                 // parameter hints
   1455                 const uint32_t hints = sPlugin->getParameterHints(index);
   1456 
   1457                 if (hints & kParameterIsOutput)
   1458                     return 1;
   1459 
   1460                 if (hints & kParameterIsBoolean)
   1461                 {
   1462                     properties->flags |= VST_PARAMETER_FLAGS_SWITCH;
   1463                 }
   1464 
   1465                 if (hints & kParameterIsInteger)
   1466                 {
   1467                     const ParameterRanges& ranges(sPlugin->getParameterRanges(index));
   1468                     properties->flags |= VST_PARAMETER_FLAGS_INTEGER_LIMITS;
   1469                     properties->min_value_i32 = static_cast<int32_t>(ranges.min);
   1470                     properties->max_value_i32 = static_cast<int32_t>(ranges.max);
   1471                 }
   1472 
   1473                 if (hints & kParameterIsLogarithmic)
   1474                 {
   1475                     properties->flags |= VST_PARAMETER_FLAGS_UNKNOWN6; // can ramp
   1476                 }
   1477 
   1478                 // parameter group (category in vst)
   1479                 const uint32_t groupId = sPlugin->getParameterGroupId(index);
   1480 
   1481                 if (groupId != kPortGroupNone)
   1482                 {
   1483                     // we can't use groupId directly, so use the index array where this group is stored in
   1484                     for (uint32_t i=0, count=sPlugin->getPortGroupCount(); i < count; ++i)
   1485                     {
   1486                         const PortGroupWithId& portGroup(sPlugin->getPortGroupByIndex(i));
   1487 
   1488                         if (portGroup.groupId == groupId)
   1489                         {
   1490                             properties->flags |= VST_PARAMETER_FLAGS_CATEGORY;
   1491                             properties->category = i + 1;
   1492                             d_strncpy(properties->category_label,
   1493                                       portGroup.name.buffer(),
   1494                                       sizeof(properties->category_label));
   1495                             break;
   1496                         }
   1497                     }
   1498 
   1499                     if (properties->category != 0)
   1500                     {
   1501                         for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i)
   1502                             if (sPlugin->getParameterGroupId(i) == groupId)
   1503                                 ++properties->num_parameters_in_category;
   1504                     }
   1505                 }
   1506 
   1507                 return 1;
   1508             }
   1509         }
   1510         return 0;
   1511 
   1512     case VST_EFFECT_OPCODE_EFFECT_CATEGORY:
   1513        #if DISTRHO_PLUGIN_IS_SYNTH
   1514         return VST_CATEGORY_02;
   1515        #else
   1516         return VST_CATEGORY_01;
   1517        #endif
   1518 
   1519     case VST_EFFECT_OPCODE_EFFECT_NAME:
   1520         if (char* const cptr = (char*)ptr)
   1521         {
   1522             d_strncpy(cptr, sPlugin->getName(), 32);
   1523             return 1;
   1524         }
   1525         return 0;
   1526 
   1527     case VST_EFFECT_OPCODE_VENDOR_NAME:
   1528         if (char* const cptr = (char*)ptr)
   1529         {
   1530             d_strncpy(cptr, sPlugin->getMaker(), 32);
   1531             return 1;
   1532         }
   1533         return 0;
   1534 
   1535     case VST_EFFECT_OPCODE_PRODUCT_NAME:
   1536         if (char* const cptr = (char*)ptr)
   1537         {
   1538             d_strncpy(cptr, sPlugin->getLabel(), 32);
   1539             return 1;
   1540         }
   1541         return 0;
   1542 
   1543     case VST_EFFECT_OPCODE_VENDOR_VERSION:
   1544         return sPlugin->getVersion();
   1545 
   1546     case VST_EFFECT_OPCODE_VST_VERSION:
   1547         return VST_VERSION_2_4_0_0;
   1548 
   1549     default:
   1550         break;
   1551     }
   1552 
   1553     // handle advanced opcodes
   1554     if (PluginVst* const pluginPtr = getEffectPlugin(effect))
   1555         return pluginPtr->vst_dispatcher(opcode, index, value, ptr, opt);
   1556 
   1557     return 0;
   1558 }
   1559 
   1560 static float VST_FUNCTION_INTERFACE vst_getParameterCallback(vst_effect* const effect,
   1561                                                              const uint32_t index)
   1562 {
   1563     if (PluginVst* const pluginPtr = getEffectPlugin(effect))
   1564         return pluginPtr->vst_getParameter(index);
   1565     return 0.0f;
   1566 }
   1567 
   1568 static void VST_FUNCTION_INTERFACE vst_setParameterCallback(vst_effect* const effect,
   1569                                                             const uint32_t index,
   1570                                                             const float value)
   1571 {
   1572     if (PluginVst* const pluginPtr = getEffectPlugin(effect))
   1573         pluginPtr->vst_setParameter(index, value);
   1574 }
   1575 
   1576 static void VST_FUNCTION_INTERFACE vst_processCallback(vst_effect* const effect,
   1577                                                        const float* const* const inputs,
   1578                                                        float** const outputs,
   1579                                                        const int32_t sampleFrames)
   1580 {
   1581     if (PluginVst* const pluginPtr = getEffectPlugin(effect))
   1582         pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
   1583 }
   1584 
   1585 static void VST_FUNCTION_INTERFACE vst_processReplacingCallback(vst_effect* const effect,
   1586                                                                 const float* const* const inputs,
   1587                                                                 float** const outputs,
   1588                                                                 const int32_t sampleFrames)
   1589 {
   1590     if (PluginVst* const pluginPtr = getEffectPlugin(effect))
   1591         pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
   1592 }
   1593 
   1594 // --------------------------------------------------------------------------------------------------------------------
   1595 
   1596 END_NAMESPACE_DISTRHO
   1597 
   1598 DISTRHO_PLUGIN_EXPORT
   1599 const vst_effect* VSTPluginMain(vst_host_callback);
   1600 
   1601 DISTRHO_PLUGIN_EXPORT
   1602 const vst_effect* VSTPluginMain(const vst_host_callback audioMaster)
   1603 {
   1604     USE_NAMESPACE_DISTRHO
   1605 
   1606     // old version
   1607     if (audioMaster(nullptr, VST_HOST_OPCODE_01 /* version */, 0, 0, nullptr, 0.0f) == 0)
   1608         return nullptr;
   1609 
   1610     // find plugin bundle
   1611     static String bundlePath;
   1612     if (bundlePath.isEmpty())
   1613     {
   1614         String tmpPath(getBinaryFilename());
   1615         tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   1616        #ifdef DISTRHO_OS_MAC
   1617         if (tmpPath.endsWith("/MacOS"))
   1618         {
   1619             tmpPath.truncate(tmpPath.rfind('/'));
   1620             if (tmpPath.endsWith("/Contents"))
   1621             {
   1622                 tmpPath.truncate(tmpPath.rfind('/'));
   1623                 bundlePath = tmpPath;
   1624                 d_nextBundlePath = bundlePath.buffer();
   1625             }
   1626         }
   1627        #else
   1628         if (tmpPath.endsWith(".vst"))
   1629         {
   1630             bundlePath = tmpPath;
   1631             d_nextBundlePath = bundlePath.buffer();
   1632         }
   1633        #endif
   1634     }
   1635 
   1636     // first internal init
   1637     if (sPlugin == nullptr)
   1638     {
   1639         // set valid but dummy values
   1640         d_nextBufferSize = 512;
   1641         d_nextSampleRate = 44100.0;
   1642         d_nextPluginIsDummy = true;
   1643         d_nextCanRequestParameterValueChanges = true;
   1644 
   1645         // Create dummy plugin to get data from
   1646         sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
   1647 
   1648         // unset
   1649         d_nextBufferSize = 0;
   1650         d_nextSampleRate = 0.0;
   1651         d_nextPluginIsDummy = false;
   1652         d_nextCanRequestParameterValueChanges = false;
   1653     }
   1654 
   1655     ExtendedAEffect* const effect = new ExtendedAEffect;
   1656     std::memset(effect, 0, sizeof(ExtendedAEffect));
   1657 
   1658     // vst fields
   1659    #ifdef WORDS_BIGENDIAN
   1660     effect->magic_number = 0x50747356;
   1661    #else
   1662     effect->magic_number = 0x56737450;
   1663    #endif
   1664     effect->unique_id    = sPlugin->getUniqueId();
   1665     effect->version      = sPlugin->getVersion();
   1666 
   1667     // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default.
   1668    #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS
   1669     const int numParams = sPlugin->getParameterCount();
   1670    #else
   1671     int numParams = 0;
   1672     bool outputsReached = false;
   1673 
   1674     for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i)
   1675     {
   1676         if (sPlugin->isParameterInput(i))
   1677         {
   1678             // parameter outputs must be all at the end
   1679             DISTRHO_SAFE_ASSERT_BREAK(! outputsReached);
   1680             ++numParams;
   1681             continue;
   1682         }
   1683         outputsReached = true;
   1684     }
   1685    #endif
   1686 
   1687     // plugin fields
   1688     effect->num_params   = numParams;
   1689     effect->num_programs = 1;
   1690     effect->num_inputs   = DISTRHO_PLUGIN_NUM_INPUTS;
   1691     effect->num_outputs  = DISTRHO_PLUGIN_NUM_OUTPUTS;
   1692 
   1693     // plugin flags
   1694     effect->flags |= 1 << 4; // uses process_float
   1695    #if DISTRHO_PLUGIN_IS_SYNTH
   1696     effect->flags |= 1 << 8;
   1697    #endif
   1698    #if DISTRHO_PLUGIN_HAS_UI
   1699     effect->flags |= 1 << 0;
   1700    #endif
   1701    #if DISTRHO_PLUGIN_WANT_STATE
   1702     effect->flags |= 1 << 5;
   1703    #endif
   1704 
   1705     // static calls
   1706     effect->control       = vst_dispatcherCallback;
   1707     effect->process       = vst_processCallback;
   1708     effect->get_parameter = vst_getParameterCallback;
   1709     effect->set_parameter = vst_setParameterCallback;
   1710     effect->process_float = vst_processReplacingCallback;
   1711 
   1712     // special values
   1713     effect->valid       = 101;
   1714     effect->audioMaster = audioMaster;
   1715     effect->pluginPtr   = nullptr;
   1716 
   1717     // done
   1718     sCleanup.effects.push_back(effect);
   1719 
   1720     return effect;
   1721 }
   1722 
   1723 #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_PLUGIN_WANT_WEBVIEW)
   1724 DISTRHO_PLUGIN_EXPORT
   1725 const vst_effect* VSTPluginMainCompat(vst_host_callback) asm ("main");
   1726 
   1727 DISTRHO_PLUGIN_EXPORT
   1728 const vst_effect* VSTPluginMainCompat(const vst_host_callback audioMaster)
   1729 {
   1730     // protect main symbol against running as executable
   1731     if (reinterpret_cast<uintptr_t>(audioMaster) < 0xff)
   1732         return nullptr;
   1733 
   1734     return VSTPluginMain(audioMaster);
   1735 }
   1736 #endif
   1737 
   1738 // --------------------------------------------------------------------------------------------------------------------