DPF

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

DistrhoPluginCLAP.cpp (87589B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 /* TODO items:
     18  * CV: write a specification
     19  * INFO: define url, manual url, support url and string version
     20  * PARAMETERS: test parameter triggers
     21  * States: skip DSP/UI only states as appropriate
     22  * UI: expose external-only UIs
     23  */
     24 
     25 #include "DistrhoPluginInternal.hpp"
     26 #include "extra/ScopedPointer.hpp"
     27 
     28 #ifndef DISTRHO_PLUGIN_CLAP_ID
     29 # error DISTRHO_PLUGIN_CLAP_ID undefined!
     30 #endif
     31 
     32 #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
     33 # undef DISTRHO_PLUGIN_HAS_UI
     34 # define DISTRHO_PLUGIN_HAS_UI 0
     35 #endif
     36 
     37 #if DISTRHO_PLUGIN_HAS_UI
     38 # include "DistrhoUIInternal.hpp"
     39 # include "../extra/Mutex.hpp"
     40 #endif
     41 
     42 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
     43 # include "../extra/RingBuffer.hpp"
     44 #endif
     45 
     46 #include <map>
     47 #include <vector>
     48 
     49 #include "clap/entry.h"
     50 #include "clap/plugin-factory.h"
     51 #include "clap/ext/audio-ports.h"
     52 #include "clap/ext/latency.h"
     53 #include "clap/ext/gui.h"
     54 #include "clap/ext/note-ports.h"
     55 #include "clap/ext/params.h"
     56 #include "clap/ext/state.h"
     57 #include "clap/ext/thread-check.h"
     58 #include "clap/ext/timer-support.h"
     59 
     60 #if (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
     61 # define DPF_CLAP_USING_HOST_TIMER 0
     62 #else
     63 # define DPF_CLAP_USING_HOST_TIMER 1
     64 #endif
     65 
     66 #ifndef DPF_CLAP_TIMER_INTERVAL
     67 # define DPF_CLAP_TIMER_INTERVAL 16 /* ~60 fps */
     68 #endif
     69 
     70 START_NAMESPACE_DISTRHO
     71 
     72 // --------------------------------------------------------------------------------------------------------------------
     73 
     74 typedef std::map<const String, String> StringMap;
     75 
     76 struct ClapEventQueue
     77 {
     78   #if DISTRHO_PLUGIN_HAS_UI
     79     enum EventType {
     80         kEventGestureBegin,
     81         kEventGestureEnd,
     82         kEventParamSet
     83     };
     84 
     85     struct Event {
     86         EventType type;
     87         uint32_t index;
     88         float value;
     89     };
     90 
     91     struct Queue {
     92         RecursiveMutex lock;
     93         uint allocated;
     94         uint used;
     95         Event* events;
     96 
     97         Queue()
     98             : allocated(0),
     99               used(0),
    100               events(nullptr) {}
    101 
    102         ~Queue()
    103         {
    104             delete[] events;
    105         }
    106 
    107         void addEventFromUI(const Event& event)
    108         {
    109             const RecursiveMutexLocker crml(lock);
    110 
    111             if (events == nullptr)
    112             {
    113                 events = static_cast<Event*>(std::malloc(sizeof(Event) * 8));
    114                 allocated = 8;
    115             }
    116             else if (used + 1 > allocated)
    117             {
    118                 allocated = used * 2;
    119                 events = static_cast<Event*>(std::realloc(events, sizeof(Event) * allocated));
    120             }
    121 
    122             std::memcpy(&events[used++], &event, sizeof(Event));
    123         }
    124     } fEventQueue;
    125 
    126    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    127     SmallStackBuffer fNotesBuffer;
    128    #endif
    129   #endif
    130 
    131    #if DISTRHO_PLUGIN_WANT_PROGRAMS
    132     uint32_t fCurrentProgram;
    133    #endif
    134 
    135   #if DISTRHO_PLUGIN_WANT_STATE
    136     StringMap fStateMap;
    137    #if DISTRHO_PLUGIN_HAS_UI
    138     virtual void setStateFromUI(const char* key, const char* value) = 0;
    139    #endif
    140   #endif
    141 
    142     struct CachedParameters {
    143         uint numParams;
    144         bool* changed;
    145         float* values;
    146 
    147         CachedParameters()
    148             : numParams(0),
    149               changed(nullptr),
    150               values(nullptr) {}
    151 
    152         ~CachedParameters()
    153         {
    154             delete[] changed;
    155             delete[] values;
    156         }
    157 
    158         void setup(const uint numParameters)
    159         {
    160             if (numParameters == 0)
    161                 return;
    162 
    163             numParams = numParameters;
    164             changed = new bool[numParameters];
    165             values = new float[numParameters];
    166 
    167             std::memset(changed, 0, sizeof(bool)*numParameters);
    168             std::memset(values, 0, sizeof(float)*numParameters);
    169         }
    170     } fCachedParameters;
    171 
    172     ClapEventQueue()
    173        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
    174         : fNotesBuffer(StackBuffer_INIT)
    175        #endif
    176     {
    177        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT)
    178         std::memset(&fNotesBuffer, 0, sizeof(fNotesBuffer));
    179        #endif
    180        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    181         fCurrentProgram = 0;
    182        #endif
    183     }
    184 
    185     virtual ~ClapEventQueue() {}
    186 };
    187 
    188 // --------------------------------------------------------------------------------------------------------------------
    189 
    190 #if DISTRHO_PLUGIN_HAS_UI
    191 
    192 #if ! DISTRHO_PLUGIN_WANT_STATE
    193 static constexpr const setStateFunc setStateCallback = nullptr;
    194 #endif
    195 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
    196 static constexpr const sendNoteFunc sendNoteCallback = nullptr;
    197 #endif
    198 
    199 /**
    200  * CLAP UI class.
    201  */
    202 class ClapUI : public DGL_NAMESPACE::IdleCallback
    203 {
    204 public:
    205     ClapUI(PluginExporter& plugin,
    206            ClapEventQueue* const eventQueue,
    207            const clap_host_t* const host,
    208            const clap_host_gui_t* const hostGui,
    209           #if DPF_CLAP_USING_HOST_TIMER
    210            const clap_host_timer_support_t* const hostTimer,
    211           #endif
    212            const bool isFloating)
    213         : fPlugin(plugin),
    214          #if DISTRHO_PLUGIN_WANT_STATE
    215           fPluginEventQueue(eventQueue),
    216          #endif
    217           fEventQueue(eventQueue->fEventQueue),
    218           fCachedParameters(eventQueue->fCachedParameters),
    219          #if DISTRHO_PLUGIN_WANT_PROGRAMS
    220           fCurrentProgram(eventQueue->fCurrentProgram),
    221          #endif
    222          #if DISTRHO_PLUGIN_WANT_STATE
    223           fStateMap(eventQueue->fStateMap),
    224          #endif
    225           fHost(host),
    226           fHostGui(hostGui),
    227          #if DPF_CLAP_USING_HOST_TIMER
    228           fTimerId(0),
    229           fHostTimer(hostTimer),
    230          #else
    231           fCallbackRegistered(false),
    232          #endif
    233           fIsFloating(isFloating),
    234           fScaleFactor(0.0),
    235           fParentWindow(0),
    236           fTransientWindow(0)
    237     {
    238        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    239         fNotesRingBuffer.setRingBuffer(&eventQueue->fNotesBuffer, false);
    240        #endif
    241     }
    242 
    243     ~ClapUI() override
    244     {
    245        #if DPF_CLAP_USING_HOST_TIMER
    246         if (fTimerId != 0)
    247             fHostTimer->unregister_timer(fHost, fTimerId);
    248        #else
    249         if (fCallbackRegistered && fUI != nullptr)
    250             fUI->removeIdleCallbackForNativeIdle(this);
    251        #endif
    252     }
    253 
    254    #ifndef DISTRHO_OS_MAC
    255     bool setScaleFactor(const double scaleFactor)
    256     {
    257         if (d_isEqual(fScaleFactor, scaleFactor))
    258             return true;
    259 
    260         fScaleFactor = scaleFactor;
    261 
    262         if (UIExporter* const ui = fUI.get())
    263             ui->notifyScaleFactorChanged(scaleFactor);
    264 
    265         return true;
    266     }
    267    #endif
    268 
    269     bool getSize(uint32_t* const width, uint32_t* const height) const
    270     {
    271         if (UIExporter* const ui = fUI.get())
    272         {
    273             *width = ui->getWidth();
    274             *height = ui->getHeight();
    275            #ifdef DISTRHO_OS_MAC
    276             const double scaleFactor = ui->getScaleFactor();
    277             *width /= scaleFactor;
    278             *height /= scaleFactor;
    279            #endif
    280             return true;
    281         }
    282 
    283         double scaleFactor = fScaleFactor;
    284        #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT)
    285         if (d_isZero(scaleFactor))
    286             scaleFactor = 1.0;
    287         *width = DISTRHO_UI_DEFAULT_WIDTH * scaleFactor;
    288         *height = DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor;
    289        #else
    290         UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
    291                          nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
    292                          fPlugin.getInstancePointer(), scaleFactor);
    293         *width = tmpUI.getWidth();
    294         *height = tmpUI.getHeight();
    295         scaleFactor = tmpUI.getScaleFactor();
    296         tmpUI.quit();
    297        #endif
    298 
    299        #ifdef DISTRHO_OS_MAC
    300         *width /= scaleFactor;
    301         *height /= scaleFactor;
    302        #endif
    303 
    304         return true;
    305     }
    306 
    307     bool canResize() const noexcept
    308     {
    309        #if DISTRHO_UI_USER_RESIZABLE
    310         if (UIExporter* const ui = fUI.get())
    311             return ui->isResizable();
    312        #endif
    313         return false;
    314     }
    315 
    316     bool getResizeHints(clap_gui_resize_hints_t* const hints) const
    317     {
    318         if (canResize())
    319         {
    320             uint minimumWidth, minimumHeight;
    321             bool keepAspectRatio;
    322             fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
    323 
    324            #ifdef DISTRHO_OS_MAC
    325             const double scaleFactor = fUI->getScaleFactor();
    326             minimumWidth /= scaleFactor;
    327             minimumHeight /= scaleFactor;
    328            #endif
    329 
    330             hints->can_resize_horizontally = true;
    331             hints->can_resize_vertically = true;
    332             hints->preserve_aspect_ratio = keepAspectRatio;
    333             hints->aspect_ratio_width = minimumWidth;
    334             hints->aspect_ratio_height = minimumHeight;
    335 
    336             return true;
    337         }
    338 
    339         hints->can_resize_horizontally = false;
    340         hints->can_resize_vertically = false;
    341         hints->preserve_aspect_ratio = false;
    342         hints->aspect_ratio_width = 0;
    343         hints->aspect_ratio_height = 0;
    344 
    345         return false;
    346     }
    347 
    348     bool adjustSize(uint32_t* const width, uint32_t* const height) const
    349     {
    350         if (canResize())
    351         {
    352             uint minimumWidth, minimumHeight;
    353             bool keepAspectRatio;
    354             fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
    355 
    356            #ifdef DISTRHO_OS_MAC
    357             const double scaleFactor = fUI->getScaleFactor();
    358             minimumWidth /= scaleFactor;
    359             minimumHeight /= scaleFactor;
    360            #endif
    361 
    362             if (keepAspectRatio)
    363             {
    364                 if (*width < 1)
    365                     *width = 1;
    366                 if (*height < 1)
    367                     *height = 1;
    368 
    369                 const double ratio = static_cast<double>(minimumWidth) / static_cast<double>(minimumHeight);
    370                 const double reqRatio = static_cast<double>(*width) / static_cast<double>(*height);
    371 
    372                 if (d_isNotEqual(ratio, reqRatio))
    373                 {
    374                     // fix width
    375                     if (reqRatio > ratio)
    376                         *width = d_roundToIntPositive(*height * ratio);
    377                     // fix height
    378                     else
    379                         *height = d_roundToIntPositive(static_cast<double>(*width) / ratio);
    380                 }
    381             }
    382 
    383             if (minimumWidth > *width)
    384                 *width = minimumWidth;
    385             if (minimumHeight > *height)
    386                 *height = minimumHeight;
    387 
    388             return true;
    389         }
    390 
    391         return false;
    392     }
    393 
    394     bool setSizeFromHost(uint32_t width, uint32_t height)
    395     {
    396         if (UIExporter* const ui = fUI.get())
    397         {
    398            #ifdef DISTRHO_OS_MAC
    399             const double scaleFactor = ui->getScaleFactor();
    400             width *= scaleFactor;
    401             height *= scaleFactor;
    402            #endif
    403             ui->setWindowSizeFromHost(width, height);
    404             return true;
    405         }
    406 
    407         return false;
    408     }
    409 
    410     bool setParent(const clap_window_t* const window)
    411     {
    412         if (fIsFloating)
    413             return false;
    414 
    415         fParentWindow = window->uptr;
    416 
    417         if (fUI == nullptr)
    418         {
    419             createUI();
    420             fHostGui->resize_hints_changed(fHost);
    421         }
    422 
    423         return true;
    424     }
    425 
    426     bool setTransient(const clap_window_t* const window)
    427     {
    428         if (! fIsFloating)
    429             return false;
    430 
    431         fTransientWindow = window->uptr;
    432 
    433         if (UIExporter* const ui = fUI.get())
    434             ui->setWindowTransientWinId(window->uptr);
    435 
    436         return true;
    437     }
    438 
    439     void suggestTitle(const char* const title)
    440     {
    441         if (! fIsFloating)
    442             return;
    443 
    444         fWindowTitle = title;
    445 
    446         if (UIExporter* const ui = fUI.get())
    447             ui->setWindowTitle(title);
    448     }
    449 
    450     bool show()
    451     {
    452         if (fUI == nullptr)
    453         {
    454             createUI();
    455             fHostGui->resize_hints_changed(fHost);
    456         }
    457 
    458         if (fIsFloating)
    459             fUI->setWindowVisible(true);
    460 
    461        #if DPF_CLAP_USING_HOST_TIMER
    462         fHostTimer->register_timer(fHost, DPF_CLAP_TIMER_INTERVAL, &fTimerId);
    463        #else
    464         fCallbackRegistered = true;
    465         fUI->addIdleCallbackForNativeIdle(this, DPF_CLAP_TIMER_INTERVAL);
    466        #endif
    467         return true;
    468     }
    469 
    470     bool hide()
    471     {
    472         if (UIExporter* const ui = fUI.get())
    473         {
    474             ui->setWindowVisible(false);
    475            #if DPF_CLAP_USING_HOST_TIMER
    476             fHostTimer->unregister_timer(fHost, fTimerId);
    477             fTimerId = 0;
    478            #else
    479             ui->removeIdleCallbackForNativeIdle(this);
    480             fCallbackRegistered = false;
    481            #endif
    482         }
    483 
    484         return true;
    485     }
    486 
    487     // ----------------------------------------------------------------------------------------------------------------
    488 
    489     void idleCallback() override
    490     {
    491         if (UIExporter* const ui = fUI.get())
    492         {
    493            #if DPF_CLAP_USING_HOST_TIMER
    494             ui->plugin_idle();
    495            #else
    496             ui->idleFromNativeIdle();
    497            #endif
    498 
    499             for (uint i=0; i<fCachedParameters.numParams; ++i)
    500             {
    501                 if (fCachedParameters.changed[i])
    502                 {
    503                     fCachedParameters.changed[i] = false;
    504                     ui->parameterChanged(i, fCachedParameters.values[i]);
    505                 }
    506             }
    507         }
    508     }
    509 
    510     // ----------------------------------------------------------------------------------------------------------------
    511 
    512     void setParameterValueFromPlugin(const uint index, const float value)
    513     {
    514         if (UIExporter* const ui = fUI.get())
    515             ui->parameterChanged(index, value);
    516     }
    517 
    518    #if DISTRHO_PLUGIN_WANT_PROGRAMS
    519     void setProgramFromPlugin(const uint index)
    520     {
    521         if (UIExporter* const ui = fUI.get())
    522             ui->programLoaded(index);
    523     }
    524    #endif
    525 
    526    #if DISTRHO_PLUGIN_WANT_STATE
    527     void setStateFromPlugin(const char* const key, const char* const value)
    528     {
    529         if (UIExporter* const ui = fUI.get())
    530             ui->stateChanged(key, value);
    531     }
    532    #endif
    533 
    534     // ----------------------------------------------------------------------------------------------------------------
    535 
    536 private:
    537     // Plugin and UI
    538     PluginExporter& fPlugin;
    539    #if DISTRHO_PLUGIN_WANT_STATE
    540     ClapEventQueue* const fPluginEventQueue;
    541    #endif
    542     ClapEventQueue::Queue& fEventQueue;
    543     ClapEventQueue::CachedParameters& fCachedParameters;
    544    #if DISTRHO_PLUGIN_WANT_PROGRAMS
    545     uint32_t& fCurrentProgram;
    546    #endif
    547    #if DISTRHO_PLUGIN_WANT_STATE
    548     StringMap& fStateMap;
    549    #endif
    550     const clap_host_t* const fHost;
    551     const clap_host_gui_t* const fHostGui;
    552    #if DPF_CLAP_USING_HOST_TIMER
    553     clap_id fTimerId;
    554     const clap_host_timer_support_t* const fHostTimer;
    555    #else
    556     bool fCallbackRegistered;
    557    #endif
    558    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    559     RingBufferControl<SmallStackBuffer> fNotesRingBuffer;
    560    #endif
    561     ScopedPointer<UIExporter> fUI;
    562 
    563     const bool fIsFloating;
    564 
    565     // Temporary data
    566     double fScaleFactor;
    567     uintptr_t fParentWindow;
    568     uintptr_t fTransientWindow;
    569     String fWindowTitle;
    570 
    571     // ----------------------------------------------------------------------------------------------------------------
    572 
    573     void createUI()
    574     {
    575         DISTRHO_SAFE_ASSERT_RETURN(fUI == nullptr,);
    576 
    577         fUI = new UIExporter(this,
    578                              fParentWindow,
    579                              fPlugin.getSampleRate(),
    580                              editParameterCallback,
    581                              setParameterCallback,
    582                              setStateCallback,
    583                              sendNoteCallback,
    584                              setSizeCallback,
    585                              nullptr, // TODO fileRequestCallback,
    586                              d_nextBundlePath,
    587                              fPlugin.getInstancePointer(),
    588                              fScaleFactor);
    589 
    590        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    591         fUI->programLoaded(fCurrentProgram);
    592        #endif
    593 
    594        #if DISTRHO_PLUGIN_WANT_FULL_STATE
    595         // Update current state from plugin side
    596         for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    597         {
    598             const String& key = cit->first;
    599             fStateMap[key] = fPlugin.getStateValue(key);
    600         }
    601        #endif
    602 
    603        #if DISTRHO_PLUGIN_WANT_STATE
    604         // Set state
    605         for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
    606         {
    607             const String& key(cit->first);
    608             const String& value(cit->second);
    609 
    610             // TODO skip DSP only states
    611 
    612             fUI->stateChanged(key, value);
    613         }
    614        #endif
    615 
    616         for (uint32_t i=0; i<fCachedParameters.numParams; ++i)
    617         {
    618             const float value = fCachedParameters.values[i] = fPlugin.getParameterValue(i);
    619             fCachedParameters.changed[i] = false;
    620             fUI->parameterChanged(i, value);
    621         }
    622 
    623         if (fIsFloating)
    624         {
    625             if (fWindowTitle.isNotEmpty())
    626                 fUI->setWindowTitle(fWindowTitle);
    627 
    628             if (fTransientWindow != 0)
    629                 fUI->setWindowTransientWinId(fTransientWindow);
    630         }
    631     }
    632 
    633     // ----------------------------------------------------------------------------------------------------------------
    634     // DPF callbacks
    635 
    636     void editParameter(const uint32_t rindex, const bool started) const
    637     {
    638         const ClapEventQueue::Event ev = {
    639             started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureEnd,
    640             rindex, 0.f
    641         };
    642         fEventQueue.addEventFromUI(ev);
    643     }
    644 
    645     static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
    646     {
    647         static_cast<ClapUI*>(ptr)->editParameter(rindex, started);
    648     }
    649 
    650     void setParameterValue(const uint32_t rindex, const float value)
    651     {
    652         const ClapEventQueue::Event ev = {
    653             ClapEventQueue::kEventParamSet,
    654             rindex, value
    655         };
    656         fEventQueue.addEventFromUI(ev);
    657     }
    658 
    659     static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
    660     {
    661         static_cast<ClapUI*>(ptr)->setParameterValue(rindex, value);
    662     }
    663 
    664     void setSizeFromPlugin(const uint width, const uint height)
    665     {
    666         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
    667 
    668        #ifdef DISTRHO_OS_MAC
    669         const double scaleFactor = fUI->getScaleFactor();
    670         const uint hostWidth = width / scaleFactor;
    671         const uint hostHeight = height / scaleFactor;
    672        #else
    673         const uint hostWidth = width;
    674         const uint hostHeight = height;
    675        #endif
    676 
    677         if (fHostGui->request_resize(fHost, hostWidth, hostHeight))
    678             fUI->setWindowSizeFromHost(width, height);
    679     }
    680 
    681     static void setSizeCallback(void* const ptr, const uint width, const uint height)
    682     {
    683         static_cast<ClapUI*>(ptr)->setSizeFromPlugin(width, height);
    684     }
    685 
    686    #if DISTRHO_PLUGIN_WANT_STATE
    687     void setState(const char* const key, const char* const value)
    688     {
    689         fPluginEventQueue->setStateFromUI(key, value);
    690     }
    691 
    692     static void setStateCallback(void* const ptr, const char* key, const char* value)
    693     {
    694         static_cast<ClapUI*>(ptr)->setState(key, value);
    695     }
    696    #endif
    697 
    698    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    699     void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
    700     {
    701         uint8_t midiData[3];
    702         midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel;
    703         midiData[1] = note;
    704         midiData[2] = velocity;
    705         fNotesRingBuffer.writeCustomData(midiData, 3);
    706         fNotesRingBuffer.commitWrite();
    707     }
    708 
    709     static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
    710     {
    711         static_cast<ClapUI*>(ptr)->sendNote(channel, note, velocity);
    712     }
    713    #endif
    714 
    715     /* TODO
    716     bool fileRequest(const char*)
    717     {
    718         return true;
    719     }
    720 
    721     static bool fileRequestCallback(void* const ptr, const char* const key)
    722     {
    723         return static_cast<ClapUI*>(ptr)->fileRequest(key);
    724     }
    725     */
    726 };
    727 
    728 // --------------------------------------------------------------------------------------------------------------------
    729 
    730 #endif // DISTRHO_PLUGIN_HAS_UI
    731 
    732 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    733 static constexpr const writeMidiFunc writeMidiCallback = nullptr;
    734 #endif
    735 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
    736 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
    737 #endif
    738 #if ! DISTRHO_PLUGIN_WANT_STATE
    739 static constexpr const updateStateValueFunc updateStateValueCallback = nullptr;
    740 #endif
    741 
    742 // --------------------------------------------------------------------------------------------------------------------
    743 
    744 /**
    745  * CLAP plugin class.
    746  */
    747 class PluginCLAP : ClapEventQueue
    748 {
    749 public:
    750     PluginCLAP(const clap_host_t* const host)
    751         : fPlugin(this,
    752                   writeMidiCallback,
    753                   requestParameterValueChangeCallback,
    754                   updateStateValueCallback),
    755           fHost(host),
    756           fOutputEvents(nullptr),
    757          #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    758           fUsingCV(false),
    759          #endif
    760          #if DISTRHO_PLUGIN_WANT_LATENCY
    761           fLatencyChanged(false),
    762           fLastKnownLatency(0),
    763          #endif
    764          #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    765           fMidiEventCount(0),
    766          #endif
    767           fHostExtensions(host)
    768     {
    769         fCachedParameters.setup(fPlugin.getParameterCount());
    770 
    771        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
    772         fNotesRingBuffer.setRingBuffer(&fNotesBuffer, true);
    773        #endif
    774 
    775        #if DISTRHO_PLUGIN_WANT_STATE
    776         for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
    777         {
    778             const String& dkey(fPlugin.getStateKey(i));
    779             fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
    780         }
    781        #endif
    782 
    783        #if DISTRHO_PLUGIN_NUM_INPUTS != 0
    784         fillInBusInfoDetails<true>();
    785        #endif
    786        #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    787         fillInBusInfoDetails<false>();
    788        #endif
    789        #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
    790         fillInBusInfoPairs();
    791        #endif
    792     }
    793 
    794     // ----------------------------------------------------------------------------------------------------------------
    795     // core
    796 
    797     template <class T>
    798     const T* getHostExtension(const char* const extensionId) const
    799     {
    800         return static_cast<const T*>(fHost->get_extension(fHost, extensionId));
    801     }
    802 
    803     bool init()
    804     {
    805         if (!clap_version_is_compatible(fHost->clap_version))
    806             return false;
    807 
    808         return fHostExtensions.init();
    809     }
    810 
    811     void activate(const double sampleRate, const uint32_t maxFramesCount)
    812     {
    813         fPlugin.setSampleRate(sampleRate, true);
    814         fPlugin.setBufferSize(maxFramesCount, true);
    815         fPlugin.activate();
    816     }
    817 
    818     void deactivate()
    819     {
    820         fPlugin.deactivate();
    821        #if DISTRHO_PLUGIN_WANT_LATENCY
    822         checkForLatencyChanges(false, true);
    823         reportLatencyChangeIfNeeded();
    824        #endif
    825     }
    826 
    827     bool process(const clap_process_t* const process)
    828     {
    829        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    830         fMidiEventCount = 0;
    831        #endif
    832 
    833        #if DISTRHO_PLUGIN_HAS_UI
    834         if (const clap_output_events_t* const outputEvents = process->out_events)
    835         {
    836             const RecursiveMutexTryLocker crmtl(fEventQueue.lock);
    837 
    838             if (crmtl.wasLocked())
    839             {
    840                 // reuse the same struct for gesture and parameters, they are compatible up to where it matters
    841                 clap_event_param_value_t clapEvent = {
    842                     { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE },
    843                     0, nullptr, 0, 0, 0, 0, 0.0
    844                 };
    845 
    846                 for (uint32_t i=0; i<fEventQueue.used; ++i)
    847                 {
    848                     const Event& event(fEventQueue.events[i]);
    849 
    850                     switch (event.type)
    851                     {
    852                     case kEventGestureBegin:
    853                         clapEvent.header.size = sizeof(clap_event_param_gesture_t);
    854                         clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_BEGIN;
    855                         clapEvent.param_id = event.index;
    856                         break;
    857                     case kEventGestureEnd:
    858                         clapEvent.header.size = sizeof(clap_event_param_gesture_t);
    859                         clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_END;
    860                         clapEvent.param_id = event.index;
    861                         break;
    862                     case kEventParamSet:
    863                         clapEvent.header.size = sizeof(clap_event_param_value_t);
    864                         clapEvent.header.type = CLAP_EVENT_PARAM_VALUE;
    865                         clapEvent.param_id = event.index;
    866                         clapEvent.value = event.value;
    867                         fPlugin.setParameterValue(event.index, event.value);
    868                         break;
    869                     default:
    870                         continue;
    871                     }
    872 
    873                     outputEvents->try_push(outputEvents, &clapEvent.header);
    874                 }
    875 
    876                 fEventQueue.used = 0;
    877             }
    878         }
    879        #endif
    880 
    881        #if DISTRHO_PLUGIN_WANT_TIMEPOS
    882         if (const clap_event_transport_t* const transport = process->transport)
    883         {
    884             fTimePosition.playing = (transport->flags & CLAP_TRANSPORT_IS_PLAYING) != 0 &&
    885                                     (transport->flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL) == 0;
    886 
    887             fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0;
    888 
    889             if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO)
    890                 fTimePosition.bbt.beatsPerMinute = transport->tempo;
    891             else
    892                 fTimePosition.bbt.beatsPerMinute = 120.0;
    893 
    894             // ticksPerBeat is not possible with CLAP
    895             fTimePosition.bbt.ticksPerBeat = 1920.0;
    896 
    897             if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE))
    898             {
    899                 if (transport->song_pos_beats >= 0)
    900                 {
    901                     const int64_t clapPos   = std::abs(transport->song_pos_beats);
    902                     const int64_t clapBeats = clapPos >> 31;
    903                     const double  clapRest  = static_cast<double>(clapPos & 0x7fffffff) / CLAP_BEATTIME_FACTOR;
    904 
    905                     fTimePosition.bbt.bar  = static_cast<int32_t>(clapBeats) / transport->tsig_num + 1;
    906                     fTimePosition.bbt.beat = static_cast<int32_t>(clapBeats % transport->tsig_num) + 1;
    907                     fTimePosition.bbt.tick = clapRest * fTimePosition.bbt.ticksPerBeat;
    908                 }
    909                 else
    910                 {
    911                     fTimePosition.bbt.bar  = 1;
    912                     fTimePosition.bbt.beat = 1;
    913                     fTimePosition.bbt.tick = 0.0;
    914                 }
    915 
    916                 fTimePosition.bbt.valid       = true;
    917                 fTimePosition.bbt.beatsPerBar = transport->tsig_num;
    918                 fTimePosition.bbt.beatType    = transport->tsig_denom;
    919             }
    920             else
    921             {
    922                 fTimePosition.bbt.valid       = false;
    923                 fTimePosition.bbt.bar         = 1;
    924                 fTimePosition.bbt.beat        = 1;
    925                 fTimePosition.bbt.tick        = 0.0;
    926                 fTimePosition.bbt.beatsPerBar = 4.0f;
    927                 fTimePosition.bbt.beatType    = 4.0f;
    928             }
    929 
    930             fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
    931                                              fTimePosition.bbt.beatsPerBar*
    932                                              (fTimePosition.bbt.bar-1);
    933         }
    934         else
    935         {
    936             fTimePosition.playing = false;
    937             fTimePosition.frame = 0;
    938             fTimePosition.bbt.valid          = false;
    939             fTimePosition.bbt.beatsPerMinute = 120.0;
    940             fTimePosition.bbt.bar            = 1;
    941             fTimePosition.bbt.beat           = 1;
    942             fTimePosition.bbt.tick           = 0.0;
    943             fTimePosition.bbt.beatsPerBar    = 4.f;
    944             fTimePosition.bbt.beatType       = 4.f;
    945             fTimePosition.bbt.barStartTick   = 0;
    946         }
    947 
    948         fPlugin.setTimePosition(fTimePosition);
    949        #endif
    950 
    951         if (const clap_input_events_t* const inputEvents = process->in_events)
    952         {
    953             if (const uint32_t len = inputEvents->size(inputEvents))
    954             {
    955                 for (uint32_t i=0; i<len; ++i)
    956                 {
    957                     const clap_event_header_t* const event = inputEvents->get(inputEvents, i);
    958 
    959                     switch (event->type)
    960                     {
    961                     case CLAP_EVENT_NOTE_ON:
    962                        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    963                         // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway
    964                         addNoteEvent(reinterpret_cast<const clap_event_note_t*>(event), true);
    965                        #endif
    966                         break;
    967                     case CLAP_EVENT_NOTE_OFF:
    968                        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    969                         // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway
    970                         addNoteEvent(reinterpret_cast<const clap_event_note_t*>(event), false);
    971                        #endif
    972                         break;
    973                     case CLAP_EVENT_NOTE_CHOKE:
    974                     case CLAP_EVENT_NOTE_END:
    975                     case CLAP_EVENT_NOTE_EXPRESSION:
    976                         break;
    977                     case CLAP_EVENT_PARAM_VALUE:
    978                         DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value_t),
    979                                                         event->size, sizeof(clap_event_param_value_t));
    980                         if (event->space_id == 0)
    981                             setParameterValueFromEvent(reinterpret_cast<const clap_event_param_value_t*>(event));
    982                         break;
    983                     case CLAP_EVENT_PARAM_MOD:
    984                     case CLAP_EVENT_PARAM_GESTURE_BEGIN:
    985                     case CLAP_EVENT_PARAM_GESTURE_END:
    986                     case CLAP_EVENT_TRANSPORT:
    987                         break;
    988                     case CLAP_EVENT_MIDI:
    989                         DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t),
    990                                                         event->size, sizeof(clap_event_midi_t));
    991                        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    992                         addMidiEvent(reinterpret_cast<const clap_event_midi_t*>(event));
    993                        #endif
    994                         break;
    995                     case CLAP_EVENT_MIDI_SYSEX:
    996                     case CLAP_EVENT_MIDI2:
    997                         break;
    998                     }
    999                 }
   1000             }
   1001         }
   1002 
   1003        #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1004         if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
   1005         {
   1006             uint8_t midiData[3];
   1007             const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0;
   1008 
   1009             while (fNotesRingBuffer.isDataAvailableForReading())
   1010             {
   1011                 if (! fNotesRingBuffer.readCustomData(midiData, 3))
   1012                     break;
   1013 
   1014                 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   1015                 midiEvent.frame = frame;
   1016                 midiEvent.size  = 3;
   1017                 std::memcpy(midiEvent.data, midiData, 3);
   1018 
   1019                 if (fMidiEventCount == kMaxMidiEvents)
   1020                     break;
   1021             }
   1022         }
   1023        #endif
   1024 
   1025         if (const uint32_t frames = process->frames_count)
   1026         {
   1027            #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1028             const float** const audioInputs = fAudioInputs;
   1029 
   1030             uint32_t in=0;
   1031             for (uint32_t i=0; i<process->audio_inputs_count; ++i)
   1032             {
   1033                 const clap_audio_buffer_t& inputs(process->audio_inputs[i]);
   1034                 DISTRHO_SAFE_ASSERT_CONTINUE(inputs.channel_count != 0);
   1035 
   1036                 for (uint32_t j=0; j<inputs.channel_count; ++j, ++in)
   1037                     audioInputs[in] = const_cast<const float*>(inputs.data32[j]);
   1038             }
   1039 
   1040             if (fUsingCV)
   1041             {
   1042                 for (; in<DISTRHO_PLUGIN_NUM_INPUTS; ++in)
   1043                     audioInputs[in] = nullptr;
   1044             }
   1045             else
   1046             {
   1047                 DISTRHO_SAFE_ASSERT_UINT2_RETURN(in == DISTRHO_PLUGIN_NUM_INPUTS,
   1048                                                  in, process->audio_inputs_count, false);
   1049             }
   1050            #else
   1051             constexpr const float** const audioInputs = nullptr;
   1052            #endif
   1053 
   1054            #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1055             float** const audioOutputs = fAudioOutputs;
   1056 
   1057             uint32_t out=0;
   1058             for (uint32_t i=0; i<process->audio_outputs_count; ++i)
   1059             {
   1060                 const clap_audio_buffer_t& outputs(process->audio_outputs[i]);
   1061                 DISTRHO_SAFE_ASSERT_CONTINUE(outputs.channel_count != 0);
   1062 
   1063                 for (uint32_t j=0; j<outputs.channel_count; ++j, ++out)
   1064                     audioOutputs[out] = outputs.data32[j];
   1065             }
   1066 
   1067             if (fUsingCV)
   1068             {
   1069                 for (; out<DISTRHO_PLUGIN_NUM_OUTPUTS; ++out)
   1070                     audioOutputs[out] = nullptr;
   1071             }
   1072             else
   1073             {
   1074                 DISTRHO_SAFE_ASSERT_UINT2_RETURN(out == DISTRHO_PLUGIN_NUM_OUTPUTS,
   1075                                                  out, DISTRHO_PLUGIN_NUM_OUTPUTS, false);
   1076             }
   1077            #else
   1078             constexpr float** const audioOutputs = nullptr;
   1079            #endif
   1080 
   1081             fOutputEvents = process->out_events;
   1082 
   1083            #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1084             fPlugin.run(audioInputs, audioOutputs, frames, fMidiEvents, fMidiEventCount);
   1085            #else
   1086             fPlugin.run(audioInputs, audioOutputs, frames);
   1087            #endif
   1088 
   1089             flushParameters(nullptr, process->out_events, frames - 1);
   1090 
   1091             fOutputEvents = nullptr;
   1092         }
   1093 
   1094        #if DISTRHO_PLUGIN_WANT_LATENCY
   1095         checkForLatencyChanges(true, false);
   1096        #endif
   1097 
   1098         return true;
   1099     }
   1100 
   1101     void onMainThread()
   1102     {
   1103        #if DISTRHO_PLUGIN_WANT_LATENCY
   1104         reportLatencyChangeIfNeeded();
   1105        #endif
   1106     }
   1107 
   1108     // ----------------------------------------------------------------------------------------------------------------
   1109     // parameters
   1110 
   1111     uint32_t getParameterCount() const
   1112     {
   1113         return fPlugin.getParameterCount();
   1114     }
   1115 
   1116     bool getParameterInfo(const uint32_t index, clap_param_info_t* const info) const
   1117     {
   1118         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1119 
   1120         if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass)
   1121         {
   1122             info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE;
   1123             std::strcpy(info->name, "Bypass");
   1124             std::strcpy(info->module, "dpf_bypass");
   1125         }
   1126         else
   1127         {
   1128             const uint32_t hints = fPlugin.getParameterHints(index);
   1129             const uint32_t groupId = fPlugin.getParameterGroupId(index);
   1130 
   1131             info->flags = 0;
   1132             if (hints & kParameterIsOutput)
   1133                 info->flags |= CLAP_PARAM_IS_READONLY;
   1134             else if (hints & kParameterIsAutomatable)
   1135                 info->flags |= CLAP_PARAM_IS_AUTOMATABLE;
   1136 
   1137             if (hints & (kParameterIsBoolean|kParameterIsInteger))
   1138                 info->flags |= CLAP_PARAM_IS_STEPPED;
   1139 
   1140             d_strncpy(info->name, fPlugin.getParameterName(index), CLAP_NAME_SIZE);
   1141 
   1142             uint wrtn;
   1143             if (groupId != kPortGroupNone)
   1144             {
   1145                 const PortGroupWithId& portGroup(fPlugin.getPortGroupById(groupId));
   1146                 strncpy(info->module, portGroup.symbol, CLAP_PATH_SIZE / 2);
   1147                 info->module[CLAP_PATH_SIZE / 2] = '\0';
   1148                 wrtn = std::strlen(info->module);
   1149                 info->module[wrtn++] = '/';
   1150             }
   1151             else
   1152             {
   1153                 wrtn = 0;
   1154             }
   1155 
   1156             d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn);
   1157         }
   1158 
   1159         info->id = index;
   1160         info->cookie = nullptr;
   1161         info->min_value = ranges.min;
   1162         info->max_value = ranges.max;
   1163         info->default_value = ranges.def;
   1164         return true;
   1165     }
   1166 
   1167     bool getParameterValue(const clap_id param_id, double* const value) const
   1168     {
   1169         *value = fPlugin.getParameterValue(param_id);
   1170         return true;
   1171     }
   1172 
   1173     bool getParameterStringForValue(const clap_id param_id, double value, char* const display, const uint32_t size) const
   1174     {
   1175         const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
   1176         const ParameterRanges& ranges(fPlugin.getParameterRanges(param_id));
   1177         const uint32_t hints = fPlugin.getParameterHints(param_id);
   1178 
   1179         if (hints & kParameterIsBoolean)
   1180         {
   1181             const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
   1182             value = value > midRange ? ranges.max : ranges.min;
   1183         }
   1184         else if (hints & kParameterIsInteger)
   1185         {
   1186             value = std::round(value);
   1187         }
   1188 
   1189         for (uint32_t i=0; i < enumValues.count; ++i)
   1190         {
   1191             if (d_isEqual(static_cast<double>(enumValues.values[i].value), value))
   1192             {
   1193                 d_strncpy(display, enumValues.values[i].label, size);
   1194                 return true;
   1195             }
   1196         }
   1197 
   1198         if (hints & kParameterIsInteger)
   1199             snprintf_i32(display, value, size);
   1200         else
   1201             snprintf_f32(display, value, size);
   1202 
   1203         return true;
   1204     }
   1205 
   1206     bool getParameterValueForString(const clap_id param_id, const char* const display, double* const value) const
   1207     {
   1208         const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
   1209         const bool isInteger = fPlugin.isParameterInteger(param_id);
   1210 
   1211         for (uint32_t i=0; i < enumValues.count; ++i)
   1212         {
   1213             if (std::strcmp(display, enumValues.values[i].label) == 0)
   1214             {
   1215                 *value = enumValues.values[i].value;
   1216                 return true;
   1217             }
   1218         }
   1219 
   1220         if (isInteger)
   1221             *value = std::atoi(display);
   1222         else
   1223             *value = std::atof(display);
   1224 
   1225         return true;
   1226     }
   1227 
   1228     void flushParameters(const clap_input_events_t* const in,
   1229                          const clap_output_events_t* const out,
   1230                          const uint32_t frameOffset)
   1231     {
   1232         if (const uint32_t len = in != nullptr ? in->size(in) : 0)
   1233         {
   1234             for (uint32_t i=0; i<len; ++i)
   1235             {
   1236                 const clap_event_header_t* const event = in->get(in, i);
   1237 
   1238                 if (event->type != CLAP_EVENT_PARAM_VALUE)
   1239                     continue;
   1240                 if (event->space_id != 0)
   1241                     continue;
   1242 
   1243                 DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value_t),
   1244                                                 event->size, sizeof(clap_event_param_value_t));
   1245 
   1246                 setParameterValueFromEvent(reinterpret_cast<const clap_event_param_value_t*>(event));
   1247             }
   1248         }
   1249 
   1250         if (out != nullptr)
   1251         {
   1252             clap_event_param_value_t clapEvent = {
   1253                 { sizeof(clap_event_param_value_t), frameOffset, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE },
   1254                 0, nullptr, 0, 0, 0, 0, 0.0
   1255             };
   1256 
   1257             float value;
   1258             for (uint i=0; i<fCachedParameters.numParams; ++i)
   1259             {
   1260                 if (fPlugin.isParameterOutputOrTrigger(i))
   1261                 {
   1262                     value = fPlugin.getParameterValue(i);
   1263 
   1264                     if (d_isEqual(fCachedParameters.values[i], value))
   1265                         continue;
   1266 
   1267                     fCachedParameters.values[i] = value;
   1268                     fCachedParameters.changed[i] = true;
   1269 
   1270                     clapEvent.param_id = i;
   1271                     clapEvent.value = value;
   1272                     out->try_push(out, &clapEvent.header);
   1273                 }
   1274             }
   1275         }
   1276 
   1277        #if DISTRHO_PLUGIN_WANT_LATENCY
   1278         const bool active = fPlugin.isActive();
   1279         checkForLatencyChanges(active, !active);
   1280        #endif
   1281     }
   1282 
   1283     // ----------------------------------------------------------------------------------------------------------------
   1284 
   1285    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1286     void addNoteEvent(const clap_event_note_t* const event, const bool isOn) noexcept
   1287     {
   1288         DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,);
   1289 
   1290         if (fMidiEventCount == kMaxMidiEvents)
   1291             return;
   1292 
   1293         MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   1294         midiEvent.frame = event->header.time;
   1295         midiEvent.size  = 3;
   1296         midiEvent.data[0] = (isOn ? 0x90 : 0x80) | (event->channel & 0x0F);
   1297         midiEvent.data[1] = std::max(0, std::min(127, static_cast<int>(event->key)));
   1298         midiEvent.data[2] = std::max(0, std::min(127, static_cast<int>(event->velocity * 127 + 0.5)));
   1299     }
   1300 
   1301     void addMidiEvent(const clap_event_midi_t* const event) noexcept
   1302     {
   1303         DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,);
   1304 
   1305         if (fMidiEventCount == kMaxMidiEvents)
   1306             return;
   1307 
   1308         MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
   1309         midiEvent.frame = event->header.time;
   1310         midiEvent.size  = 3;
   1311         std::memcpy(midiEvent.data, event->data, 3);
   1312     }
   1313    #endif
   1314 
   1315     void setParameterValueFromEvent(const clap_event_param_value_t* const event)
   1316     {
   1317         fCachedParameters.values[event->param_id] = event->value;
   1318         fCachedParameters.changed[event->param_id] = true;
   1319         fPlugin.setParameterValue(event->param_id, event->value);
   1320     }
   1321 
   1322     // ----------------------------------------------------------------------------------------------------------------
   1323     // audio ports
   1324 
   1325    #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1326     template<bool isInput>
   1327     uint32_t getAudioPortCount() const noexcept
   1328     {
   1329         return (isInput ? fAudioInputBuses : fAudioOutputBuses).size();
   1330     }
   1331 
   1332     template<bool isInput>
   1333     bool getAudioPortInfo(const uint32_t index, clap_audio_port_info_t* const info) const noexcept
   1334     {
   1335         const std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses);
   1336         DISTRHO_SAFE_ASSERT_RETURN(index < busInfos.size(), false);
   1337 
   1338         const BusInfo& busInfo(busInfos[index]);
   1339 
   1340         info->id = busInfo.groupId;
   1341         d_strncpy(info->name, busInfo.name, CLAP_NAME_SIZE);
   1342 
   1343         info->flags = busInfo.isMain ? CLAP_AUDIO_PORT_IS_MAIN : 0x0;
   1344         info->channel_count = busInfo.numChannels;
   1345 
   1346         switch (busInfo.groupId)
   1347         {
   1348         case kPortGroupMono:
   1349             info->port_type = CLAP_PORT_MONO;
   1350             break;
   1351         case kPortGroupStereo:
   1352             info->port_type = CLAP_PORT_STEREO;
   1353             break;
   1354         default:
   1355             info->port_type = nullptr;
   1356             break;
   1357         }
   1358 
   1359         info->in_place_pair = busInfo.hasPair ? busInfo.groupId : CLAP_INVALID_ID;
   1360         return true;
   1361     }
   1362    #endif
   1363 
   1364     // ----------------------------------------------------------------------------------------------------------------
   1365     // latency
   1366 
   1367    #if DISTRHO_PLUGIN_WANT_LATENCY
   1368     uint32_t getLatency() const noexcept
   1369     {
   1370         return fPlugin.getLatency();
   1371     }
   1372 
   1373     void checkForLatencyChanges(const bool isActive, const bool fromMainThread)
   1374     {
   1375         const uint32_t latency = fPlugin.getLatency();
   1376 
   1377         if (fLastKnownLatency == latency)
   1378             return;
   1379 
   1380         fLastKnownLatency = latency;
   1381 
   1382         if (fHostExtensions.latency == nullptr)
   1383             return;
   1384 
   1385         if (isActive)
   1386         {
   1387             fLatencyChanged = true;
   1388             fHost->request_restart(fHost);
   1389         }
   1390         else
   1391         {
   1392             // if this is main-thread we can report latency change directly
   1393             if (fromMainThread || (fHostExtensions.threadCheck != nullptr && fHostExtensions.threadCheck->is_main_thread(fHost)))
   1394             {
   1395                 fLatencyChanged = false;
   1396                 fHostExtensions.latency->changed(fHost);
   1397             }
   1398             // otherwise we need to request a main-thread callback
   1399             else
   1400             {
   1401                 fLatencyChanged = true;
   1402                 fHost->request_callback(fHost);
   1403             }
   1404         }
   1405     }
   1406 
   1407     // called from main thread
   1408     void reportLatencyChangeIfNeeded()
   1409     {
   1410         if (fLatencyChanged)
   1411         {
   1412             fLatencyChanged = false;
   1413             fHostExtensions.latency->changed(fHost);
   1414         }
   1415     }
   1416    #endif
   1417 
   1418     // ----------------------------------------------------------------------------------------------------------------
   1419     // state
   1420 
   1421     bool stateSave(const clap_ostream_t* const stream)
   1422     {
   1423         const uint32_t paramCount = fPlugin.getParameterCount();
   1424        #if DISTRHO_PLUGIN_WANT_STATE
   1425         const uint32_t stateCount = fPlugin.getStateCount();
   1426        #else
   1427         const uint32_t stateCount = 0;
   1428        #endif
   1429 
   1430         if (stateCount == 0 && paramCount == 0)
   1431         {
   1432             char buffer = '\0';
   1433             return stream->write(stream, &buffer, 1) == 1;
   1434         }
   1435 
   1436        #if DISTRHO_PLUGIN_WANT_FULL_STATE
   1437         // Update current state
   1438         for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   1439         {
   1440             const String& key(cit->first);
   1441             fStateMap[key] = fPlugin.getStateValue(key);
   1442         }
   1443        #endif
   1444 
   1445         String state;
   1446 
   1447        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1448         {
   1449             String tmpStr("__dpf_program__\xff");
   1450             tmpStr += String(fCurrentProgram);
   1451             tmpStr += "\xff";
   1452 
   1453             state += tmpStr;
   1454         }
   1455        #endif
   1456 
   1457        #if DISTRHO_PLUGIN_WANT_STATE
   1458         if (stateCount != 0)
   1459         {
   1460             state += "__dpf_state_begin__\xff";
   1461 
   1462             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   1463             {
   1464                 const String& key(cit->first);
   1465                 const String& value(cit->second);
   1466 
   1467                 // join key and value
   1468                 String tmpStr;
   1469                 tmpStr  = key;
   1470                 tmpStr += "\xff";
   1471                 tmpStr += value;
   1472                 tmpStr += "\xff";
   1473 
   1474                 state += tmpStr;
   1475             }
   1476 
   1477             state += "__dpf_state_end__\xff";
   1478         }
   1479        #endif
   1480 
   1481         if (paramCount != 0)
   1482         {
   1483             state += "__dpf_parameters_begin__\xff";
   1484 
   1485             for (uint32_t i=0; i<paramCount; ++i)
   1486             {
   1487                 if (fPlugin.isParameterOutputOrTrigger(i))
   1488                     continue;
   1489 
   1490                 // join key and value
   1491                 String tmpStr;
   1492                 tmpStr  = fPlugin.getParameterSymbol(i);
   1493                 tmpStr += "\xff";
   1494                 if (fPlugin.getParameterHints(i) & kParameterIsInteger)
   1495                     tmpStr += String(static_cast<int>(std::round(fPlugin.getParameterValue(i))));
   1496                 else
   1497                     tmpStr += String(fPlugin.getParameterValue(i));
   1498                 tmpStr += "\xff";
   1499 
   1500                 state += tmpStr;
   1501             }
   1502 
   1503             state += "__dpf_parameters_end__\xff";
   1504         }
   1505 
   1506         // terminator
   1507         state += "\xfe";
   1508 
   1509         state.replace('\xff', '\0');
   1510 
   1511         // now saving state, carefully until host written bytes matches full state size
   1512         const char* buffer = state.buffer();
   1513         const int32_t size = static_cast<int32_t>(state.length())+1;
   1514 
   1515         for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn)
   1516         {
   1517             wrtn = stream->write(stream, buffer + wrtntotal, size - wrtntotal);
   1518             DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, false);
   1519         }
   1520 
   1521         return true;
   1522     }
   1523 
   1524     bool stateLoad(const clap_istream_t* const stream)
   1525     {
   1526        #if DISTRHO_PLUGIN_HAS_UI
   1527         ClapUI* const ui = fUI.get();
   1528        #endif
   1529         String key, value;
   1530         bool empty = true;
   1531         bool hasValue = false;
   1532         bool fillingKey = true; // if filling key or value
   1533         char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters)
   1534 
   1535         char buffer[512], orig;
   1536         buffer[sizeof(buffer)-1] = '\xff';
   1537 
   1538         for (int32_t terminated = 0; terminated == 0;)
   1539         {
   1540             const int32_t read = stream->read(stream, buffer, sizeof(buffer)-1);
   1541             DISTRHO_SAFE_ASSERT_INT_RETURN(read >= 0, read, false);
   1542 
   1543             if (read == 0)
   1544                 return !empty;
   1545 
   1546             empty = false;
   1547             for (int32_t i = 0; i < read; ++i)
   1548             {
   1549                 // found terminator, stop here
   1550                 if (buffer[i] == '\xfe')
   1551                 {
   1552                     terminated = 1;
   1553                     break;
   1554                 }
   1555 
   1556                 // store character at read position
   1557                 orig = buffer[read];
   1558 
   1559                 // place null character to create valid string
   1560                 buffer[read] = '\0';
   1561 
   1562                 // append to temporary vars
   1563                 if (fillingKey)
   1564                 {
   1565                     key += buffer + i;
   1566                 }
   1567                 else
   1568                 {
   1569                     value += buffer + i;
   1570                     hasValue = true;
   1571                 }
   1572 
   1573                 // increase buffer offset by length of string
   1574                 i += std::strlen(buffer + i);
   1575 
   1576                 // restore read character
   1577                 buffer[read] = orig;
   1578 
   1579                 // if buffer offset points to null, we found the end of a string, lets check
   1580                 if (buffer[i] == '\0')
   1581                 {
   1582                     // special keys
   1583                     if (key == "__dpf_state_begin__")
   1584                     {
   1585                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
   1586                                                        queryingType, false);
   1587                         queryingType = 's';
   1588                         key.clear();
   1589                         value.clear();
   1590                         hasValue = false;
   1591                         continue;
   1592                     }
   1593                     if (key == "__dpf_state_end__")
   1594                     {
   1595                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, false);
   1596                         queryingType = 'n';
   1597                         key.clear();
   1598                         value.clear();
   1599                         hasValue = false;
   1600                         continue;
   1601                     }
   1602                     if (key == "__dpf_parameters_begin__")
   1603                     {
   1604                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
   1605                                                        queryingType, false);
   1606                         queryingType = 'p';
   1607                         key.clear();
   1608                         value.clear();
   1609                         hasValue = false;
   1610                         continue;
   1611                     }
   1612                     if (key == "__dpf_parameters_end__")
   1613                     {
   1614                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, false);
   1615                         queryingType = 'x';
   1616                         key.clear();
   1617                         value.clear();
   1618                         hasValue = false;
   1619                         continue;
   1620                     }
   1621 
   1622                     // no special key, swap between reading real key and value
   1623                     fillingKey = !fillingKey;
   1624 
   1625                     // if there is no value yet keep reading until we have one
   1626                     if (! hasValue)
   1627                         continue;
   1628 
   1629                     if (key == "__dpf_program__")
   1630                     {
   1631                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, false);
   1632                         queryingType = 'n';
   1633 
   1634                         d_debug("found program '%s'", value.buffer());
   1635 
   1636                       #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1637                         const int program = std::atoi(value.buffer());
   1638                         DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0);
   1639 
   1640                         fCurrentProgram = static_cast<uint32_t>(program);
   1641                         fPlugin.loadProgram(fCurrentProgram);
   1642 
   1643                        #if DISTRHO_PLUGIN_HAS_UI
   1644                         if (ui != nullptr)
   1645                             ui->setProgramFromPlugin(fCurrentProgram);
   1646                        #endif
   1647                       #endif
   1648                     }
   1649                     else if (queryingType == 's')
   1650                     {
   1651                         d_debug("found state '%s' '%s'", key.buffer(), value.buffer());
   1652 
   1653                        #if DISTRHO_PLUGIN_WANT_STATE
   1654                         if (fPlugin.wantStateKey(key))
   1655                         {
   1656                             fStateMap[key] = value;
   1657                             fPlugin.setState(key, value);
   1658 
   1659                            #if DISTRHO_PLUGIN_HAS_UI
   1660                             if (ui != nullptr)
   1661                                 ui->setStateFromPlugin(key, value);
   1662                            #endif
   1663                         }
   1664                        #endif
   1665                     }
   1666                     else if (queryingType == 'p')
   1667                     {
   1668                         d_debug("found parameter '%s' '%s'", key.buffer(), value.buffer());
   1669                         float fvalue;
   1670 
   1671                         // find parameter with this symbol, and set its value
   1672                         for (uint32_t j=0; j<fCachedParameters.numParams; ++j)
   1673                         {
   1674                             if (fPlugin.isParameterOutputOrTrigger(j))
   1675                                 continue;
   1676                             if (fPlugin.getParameterSymbol(j) != key)
   1677                                 continue;
   1678 
   1679                             if (fPlugin.getParameterHints(j) & kParameterIsInteger)
   1680                             {
   1681                                 fvalue = std::atoi(value.buffer());
   1682                             }
   1683                             else
   1684                             {
   1685                                 const ScopedSafeLocale ssl;
   1686                                 fvalue = std::atof(value.buffer());
   1687                             }
   1688 
   1689                             fCachedParameters.values[j] = fvalue;
   1690                            #if DISTRHO_PLUGIN_HAS_UI
   1691                             if (ui != nullptr)
   1692                             {
   1693                                 // UI parameter updates are handled outside the read loop (after host param restart)
   1694                                 fCachedParameters.changed[j] = true;
   1695                             }
   1696                            #endif
   1697                             fPlugin.setParameterValue(j, fvalue);
   1698                             break;
   1699                         }
   1700                     }
   1701 
   1702                     key.clear();
   1703                     value.clear();
   1704                     hasValue = false;
   1705                 }
   1706             }
   1707         }
   1708 
   1709         if (fHostExtensions.params != nullptr)
   1710             fHostExtensions.params->rescan(fHost, CLAP_PARAM_RESCAN_VALUES|CLAP_PARAM_RESCAN_TEXT);
   1711 
   1712        #if DISTRHO_PLUGIN_WANT_LATENCY
   1713         checkForLatencyChanges(fPlugin.isActive(), true);
   1714         reportLatencyChangeIfNeeded();
   1715        #endif
   1716 
   1717        #if DISTRHO_PLUGIN_HAS_UI
   1718         if (ui != nullptr)
   1719         {
   1720             for (uint32_t i=0; i<fCachedParameters.numParams; ++i)
   1721             {
   1722                 if (fPlugin.isParameterOutputOrTrigger(i))
   1723                     continue;
   1724                 fCachedParameters.changed[i] = false;
   1725                 ui->setParameterValueFromPlugin(i, fCachedParameters.values[i]);
   1726             }
   1727         }
   1728        #endif
   1729 
   1730         return true;
   1731     }
   1732 
   1733     // ----------------------------------------------------------------------------------------------------------------
   1734     // gui
   1735 
   1736    #if DISTRHO_PLUGIN_HAS_UI
   1737     bool createUI(const bool isFloating)
   1738     {
   1739         const clap_host_gui_t* const hostGui = getHostExtension<clap_host_gui_t>(CLAP_EXT_GUI);
   1740         DISTRHO_SAFE_ASSERT_RETURN(hostGui != nullptr, false);
   1741 
   1742        #if DPF_CLAP_USING_HOST_TIMER
   1743         const clap_host_timer_support_t* const hostTimer = getHostExtension<clap_host_timer_support_t>(CLAP_EXT_TIMER_SUPPORT);
   1744         DISTRHO_SAFE_ASSERT_RETURN(hostTimer != nullptr, false);
   1745        #endif
   1746 
   1747         fUI = new ClapUI(fPlugin, this, fHost, hostGui,
   1748                         #if DPF_CLAP_USING_HOST_TIMER
   1749                          hostTimer,
   1750                         #endif
   1751                          isFloating);
   1752         return true;
   1753     }
   1754 
   1755     void destroyUI()
   1756     {
   1757         fUI = nullptr;
   1758     }
   1759 
   1760     ClapUI* getUI() const noexcept
   1761     {
   1762         return fUI.get();
   1763     }
   1764    #endif
   1765 
   1766    #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE
   1767     void setStateFromUI(const char* const key, const char* const value) override
   1768     {
   1769         fPlugin.setState(key, value);
   1770 
   1771         if (fPlugin.wantStateKey(key))
   1772         {
   1773             const String dkey(key);
   1774             fStateMap[dkey] = value;
   1775         }
   1776     }
   1777    #endif
   1778 
   1779     // ----------------------------------------------------------------------------------------------------------------
   1780 
   1781 private:
   1782     // Plugin and UI
   1783     PluginExporter fPlugin;
   1784    #if DISTRHO_PLUGIN_HAS_UI
   1785     ScopedPointer<ClapUI> fUI;
   1786    #endif
   1787 
   1788     // CLAP stuff
   1789     const clap_host_t* const fHost;
   1790     const clap_output_events_t* fOutputEvents;
   1791 
   1792    #if DISTRHO_PLUGIN_NUM_INPUTS != 0
   1793     const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS];
   1794    #endif
   1795    #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1796     float* fAudioOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS];
   1797    #endif
   1798    #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1799     bool fUsingCV;
   1800    #endif
   1801    #if DISTRHO_PLUGIN_WANT_LATENCY
   1802     bool fLatencyChanged;
   1803     uint32_t fLastKnownLatency;
   1804    #endif
   1805   #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1806     uint32_t fMidiEventCount;
   1807     MidiEvent fMidiEvents[kMaxMidiEvents];
   1808    #if DISTRHO_PLUGIN_HAS_UI
   1809     RingBufferControl<SmallStackBuffer> fNotesRingBuffer;
   1810    #endif
   1811   #endif
   1812    #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1813     TimePosition fTimePosition;
   1814    #endif
   1815 
   1816     struct HostExtensions {
   1817         const clap_host_t* const host;
   1818         const clap_host_params_t* params;
   1819        #if DISTRHO_PLUGIN_WANT_LATENCY
   1820         const clap_host_latency_t* latency;
   1821         const clap_host_thread_check_t* threadCheck;
   1822        #endif
   1823 
   1824         HostExtensions(const clap_host_t* const host)
   1825             : host(host),
   1826               params(nullptr)
   1827            #if DISTRHO_PLUGIN_WANT_LATENCY
   1828             , latency(nullptr)
   1829             , threadCheck(nullptr)
   1830            #endif
   1831         {}
   1832 
   1833         bool init()
   1834         {
   1835             params = static_cast<const clap_host_params_t*>(host->get_extension(host, CLAP_EXT_PARAMS));
   1836            #if DISTRHO_PLUGIN_WANT_LATENCY
   1837             DISTRHO_SAFE_ASSERT_RETURN(host->request_restart != nullptr, false);
   1838             DISTRHO_SAFE_ASSERT_RETURN(host->request_callback != nullptr, false);
   1839             latency = static_cast<const clap_host_latency_t*>(host->get_extension(host, CLAP_EXT_LATENCY));
   1840             threadCheck = static_cast<const clap_host_thread_check_t*>(host->get_extension(host, CLAP_EXT_THREAD_CHECK));
   1841            #endif
   1842             return true;
   1843         }
   1844     } fHostExtensions;
   1845 
   1846     // ----------------------------------------------------------------------------------------------------------------
   1847     // helper functions for dealing with buses
   1848 
   1849    #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1850     struct BusInfo {
   1851         char name[CLAP_NAME_SIZE];
   1852         uint32_t numChannels;
   1853         bool hasPair;
   1854         bool isCV;
   1855         bool isMain;
   1856         uint32_t groupId;
   1857     };
   1858     std::vector<BusInfo> fAudioInputBuses, fAudioOutputBuses;
   1859 
   1860     template<bool isInput>
   1861     void fillInBusInfoDetails()
   1862     {
   1863         constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
   1864         std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses);
   1865 
   1866         enum {
   1867             kPortTypeNull,
   1868             kPortTypeAudio,
   1869             kPortTypeSidechain,
   1870             kPortTypeCV,
   1871             kPortTypeGroup
   1872         } lastSeenPortType = kPortTypeNull;
   1873         uint32_t lastSeenGroupId = kPortGroupNone;
   1874         uint32_t nonGroupAudioId = 0;
   1875         uint32_t nonGroupSidechainId = 0x20000000;
   1876 
   1877         for (uint32_t i=0; i<numPorts; ++i)
   1878         {
   1879             const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   1880 
   1881             if (port.groupId != kPortGroupNone)
   1882             {
   1883                 if (lastSeenPortType != kPortTypeGroup || lastSeenGroupId != port.groupId)
   1884                 {
   1885                     lastSeenPortType = kPortTypeGroup;
   1886                     lastSeenGroupId = port.groupId;
   1887 
   1888                     BusInfo busInfo = {
   1889                         {}, 1, false, false,
   1890                         // if this is the first port, set it as main
   1891                         busInfos.empty(),
   1892                         // user given group id with extra safety offset
   1893                         port.groupId + 0x80000000
   1894                     };
   1895 
   1896                     const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId));
   1897 
   1898                     switch (port.groupId)
   1899                     {
   1900                     case kPortGroupStereo:
   1901                     case kPortGroupMono:
   1902                         if (busInfo.isMain)
   1903                         {
   1904                             d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE);
   1905                             break;
   1906                         }
   1907                     // fall-through
   1908                     default:
   1909                         if (group.name.isNotEmpty())
   1910                             d_strncpy(busInfo.name, group.name, CLAP_NAME_SIZE);
   1911                         else
   1912                             d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
   1913                         break;
   1914                     }
   1915 
   1916                     busInfos.push_back(busInfo);
   1917                 }
   1918                 else
   1919                 {
   1920                     ++busInfos.back().numChannels;
   1921                 }
   1922             }
   1923             else if (port.hints & kAudioPortIsCV)
   1924             {
   1925                 // TODO
   1926                 lastSeenPortType = kPortTypeCV;
   1927                 lastSeenGroupId = kPortGroupNone;
   1928                 fUsingCV = true;
   1929             }
   1930             else if (port.hints & kAudioPortIsSidechain)
   1931             {
   1932                 if (lastSeenPortType != kPortTypeSidechain)
   1933                 {
   1934                     lastSeenPortType = kPortTypeSidechain;
   1935                     lastSeenGroupId = kPortGroupNone;
   1936 
   1937                     BusInfo busInfo = {
   1938                         {}, 1, false, false,
   1939                         // not main
   1940                         false,
   1941                         // give unique id
   1942                         nonGroupSidechainId++
   1943                     };
   1944 
   1945                     d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
   1946 
   1947                     busInfos.push_back(busInfo);
   1948                 }
   1949                 else
   1950                 {
   1951                     ++busInfos.back().numChannels;
   1952                 }
   1953             }
   1954             else
   1955             {
   1956                 if (lastSeenPortType != kPortTypeAudio)
   1957                 {
   1958                     lastSeenPortType = kPortTypeAudio;
   1959                     lastSeenGroupId = kPortGroupNone;
   1960 
   1961                     BusInfo busInfo = {
   1962                         {}, 1, false, false,
   1963                         // if this is the first port, set it as main
   1964                         busInfos.empty(),
   1965                         // give unique id
   1966                         nonGroupAudioId++
   1967                     };
   1968 
   1969                     if (busInfo.isMain)
   1970                     {
   1971                         d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE);
   1972                     }
   1973                     else
   1974                     {
   1975                         d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
   1976                     }
   1977 
   1978                     busInfos.push_back(busInfo);
   1979                 }
   1980                 else
   1981                 {
   1982                     ++busInfos.back().numChannels;
   1983                 }
   1984             }
   1985         }
   1986     }
   1987    #endif
   1988 
   1989    #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   1990     void fillInBusInfoPairs()
   1991     {
   1992         const size_t numChannels = std::min(fAudioInputBuses.size(), fAudioOutputBuses.size());
   1993 
   1994         for (size_t i=0; i<numChannels; ++i)
   1995         {
   1996             if (fAudioInputBuses[i].groupId != fAudioOutputBuses[i].groupId)
   1997                 break;
   1998             if (fAudioInputBuses[i].numChannels != fAudioOutputBuses[i].numChannels)
   1999                 break;
   2000             if (fAudioInputBuses[i].isMain != fAudioOutputBuses[i].isMain)
   2001                 break;
   2002 
   2003             fAudioInputBuses[i].hasPair = fAudioOutputBuses[i].hasPair = true;
   2004         }
   2005     }
   2006    #endif
   2007 
   2008     // ----------------------------------------------------------------------------------------------------------------
   2009     // DPF callbacks
   2010 
   2011    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2012     bool writeMidi(const MidiEvent& midiEvent)
   2013     {
   2014         DISTRHO_SAFE_ASSERT_RETURN(fOutputEvents != nullptr, false);
   2015 
   2016         if (midiEvent.size > 3)
   2017             return true;
   2018 
   2019         const clap_event_midi clapEvent = {
   2020             { sizeof(clap_event_midi), midiEvent.frame, 0, CLAP_EVENT_MIDI, CLAP_EVENT_IS_LIVE },
   2021             0, { midiEvent.data[0],
   2022                  static_cast<uint8_t>(midiEvent.size >= 2 ? midiEvent.data[1] : 0),
   2023                  static_cast<uint8_t>(midiEvent.size >= 3 ? midiEvent.data[2] : 0) }
   2024         };
   2025         return fOutputEvents->try_push(fOutputEvents, &clapEvent.header);
   2026     }
   2027 
   2028     static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
   2029     {
   2030         return static_cast<PluginCLAP*>(ptr)->writeMidi(midiEvent);
   2031     }
   2032    #endif
   2033 
   2034    #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
   2035     bool requestParameterValueChange(uint32_t, float)
   2036     {
   2037         return true;
   2038     }
   2039 
   2040     static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
   2041     {
   2042         return static_cast<PluginCLAP*>(ptr)->requestParameterValueChange(index, value);
   2043     }
   2044    #endif
   2045 
   2046    #if DISTRHO_PLUGIN_WANT_STATE
   2047     bool updateState(const char*, const char*)
   2048     {
   2049         return true;
   2050     }
   2051 
   2052     static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
   2053     {
   2054         return static_cast<PluginCLAP*>(ptr)->updateState(key, value);
   2055     }
   2056    #endif
   2057 };
   2058 
   2059 // --------------------------------------------------------------------------------------------------------------------
   2060 
   2061 static ScopedPointer<PluginExporter> sPlugin;
   2062 
   2063 // --------------------------------------------------------------------------------------------------------------------
   2064 // plugin gui
   2065 
   2066 #if DISTRHO_PLUGIN_HAS_UI
   2067 
   2068 static const char* const kSupportedAPIs[] = {
   2069 #if defined(DISTRHO_OS_WINDOWS)
   2070     CLAP_WINDOW_API_WIN32,
   2071 #elif defined(DISTRHO_OS_MAC)
   2072     CLAP_WINDOW_API_COCOA,
   2073 #else
   2074     CLAP_WINDOW_API_X11,
   2075 #endif
   2076 };
   2077 
   2078 // TODO DPF external UI
   2079 static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
   2080 {
   2081     for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
   2082     {
   2083         if (std::strcmp(kSupportedAPIs[i], api) == 0)
   2084             return true;
   2085     }
   2086 
   2087     return false;
   2088 }
   2089 
   2090 // TODO DPF external UI
   2091 static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
   2092 {
   2093     *api = kSupportedAPIs[0];
   2094     *is_floating = false;
   2095     return true;
   2096 }
   2097 
   2098 static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
   2099 {
   2100     for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
   2101     {
   2102         if (std::strcmp(kSupportedAPIs[i], api) == 0)
   2103         {
   2104             PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2105             return instance->createUI(is_floating);
   2106         }
   2107     }
   2108 
   2109     return false;
   2110 }
   2111 
   2112 static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin)
   2113 {
   2114     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2115     instance->destroyUI();
   2116 }
   2117 
   2118 static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
   2119 {
   2120     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2121     ClapUI* const gui = instance->getUI();
   2122     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2123    #ifndef DISTRHO_OS_MAC
   2124     return gui->setScaleFactor(scale);
   2125    #else
   2126     return true;
   2127     // unused
   2128     (void)scale;
   2129    #endif
   2130 }
   2131 
   2132 static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
   2133 {
   2134     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2135     ClapUI* const gui = instance->getUI();
   2136     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2137     return gui->getSize(width, height);
   2138 }
   2139 
   2140 static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin)
   2141 {
   2142     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2143     ClapUI* const gui = instance->getUI();
   2144     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2145     return gui->canResize();
   2146 }
   2147 
   2148 static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
   2149 {
   2150     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2151     ClapUI* const gui = instance->getUI();
   2152     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2153     return gui->getResizeHints(hints);
   2154 }
   2155 
   2156 static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
   2157 {
   2158     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2159     ClapUI* const gui = instance->getUI();
   2160     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2161     return gui->adjustSize(width, height);
   2162 }
   2163 
   2164 static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
   2165 {
   2166     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2167     ClapUI* const gui = instance->getUI();
   2168     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2169     return gui->setSizeFromHost(width, height);
   2170 }
   2171 
   2172 static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
   2173 {
   2174     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2175     ClapUI* const gui = instance->getUI();
   2176     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2177     return gui->setParent(window);
   2178 }
   2179 
   2180 static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
   2181 {
   2182     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2183     ClapUI* const gui = instance->getUI();
   2184     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2185     return gui->setTransient(window);
   2186 }
   2187 
   2188 static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
   2189 {
   2190     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2191     ClapUI* const gui = instance->getUI();
   2192     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
   2193     return gui->suggestTitle(title);
   2194 }
   2195 
   2196 static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin)
   2197 {
   2198     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2199     ClapUI* const gui = instance->getUI();
   2200     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2201     return gui->show();
   2202 }
   2203 
   2204 static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin)
   2205 {
   2206     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2207     ClapUI* const gui = instance->getUI();
   2208     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
   2209     return gui->hide();
   2210 }
   2211 
   2212 static const clap_plugin_gui_t clap_plugin_gui = {
   2213     clap_gui_is_api_supported,
   2214     clap_gui_get_preferred_api,
   2215     clap_gui_create,
   2216     clap_gui_destroy,
   2217     clap_gui_set_scale,
   2218     clap_gui_get_size,
   2219     clap_gui_can_resize,
   2220     clap_gui_get_resize_hints,
   2221     clap_gui_adjust_size,
   2222     clap_gui_set_size,
   2223     clap_gui_set_parent,
   2224     clap_gui_set_transient,
   2225     clap_gui_suggest_title,
   2226     clap_gui_show,
   2227     clap_gui_hide
   2228 };
   2229 
   2230 // --------------------------------------------------------------------------------------------------------------------
   2231 // plugin timer
   2232 
   2233 #if DPF_CLAP_USING_HOST_TIMER
   2234 static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
   2235 {
   2236     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2237     ClapUI* const gui = instance->getUI();
   2238     DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
   2239     return gui->idleCallback();
   2240 }
   2241 
   2242 static const clap_plugin_timer_support_t clap_timer = {
   2243     clap_plugin_on_timer
   2244 };
   2245 #endif
   2246 
   2247 #endif // DISTRHO_PLUGIN_HAS_UI
   2248 
   2249 // --------------------------------------------------------------------------------------------------------------------
   2250 // plugin audio ports
   2251 
   2252 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2253 static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
   2254 {
   2255     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2256     return is_input ? instance->getAudioPortCount<true>()
   2257                     : instance->getAudioPortCount<false>();
   2258 }
   2259 
   2260 static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
   2261                                         const uint32_t index,
   2262                                         const bool is_input,
   2263                                         clap_audio_port_info_t* const info)
   2264 {
   2265     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2266     return is_input ? instance->getAudioPortInfo<true>(index, info)
   2267                     : instance->getAudioPortInfo<false>(index, info);
   2268 }
   2269 
   2270 static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
   2271     clap_plugin_audio_ports_count,
   2272     clap_plugin_audio_ports_get
   2273 };
   2274 #endif
   2275 
   2276 // --------------------------------------------------------------------------------------------------------------------
   2277 // plugin note ports
   2278 
   2279 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
   2280 static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
   2281 {
   2282     return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0;
   2283 }
   2284 
   2285 static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
   2286                                                 const bool is_input, clap_note_port_info_t* const info)
   2287 {
   2288     if (is_input)
   2289     {
   2290        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2291         info->id = 0;
   2292         info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
   2293         info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
   2294         std::strcpy(info->name, "Event/MIDI Input");
   2295         return true;
   2296        #endif
   2297     }
   2298     else
   2299     {
   2300        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2301         info->id = 0;
   2302         info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
   2303         info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
   2304         std::strcpy(info->name, "Event/MIDI Output");
   2305         return true;
   2306        #endif
   2307     }
   2308 
   2309     return false;
   2310 }
   2311 
   2312 static const clap_plugin_note_ports_t clap_plugin_note_ports = {
   2313     clap_plugin_note_ports_count,
   2314     clap_plugin_note_ports_get
   2315 };
   2316 #endif
   2317 
   2318 // --------------------------------------------------------------------------------------------------------------------
   2319 // plugin parameters
   2320 
   2321 static uint32_t CLAP_ABI clap_plugin_params_count(const clap_plugin_t* const plugin)
   2322 {
   2323     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2324     return instance->getParameterCount();
   2325 }
   2326 
   2327 static bool CLAP_ABI clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info)
   2328 {
   2329     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2330     return instance->getParameterInfo(index, info);
   2331 }
   2332 
   2333 static bool CLAP_ABI clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value)
   2334 {
   2335     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2336     return instance->getParameterValue(param_id, value);
   2337 }
   2338 
   2339 static bool CLAP_ABI clap_plugin_params_value_to_text(const clap_plugin_t* plugin, const clap_id param_id, const double value, char* const display, const uint32_t size)
   2340 {
   2341     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2342     return instance->getParameterStringForValue(param_id, value, display, size);
   2343 }
   2344 
   2345 static bool CLAP_ABI clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value)
   2346 {
   2347     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2348     return instance->getParameterValueForString(param_id, display, value);
   2349 }
   2350 
   2351 static void CLAP_ABI clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out)
   2352 {
   2353     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2354     return instance->flushParameters(in, out, 0);
   2355 }
   2356 
   2357 static const clap_plugin_params_t clap_plugin_params = {
   2358     clap_plugin_params_count,
   2359     clap_plugin_params_get_info,
   2360     clap_plugin_params_get_value,
   2361     clap_plugin_params_value_to_text,
   2362     clap_plugin_params_text_to_value,
   2363     clap_plugin_params_flush
   2364 };
   2365 
   2366 #if DISTRHO_PLUGIN_WANT_LATENCY
   2367 // --------------------------------------------------------------------------------------------------------------------
   2368 // plugin latency
   2369 
   2370 static uint32_t CLAP_ABI clap_plugin_latency_get(const clap_plugin_t* const plugin)
   2371 {
   2372     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2373     return instance->getLatency();
   2374 }
   2375 
   2376 static const clap_plugin_latency_t clap_plugin_latency = {
   2377     clap_plugin_latency_get
   2378 };
   2379 #endif
   2380 
   2381 // --------------------------------------------------------------------------------------------------------------------
   2382 // plugin state
   2383 
   2384 static bool CLAP_ABI clap_plugin_state_save(const clap_plugin_t* const plugin, const clap_ostream_t* const stream)
   2385 {
   2386     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2387     return instance->stateSave(stream);
   2388 }
   2389 
   2390 static bool CLAP_ABI clap_plugin_state_load(const clap_plugin_t* const plugin, const clap_istream_t* const stream)
   2391 {
   2392     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2393     return instance->stateLoad(stream);
   2394 }
   2395 
   2396 static const clap_plugin_state_t clap_plugin_state = {
   2397     clap_plugin_state_save,
   2398     clap_plugin_state_load
   2399 };
   2400 
   2401 // --------------------------------------------------------------------------------------------------------------------
   2402 // plugin
   2403 
   2404 static bool CLAP_ABI clap_plugin_init(const clap_plugin_t* const plugin)
   2405 {
   2406     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2407     return instance->init();
   2408 }
   2409 
   2410 static void CLAP_ABI clap_plugin_destroy(const clap_plugin_t* const plugin)
   2411 {
   2412     delete static_cast<PluginCLAP*>(plugin->plugin_data);
   2413     std::free(const_cast<clap_plugin_t*>(plugin));
   2414 }
   2415 
   2416 static bool CLAP_ABI clap_plugin_activate(const clap_plugin_t* const plugin,
   2417                                           const double sample_rate,
   2418                                           uint32_t,
   2419                                           const uint32_t max_frames_count)
   2420 {
   2421     d_nextBufferSize = max_frames_count;
   2422     d_nextSampleRate = sample_rate;
   2423 
   2424     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2425     instance->activate(sample_rate, max_frames_count);
   2426     return true;
   2427 }
   2428 
   2429 static void CLAP_ABI clap_plugin_deactivate(const clap_plugin_t* const plugin)
   2430 {
   2431     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2432     instance->deactivate();
   2433 }
   2434 
   2435 static bool CLAP_ABI clap_plugin_start_processing(const clap_plugin_t*)
   2436 {
   2437     // nothing to do
   2438     return true;
   2439 }
   2440 
   2441 static void CLAP_ABI clap_plugin_stop_processing(const clap_plugin_t*)
   2442 {
   2443     // nothing to do
   2444 }
   2445 
   2446 static void CLAP_ABI clap_plugin_reset(const clap_plugin_t*)
   2447 {
   2448     // nothing to do
   2449 }
   2450 
   2451 static clap_process_status CLAP_ABI clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process)
   2452 {
   2453     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2454     return instance->process(process) ? CLAP_PROCESS_CONTINUE : CLAP_PROCESS_ERROR;
   2455 }
   2456 
   2457 static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, const char* const id)
   2458 {
   2459     if (std::strcmp(id, CLAP_EXT_PARAMS) == 0)
   2460         return &clap_plugin_params;
   2461     if (std::strcmp(id, CLAP_EXT_STATE) == 0)
   2462         return &clap_plugin_state;
   2463    #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
   2464     if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
   2465         return &clap_plugin_audio_ports;
   2466    #endif
   2467    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
   2468     if (std::strcmp(id, CLAP_EXT_NOTE_PORTS) == 0)
   2469         return &clap_plugin_note_ports;
   2470    #endif
   2471    #if DISTRHO_PLUGIN_WANT_LATENCY
   2472     if (std::strcmp(id, CLAP_EXT_LATENCY) == 0)
   2473         return &clap_plugin_latency;
   2474    #endif
   2475   #if DISTRHO_PLUGIN_HAS_UI
   2476     if (std::strcmp(id, CLAP_EXT_GUI) == 0)
   2477         return &clap_plugin_gui;
   2478    #if DPF_CLAP_USING_HOST_TIMER
   2479     if (std::strcmp(id, CLAP_EXT_TIMER_SUPPORT) == 0)
   2480         return &clap_timer;
   2481    #endif
   2482   #endif
   2483     return nullptr;
   2484 }
   2485 
   2486 static void CLAP_ABI clap_plugin_on_main_thread(const clap_plugin_t* const plugin)
   2487 {
   2488     PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
   2489     instance->onMainThread();
   2490 }
   2491 
   2492 // --------------------------------------------------------------------------------------------------------------------
   2493 // plugin factory
   2494 
   2495 static uint32_t CLAP_ABI clap_get_plugin_count(const clap_plugin_factory_t*)
   2496 {
   2497     return 1;
   2498 }
   2499 
   2500 static const clap_plugin_descriptor_t* CLAP_ABI clap_get_plugin_descriptor(const clap_plugin_factory_t*,
   2501                                                                            const uint32_t index)
   2502 {
   2503     DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr);
   2504 
   2505     static const char* features[] = {
   2506        #ifdef DISTRHO_PLUGIN_CLAP_FEATURES
   2507         DISTRHO_PLUGIN_CLAP_FEATURES,
   2508        #elif DISTRHO_PLUGIN_IS_SYNTH
   2509         "instrument",
   2510        #else
   2511         "audio-effect",
   2512        #endif
   2513         nullptr
   2514     };
   2515 
   2516     static const clap_plugin_descriptor_t descriptor = {
   2517         CLAP_VERSION,
   2518         DISTRHO_PLUGIN_CLAP_ID,
   2519         sPlugin->getName(),
   2520         sPlugin->getMaker(),
   2521         // TODO url
   2522         "",
   2523         // TODO manual url
   2524         "",
   2525         // TODO support url
   2526         "",
   2527         // TODO version string
   2528         "",
   2529         sPlugin->getDescription(),
   2530         features
   2531     };
   2532 
   2533     return &descriptor;
   2534 }
   2535 
   2536 static const clap_plugin_t* CLAP_ABI clap_create_plugin(const clap_plugin_factory_t* const factory,
   2537                                                         const clap_host_t* const host,
   2538                                                         const char* const plugin_id)
   2539 {
   2540     if (plugin_id == nullptr || std::strcmp(plugin_id, DISTRHO_PLUGIN_CLAP_ID) != 0)
   2541         return nullptr;
   2542 
   2543     clap_plugin_t* const pluginptr = static_cast<clap_plugin_t*>(std::malloc(sizeof(clap_plugin_t)));
   2544     DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr);
   2545 
   2546     // default early values
   2547     if (d_nextBufferSize == 0)
   2548         d_nextBufferSize = 1024;
   2549     if (d_nextSampleRate <= 0.0)
   2550         d_nextSampleRate = 44100.0;
   2551 
   2552     d_nextCanRequestParameterValueChanges = true;
   2553 
   2554     const clap_plugin_t plugin = {
   2555         clap_get_plugin_descriptor(factory, 0),
   2556         new PluginCLAP(host),
   2557         clap_plugin_init,
   2558         clap_plugin_destroy,
   2559         clap_plugin_activate,
   2560         clap_plugin_deactivate,
   2561         clap_plugin_start_processing,
   2562         clap_plugin_stop_processing,
   2563         clap_plugin_reset,
   2564         clap_plugin_process,
   2565         clap_plugin_get_extension,
   2566         clap_plugin_on_main_thread
   2567     };
   2568 
   2569     std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t));
   2570     return pluginptr;
   2571 }
   2572 
   2573 static const clap_plugin_factory_t clap_plugin_factory = {
   2574     clap_get_plugin_count,
   2575     clap_get_plugin_descriptor,
   2576     clap_create_plugin
   2577 };
   2578 
   2579 // --------------------------------------------------------------------------------------------------------------------
   2580 // plugin entry
   2581 
   2582 static bool CLAP_ABI clap_plugin_entry_init(const char* const plugin_path)
   2583 {
   2584     static String bundlePath;
   2585     bundlePath = plugin_path;
   2586     d_nextBundlePath = bundlePath.buffer();
   2587 
   2588     // init dummy plugin
   2589     if (sPlugin == nullptr)
   2590     {
   2591         // set valid but dummy values
   2592         d_nextBufferSize = 512;
   2593         d_nextSampleRate = 44100.0;
   2594         d_nextPluginIsDummy = true;
   2595         d_nextCanRequestParameterValueChanges = true;
   2596 
   2597         // Create dummy plugin to get data from
   2598         sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
   2599 
   2600         // unset
   2601         d_nextBufferSize = 0;
   2602         d_nextSampleRate = 0.0;
   2603         d_nextPluginIsDummy = false;
   2604         d_nextCanRequestParameterValueChanges = false;
   2605     }
   2606 
   2607     return true;
   2608 }
   2609 
   2610 static void CLAP_ABI clap_plugin_entry_deinit(void)
   2611 {
   2612     sPlugin = nullptr;
   2613 }
   2614 
   2615 static const void* CLAP_ABI clap_plugin_entry_get_factory(const char* const factory_id)
   2616 {
   2617     if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0)
   2618         return &clap_plugin_factory;
   2619     return nullptr;
   2620 }
   2621 
   2622 static const clap_plugin_entry_t clap_plugin_entry = {
   2623     CLAP_VERSION,
   2624     clap_plugin_entry_init,
   2625     clap_plugin_entry_deinit,
   2626     clap_plugin_entry_get_factory
   2627 };
   2628 
   2629 // --------------------------------------------------------------------------------------------------------------------
   2630 
   2631 END_NAMESPACE_DISTRHO
   2632 
   2633 // --------------------------------------------------------------------------------------------------------------------
   2634 
   2635 DISTRHO_PLUGIN_EXPORT
   2636 const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry;
   2637 
   2638 // --------------------------------------------------------------------------------------------------------------------