DPF

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

DistrhoPluginVST3.cpp (191027B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 /* TODO items:
     18  * == parameters
     19  * - test parameter triggers
     20  * - have parameter outputs host-provided UI working in at least 1 host
     21  * - parameter groups via unit ids
     22  * - test parameter changes from DSP (aka requestParameterValueChange)
     23  * - implement getParameterNormalized/setParameterNormalized for MIDI CC params ?
     24  * - verify that latency changes works (with and without DPF_VST3_USES_SEPARATE_CONTROLLER)
     25  * == MIDI
     26  * - MIDI CC changes (need to store value to give to the host?)
     27  * - MIDI program changes
     28  * - MIDI sysex
     29  * == BUSES
     30  * - routing info, do we care?
     31  * == CV
     32  * - cv scaling to -1/+1
     33  * - test in at least 1 host
     34  * == INFO
     35  * - set factory email (needs new DPF API, useful for LV2 as well)
     36  * - do something with set_io_mode?
     37  */
     38 
     39 #include "DistrhoPluginInternal.hpp"
     40 #include "../DistrhoPluginUtils.hpp"
     41 #include "../extra/ScopedPointer.hpp"
     42 
     43 #define DPF_VST3_MAX_BUFFER_SIZE 32768
     44 #define DPF_VST3_MAX_SAMPLE_RATE 384000
     45 #define DPF_VST3_MAX_LATENCY     DPF_VST3_MAX_SAMPLE_RATE * 10
     46 
     47 #if DISTRHO_PLUGIN_HAS_UI
     48 # include "../extra/RingBuffer.hpp"
     49 #endif
     50 
     51 #include "travesty/audio_processor.h"
     52 #include "travesty/component.h"
     53 #include "travesty/edit_controller.h"
     54 #include "travesty/factory.h"
     55 #include "travesty/host.h"
     56 
     57 #include <map>
     58 #include <string>
     59 #include <vector>
     60 
     61 START_NAMESPACE_DISTRHO
     62 
     63 // --------------------------------------------------------------------------------------------------------------------
     64 
     65 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
     66 static constexpr const writeMidiFunc writeMidiCallback = nullptr;
     67 #endif
     68 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
     69 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
     70 #endif
     71 
     72 typedef std::map<const String, String> StringMap;
     73 
     74 // --------------------------------------------------------------------------------------------------------------------
     75 // custom v3_tuid compatible type
     76 
     77 typedef uint32_t dpf_tuid[4];
     78 #ifdef DISTRHO_PROPER_CPP11_SUPPORT
     79 static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch");
     80 #endif
     81 
     82 // --------------------------------------------------------------------------------------------------------------------
     83 // custom, constant uids related to DPF
     84 
     85 static constexpr const uint32_t dpf_id_entry = d_cconst('D', 'P', 'F', ' ');
     86 static constexpr const uint32_t dpf_id_clas  = d_cconst('c', 'l', 'a', 's');
     87 static constexpr const uint32_t dpf_id_comp  = d_cconst('c', 'o', 'm', 'p');
     88 static constexpr const uint32_t dpf_id_ctrl  = d_cconst('c', 't', 'r', 'l');
     89 static constexpr const uint32_t dpf_id_proc  = d_cconst('p', 'r', 'o', 'c');
     90 static constexpr const uint32_t dpf_id_view  = d_cconst('v', 'i', 'e', 'w');
     91 
     92 // --------------------------------------------------------------------------------------------------------------------
     93 // plugin specific uids (values are filled in during plugin init)
     94 
     95 #if defined(DISTRHO_PLUGIN_BRAND_ID) && !defined(DPF_VST3_DONT_USE_BRAND_ID)
     96 static constexpr const uint32_t dpf_id_brand = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID));
     97 #else
     98 static constexpr const uint32_t dpf_id_brand = 0;
     99 #endif
    100 
    101 static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, dpf_id_brand };
    102 static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, dpf_id_brand };
    103 static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, dpf_id_brand };
    104 static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, dpf_id_brand };
    105 static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, dpf_id_brand };
    106 
    107 // --------------------------------------------------------------------------------------------------------------------
    108 // Utility functions
    109 
    110 const char* tuid2str(const v3_tuid iid)
    111 {
    112     static constexpr const struct {
    113         v3_tuid iid;
    114         const char* name;
    115     } extra_known_iids[] = {
    116         { V3_ID(0x00000000,0x00000000,0x00000000,0x00000000), "(nil)" },
    117         // edit-controller
    118         { V3_ID(0xF040B4B3,0xA36045EC,0xABCDC045,0xB4D5A2CC), "{v3_component_handler2|NOT}" },
    119         { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" },
    120         { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" },
    121         { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" },
    122         { V3_ID(0xB7F8F859,0x41234872,0x91169581,0x4F3721A3), "{v3_edit_controller_note_expression_controller|NOT}" },
    123         // units
    124         { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" },
    125         { V3_ID(0x6C389611,0xD391455D,0xB870B833,0x94A0EFDD), "{v3_unit_data|NOT}" },
    126         { V3_ID(0x4B5147F8,0x4654486B,0x8DAB30BA,0x163A3C56), "{v3_unit_handler|NOT}" },
    127         { V3_ID(0xF89F8CDF,0x699E4BA5,0x96AAC9A4,0x81452B01), "{v3_unit_handler2|NOT}" },
    128         { V3_ID(0x3D4BD6B5,0x913A4FD2,0xA886E768,0xA5EB92C1), "{v3_unit_info|NOT}" },
    129         // misc
    130         { V3_ID(0x309ECE78,0xEB7D4FAE,0x8B2225D9,0x09FD08B6), "{v3_audio_presentation_latency|NOT}" },
    131         { V3_ID(0xB4E8287F,0x1BB346AA,0x83A46667,0x68937BAB), "{v3_automation_state|NOT}" },
    132         { V3_ID(0x0F194781,0x8D984ADA,0xBBA0C1EF,0xC011D8D0), "{v3_info_listener|NOT}" },
    133         { V3_ID(0x6D21E1DC,0x91199D4B,0xA2A02FEF,0x6C1AE55C), "{v3_parameter_function_name|NOT}" },
    134         { V3_ID(0x8AE54FDA,0xE93046B9,0xA28555BC,0xDC98E21E), "{v3_prefetchable_support|NOT}" },
    135         { V3_ID(0xA81A0471,0x48C34DC4,0xAC30C9E1,0x3C8393D5), "{v3_xml_representation_stream|NOT}" },
    136         /*
    137         // seen in the wild but unknown, related to component
    138         { V3_ID(0x6548D671,0x997A4EA5,0x9B336A6F,0xB3E93B50), "{v3_|NOT}" },
    139         { V3_ID(0xC2B7896B,0x069844D5,0x8F06E937,0x33A35FF7), "{v3_|NOT}" },
    140         { V3_ID(0xE123DE93,0xE0F642A4,0xAE53867E,0x53F059EE), "{v3_|NOT}" },
    141         { V3_ID(0x83850D7B,0xC12011D8,0xA143000A,0x959B31C6), "{v3_|NOT}" },
    142         { V3_ID(0x9598D418,0xA00448AC,0x9C6D8248,0x065B2E5C), "{v3_|NOT}" },
    143         { V3_ID(0xBD386132,0x45174BAD,0xA324390B,0xFD297506), "{v3_|NOT}" },
    144         { V3_ID(0xD7296A84,0x23B1419C,0xAAD0FAA3,0x53BB16B7), "{v3_|NOT}" },
    145         { V3_ID(0x181A0AF6,0xA10947BA,0x8A6F7C7C,0x3FF37129), "{v3_|NOT}" },
    146         { V3_ID(0xC2B7896B,0x69A844D5,0x8F06E937,0x33A35FF7), "{v3_|NOT}" },
    147         // seen in the wild but unknown, related to edit controller
    148         { V3_ID(0x1F2F76D3,0xBFFB4B96,0xB99527A5,0x5EBCCEF4), "{v3_|NOT}" },
    149         { V3_ID(0x6B2449CC,0x419740B5,0xAB3C79DA,0xC5FE5C86), "{v3_|NOT}" },
    150         { V3_ID(0x67800560,0x5E784D90,0xB97BAB4C,0x8DC5BAA3), "{v3_|NOT}" },
    151         { V3_ID(0xDB51DA00,0x8FD5416D,0xB84894D8,0x7FDE73E4), "{v3_|NOT}" },
    152         { V3_ID(0xE90FC54F,0x76F24235,0x8AF8BD15,0x68C663D6), "{v3_|NOT}" },
    153         { V3_ID(0x07938E89,0xBA0D4CA8,0x8C7286AB,0xA9DDA95B), "{v3_|NOT}" },
    154         { V3_ID(0x42879094,0xA2F145ED,0xAC90E82A,0x99458870), "{v3_|NOT}" },
    155         { V3_ID(0xC3B17BC0,0x2C174494,0x80293402,0xFBC4BBF8), "{v3_|NOT}" },
    156         { V3_ID(0x31E29A7A,0xE55043AD,0x8B95B9B8,0xDA1FBE1E), "{v3_|NOT}" },
    157         { V3_ID(0x8E3C292C,0x95924F9D,0xB2590B1E,0x100E4198), "{v3_|NOT}" },
    158         { V3_ID(0x50553FD9,0x1D2C4C24,0xB410F484,0xC5FB9F3F), "{v3_|NOT}" },
    159         { V3_ID(0xF185556C,0x5EE24FC7,0x92F28754,0xB7759EA8), "{v3_|NOT}" },
    160         { V3_ID(0xD2CE9317,0xF24942C9,0x9742E82D,0xB10CCC52), "{v3_|NOT}" },
    161         { V3_ID(0xDA57E6D1,0x1F3242D1,0xAD9C1A82,0xFDB95695), "{v3_|NOT}" },
    162         { V3_ID(0x3ABDFC3E,0x4B964A66,0xFCD86F10,0x0D554023), "{v3_|NOT}" },
    163         // seen in the wild but unknown, related to view
    164         { V3_ID(0xAA3E50FF,0xB78840EE,0xADCD48E8,0x094CEDB7), "{v3_|NOT}" },
    165         { V3_ID(0x2CAE14DB,0x4DE04C6E,0x8BD2E611,0x1B31A9C2), "{v3_|NOT}" },
    166         { V3_ID(0xD868D61D,0x20F445F4,0x947D069E,0xC811D1E4), "{v3_|NOT}" },
    167         { V3_ID(0xEE49E3CA,0x6FCB44FB,0xAEBEE6C3,0x48625122), "{v3_|NOT}" },
    168         */
    169     };
    170 
    171     if (v3_tuid_match(iid, v3_audio_processor_iid))
    172         return "{v3_audio_processor}";
    173     if (v3_tuid_match(iid, v3_attribute_list_iid))
    174         return "{v3_attribute_list_iid}";
    175     if (v3_tuid_match(iid, v3_bstream_iid))
    176         return "{v3_bstream}";
    177     if (v3_tuid_match(iid, v3_component_iid))
    178         return "{v3_component}";
    179     if (v3_tuid_match(iid, v3_component_handler_iid))
    180         return "{v3_component_handler}";
    181     if (v3_tuid_match(iid, v3_connection_point_iid))
    182         return "{v3_connection_point_iid}";
    183     if (v3_tuid_match(iid, v3_edit_controller_iid))
    184         return "{v3_edit_controller}";
    185     if (v3_tuid_match(iid, v3_event_handler_iid))
    186         return "{v3_event_handler_iid}";
    187     if (v3_tuid_match(iid, v3_event_list_iid))
    188         return "{v3_event_list}";
    189     if (v3_tuid_match(iid, v3_funknown_iid))
    190         return "{v3_funknown}";
    191     if (v3_tuid_match(iid, v3_host_application_iid))
    192         return "{v3_host_application_iid}";
    193     if (v3_tuid_match(iid, v3_message_iid))
    194         return "{v3_message_iid}";
    195     if (v3_tuid_match(iid, v3_midi_mapping_iid))
    196         return "{v3_midi_mapping_iid}";
    197     if (v3_tuid_match(iid, v3_param_value_queue_iid))
    198         return "{v3_param_value_queue}";
    199     if (v3_tuid_match(iid, v3_param_changes_iid))
    200         return "{v3_param_changes}";
    201     if (v3_tuid_match(iid, v3_plugin_base_iid))
    202         return "{v3_plugin_base}";
    203     if (v3_tuid_match(iid, v3_plugin_factory_iid))
    204         return "{v3_plugin_factory}";
    205     if (v3_tuid_match(iid, v3_plugin_factory_2_iid))
    206         return "{v3_plugin_factory_2}";
    207     if (v3_tuid_match(iid, v3_plugin_factory_3_iid))
    208         return "{v3_plugin_factory_3}";
    209     if (v3_tuid_match(iid, v3_plugin_frame_iid))
    210         return "{v3_plugin_frame}";
    211     if (v3_tuid_match(iid, v3_plugin_view_iid))
    212         return "{v3_plugin_view}";
    213     if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid))
    214         return "{v3_plugin_view_content_scale_iid}";
    215     if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid))
    216         return "{v3_plugin_view_parameter_finder}";
    217     if (v3_tuid_match(iid, v3_process_context_requirements_iid))
    218         return "{v3_process_context_requirements}";
    219     if (v3_tuid_match(iid, v3_run_loop_iid))
    220         return "{v3_run_loop_iid}";
    221     if (v3_tuid_match(iid, v3_timer_handler_iid))
    222         return "{v3_timer_handler_iid}";
    223 
    224     if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0)
    225         return "{dpf_tuid_class}";
    226     if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0)
    227         return "{dpf_tuid_component}";
    228     if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0)
    229         return "{dpf_tuid_controller}";
    230     if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0)
    231         return "{dpf_tuid_processor}";
    232     if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0)
    233         return "{dpf_tuid_view}";
    234 
    235     for (size_t i=0; i<ARRAY_SIZE(extra_known_iids); ++i)
    236     {
    237         if (v3_tuid_match(iid, extra_known_iids[i].iid))
    238             return extra_known_iids[i].name;
    239     }
    240 
    241     static char buf[46];
    242     std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}",
    243                   (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]),
    244                   (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]),
    245                   (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]),
    246                   (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15]));
    247     return buf;
    248 }
    249 
    250 // --------------------------------------------------------------------------------------------------------------------
    251 // dpf_plugin_view_create (implemented on UI side)
    252 
    253 v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);
    254 
    255 // --------------------------------------------------------------------------------------------------------------------
    256 
    257 /**
    258  * VST3 DSP class.
    259  *
    260  * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things.
    261  * This class is created during the "initialize" component event, and destroyed during "terminate".
    262  *
    263  * The low-level VST3 stuff comes after.
    264  */
    265 class PluginVst3
    266 {
    267     /* Buses: count possible buses we can provide to the host, in case they are not yet defined by the developer.
    268      * These values are only used if port groups aren't set.
    269      *
    270      * When port groups are not in use:
    271      * - 1 bus is provided for the main audio (if there is any)
    272      * - 1 for sidechain
    273      * - 1 for each cv port
    274      * So basically:
    275      * Main audio is used as first bus, if available.
    276      * Then sidechain, also if available.
    277      * And finally each CV port individually.
    278      *
    279      * MIDI will have a single bus, nothing special there.
    280      */
    281     struct BusInfo {
    282         uint8_t audio;     // either 0 or 1
    283         uint8_t sidechain; // either 0 or 1
    284         uint32_t groups;
    285         uint32_t audioPorts;
    286         uint32_t sidechainPorts;
    287         uint32_t groupPorts;
    288         uint32_t cvPorts;
    289 
    290         BusInfo()
    291           : audio(0),
    292             sidechain(0),
    293             groups(0),
    294             audioPorts(0),
    295             sidechainPorts(0),
    296             groupPorts(0),
    297             cvPorts(0) {}
    298     } inputBuses, outputBuses;
    299 
    300    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    301     /* Handy class for storing and sorting VST3 events and MIDI CC parameters.
    302      * It will only store events for which a MIDI conversion is possible.
    303      */
    304     struct InputEventList {
    305         enum Type {
    306             NoteOn,
    307             NoteOff,
    308             SysexData,
    309             PolyPressure,
    310             CC_Normal,
    311             CC_ChannelPressure,
    312             CC_Pitchbend,
    313             UI_MIDI // event from UI
    314         };
    315         struct InputEventStorage {
    316             Type type;
    317             union {
    318                 v3_event_note_on noteOn;
    319                 v3_event_note_off noteOff;
    320                 v3_event_data sysexData;
    321                 v3_event_poly_pressure polyPressure;
    322                 uint8_t midi[3];
    323             };
    324         } eventListStorage[kMaxMidiEvents];
    325 
    326         struct InputEvent {
    327             int32_t sampleOffset;
    328             const InputEventStorage* storage;
    329             InputEvent* next;
    330         } eventList[kMaxMidiEvents];
    331 
    332         uint16_t numUsed;
    333         int32_t firstSampleOffset;
    334         int32_t lastSampleOffset;
    335         InputEvent* firstEvent;
    336         InputEvent* lastEvent;
    337 
    338         void init()
    339         {
    340             numUsed = 0;
    341             firstSampleOffset = lastSampleOffset = 0;
    342             firstEvent = nullptr;
    343         }
    344 
    345         uint32_t convert(MidiEvent midiEvents[kMaxMidiEvents]) const noexcept
    346         {
    347             uint32_t count = 0;
    348 
    349             for (const InputEvent* event = firstEvent; event != nullptr; event = event->next)
    350             {
    351                 MidiEvent& midiEvent(midiEvents[count++]);
    352                 midiEvent.frame = event->sampleOffset;
    353 
    354                 const InputEventStorage& eventStorage(*event->storage);
    355 
    356                 switch (eventStorage.type)
    357                 {
    358                 case NoteOn:
    359                     midiEvent.size = 3;
    360                     midiEvent.data[0] = 0x90 | (eventStorage.noteOn.channel & 0xf);
    361                     midiEvent.data[1] = eventStorage.noteOn.pitch;
    362                     midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.noteOn.velocity * 127)));
    363                     midiEvent.data[3] = 0;
    364                     break;
    365                 case NoteOff:
    366                     midiEvent.size = 3;
    367                     midiEvent.data[0] = 0x80 | (eventStorage.noteOff.channel & 0xf);
    368                     midiEvent.data[1] = eventStorage.noteOff.pitch;
    369                     midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.noteOff.velocity * 127)));
    370                     midiEvent.data[3] = 0;
    371                     break;
    372                 /* TODO
    373                 case SysexData:
    374                     break;
    375                 */
    376                 case PolyPressure:
    377                     midiEvent.size = 3;
    378                     midiEvent.data[0] = 0xA0 | (eventStorage.polyPressure.channel & 0xf);
    379                     midiEvent.data[1] = eventStorage.polyPressure.pitch;
    380                     midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.polyPressure.pressure * 127)));
    381                     midiEvent.data[3] = 0;
    382                     break;
    383                 case CC_Normal:
    384                     midiEvent.size = 3;
    385                     midiEvent.data[0] = 0xB0 | (eventStorage.midi[0] & 0xf);
    386                     midiEvent.data[1] = eventStorage.midi[1];
    387                     midiEvent.data[2] = eventStorage.midi[2];
    388                     break;
    389                 case CC_ChannelPressure:
    390                     midiEvent.size = 2;
    391                     midiEvent.data[0] = 0xD0 | (eventStorage.midi[0] & 0xf);
    392                     midiEvent.data[1] = eventStorage.midi[1];
    393                     midiEvent.data[2] = 0;
    394                     break;
    395                 case CC_Pitchbend:
    396                     midiEvent.size = 3;
    397                     midiEvent.data[0] = 0xE0 | (eventStorage.midi[0] & 0xf);
    398                     midiEvent.data[1] = eventStorage.midi[1];
    399                     midiEvent.data[2] = eventStorage.midi[2];
    400                     break;
    401                 case UI_MIDI:
    402                     midiEvent.size = 3;
    403                     midiEvent.data[0] = eventStorage.midi[0];
    404                     midiEvent.data[1] = eventStorage.midi[1];
    405                     midiEvent.data[2] = eventStorage.midi[2];
    406                     break;
    407                 default:
    408                     midiEvent.size = 0;
    409                     break;
    410                 }
    411             }
    412 
    413             return count;
    414         }
    415 
    416         bool appendEvent(const v3_event& event) noexcept
    417         {
    418             // only save events that can be converted directly into MIDI
    419             switch (event.type)
    420             {
    421             case V3_EVENT_NOTE_ON:
    422             case V3_EVENT_NOTE_OFF:
    423             // case V3_EVENT_DATA:
    424             case V3_EVENT_POLY_PRESSURE:
    425                 break;
    426             default:
    427                 return false;
    428             }
    429 
    430             InputEventStorage& eventStorage(eventListStorage[numUsed]);
    431 
    432             switch (event.type)
    433             {
    434             case V3_EVENT_NOTE_ON:
    435                 eventStorage.type = NoteOn;
    436                 eventStorage.noteOn = event.note_on;
    437                 break;
    438             case V3_EVENT_NOTE_OFF:
    439                 eventStorage.type = NoteOff;
    440                 eventStorage.noteOff = event.note_off;
    441                 break;
    442             case V3_EVENT_DATA:
    443                 eventStorage.type = SysexData;
    444                 eventStorage.sysexData = event.data;
    445                 break;
    446             case V3_EVENT_POLY_PRESSURE:
    447                 eventStorage.type = PolyPressure;
    448                 eventStorage.polyPressure = event.poly_pressure;
    449                 break;
    450             default:
    451                 return false;
    452             }
    453 
    454             eventList[numUsed].sampleOffset = event.sample_offset;
    455             eventList[numUsed].storage = &eventStorage;
    456 
    457             return placeSorted(event.sample_offset);
    458         }
    459 
    460         bool appendCC(const int32_t sampleOffset, v3_param_id paramId, const double normalized) noexcept
    461         {
    462             InputEventStorage& eventStorage(eventListStorage[numUsed]);
    463 
    464             paramId -= kVst3InternalParameterMidiCC_start;
    465 
    466             const uint8_t cc = paramId % 130;
    467 
    468             switch (cc)
    469             {
    470             case 128:
    471                 eventStorage.type = CC_ChannelPressure;
    472                 eventStorage.midi[1] = std::max(0, std::min(127, d_roundToIntPositive(normalized * 127)));
    473                 eventStorage.midi[2] = 0;
    474                 break;
    475             case 129:
    476                 eventStorage.type = CC_Pitchbend;
    477                 eventStorage.midi[1] = std::max(0, std::min(16384, (int)(normalized * 16384))) & 0x7f;
    478                 eventStorage.midi[2] = std::max(0, std::min(16384, (int)(normalized * 16384))) >> 7;
    479                 break;
    480             default:
    481                 eventStorage.type = CC_Normal;
    482                 eventStorage.midi[1] = cc;
    483                 eventStorage.midi[2] = std::max(0, std::min(127, d_roundToIntPositive(normalized * 127)));
    484                 break;
    485             }
    486 
    487             eventStorage.midi[0] = paramId / 130;
    488 
    489             eventList[numUsed].sampleOffset = sampleOffset;
    490             eventList[numUsed].storage = &eventStorage;
    491 
    492             return placeSorted(sampleOffset);
    493         }
    494 
    495        #if DISTRHO_PLUGIN_HAS_UI
    496         // NOTE always runs first
    497         bool appendFromUI(const uint8_t midiData[3])
    498         {
    499             InputEventStorage& eventStorage(eventListStorage[numUsed]);
    500 
    501             eventStorage.type = UI_MIDI;
    502             std::memcpy(eventStorage.midi, midiData, sizeof(uint8_t)*3);
    503 
    504             InputEvent* const event = &eventList[numUsed];
    505 
    506             event->sampleOffset = 0;
    507             event->storage = &eventStorage;
    508             event->next = nullptr;
    509 
    510             if (numUsed == 0)
    511             {
    512                 firstEvent = lastEvent = event;
    513             }
    514             else
    515             {
    516                 lastEvent->next = event;
    517                 lastEvent = event;
    518             }
    519 
    520             return ++numUsed == kMaxMidiEvents;
    521         }
    522        #endif
    523 
    524     private:
    525         bool placeSorted(const int32_t sampleOffset) noexcept
    526         {
    527             InputEvent* const event = &eventList[numUsed];
    528 
    529             // initialize
    530             if (numUsed == 0)
    531             {
    532                 firstSampleOffset = lastSampleOffset = sampleOffset;
    533                 firstEvent = lastEvent = event;
    534                 event->next = nullptr;
    535             }
    536             // push to the back
    537             else if (sampleOffset >= lastSampleOffset)
    538             {
    539                 lastSampleOffset = sampleOffset;
    540                 lastEvent->next = event;
    541                 lastEvent = event;
    542                 event->next = nullptr;
    543             }
    544             // push to the front
    545             else if (sampleOffset < firstSampleOffset)
    546             {
    547                 firstSampleOffset = sampleOffset;
    548                 event->next = firstEvent;
    549                 firstEvent = event;
    550             }
    551             // find place in between events
    552             else
    553             {
    554                 // keep reference out of the loop so we can check validity afterwards
    555                 InputEvent* event2 = firstEvent;
    556 
    557                 // iterate all events
    558                 for (; event2 != nullptr; event2 = event2->next)
    559                 {
    560                     // if offset is higher than iterated event, stop and insert in-between
    561                     if (sampleOffset > event2->sampleOffset)
    562                         break;
    563 
    564                     // if offset matches, find the last event with the same offset so we can push after it
    565                     if (sampleOffset == event2->sampleOffset)
    566                     {
    567                         event2 = event2->next;
    568                         for (; event2 != nullptr && sampleOffset == event2->sampleOffset; event2 = event2->next) {}
    569                         break;
    570                     }
    571                 }
    572 
    573                 DISTRHO_SAFE_ASSERT_RETURN(event2 != nullptr, true);
    574 
    575                 event->next = event2->next;
    576                 event2->next = event;
    577             }
    578 
    579             return ++numUsed == kMaxMidiEvents;
    580         }
    581     } inputEventList;
    582    #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT
    583 
    584 public:
    585     PluginVst3(v3_host_application** const host, const bool isComponent)
    586         : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr),
    587           fComponentHandler(nullptr),
    588         #if DISTRHO_PLUGIN_HAS_UI
    589          #if DPF_VST3_USES_SEPARATE_CONTROLLER
    590           fConnectionFromCompToCtrl(nullptr),
    591          #endif
    592           fConnectionFromCtrlToView(nullptr),
    593           fHostApplication(host),
    594         #endif
    595           fParameterCount(fPlugin.getParameterCount()),
    596           fVst3ParameterCount(fParameterCount + kVst3InternalParameterCount),
    597           fCachedParameterValues(nullptr),
    598           fDummyAudioBuffer(nullptr),
    599           fParameterValuesChangedDuringProcessing(nullptr)
    600        #if DPF_VST3_USES_SEPARATE_CONTROLLER
    601         , fIsComponent(isComponent)
    602        #endif
    603        #if DISTRHO_PLUGIN_HAS_UI
    604         , fParameterValueChangesForUI(nullptr)
    605         , fConnectedToUI(false)
    606        #endif
    607        #if DISTRHO_PLUGIN_WANT_LATENCY
    608         , fLastKnownLatency(fPlugin.getLatency())
    609        #endif
    610        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    611         , fHostEventOutputHandle(nullptr)
    612        #endif
    613        #if DISTRHO_PLUGIN_WANT_PROGRAMS
    614         , fCurrentProgram(0)
    615         , fProgramCountMinusOne(fPlugin.getProgramCount()-1)
    616        #endif
    617     {
    618        #if !DPF_VST3_USES_SEPARATE_CONTROLLER
    619         DISTRHO_SAFE_ASSERT(isComponent);
    620        #endif
    621 
    622        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    623         std::memset(fEnabledInputs, 0, sizeof(fEnabledInputs));
    624         fillInBusInfoDetails<true>();
    625        #endif
    626        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    627         std::memset(fEnabledOutputs, 0, sizeof(fEnabledOutputs));
    628         fillInBusInfoDetails<false>();
    629        #endif
    630 
    631         if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount)
    632         {
    633             fCachedParameterValues = new float[extraParameterCount];
    634 
    635            #if DPF_VST3_USES_SEPARATE_CONTROLLER
    636             fCachedParameterValues[kVst3InternalParameterBufferSize] = fPlugin.getBufferSize();
    637             fCachedParameterValues[kVst3InternalParameterSampleRate] = fPlugin.getSampleRate();
    638            #endif
    639            #if DISTRHO_PLUGIN_WANT_LATENCY
    640             fCachedParameterValues[kVst3InternalParameterLatency]    = fLastKnownLatency;
    641            #endif
    642            #if DISTRHO_PLUGIN_WANT_PROGRAMS
    643             fCachedParameterValues[kVst3InternalParameterProgram]    = 0.0f;
    644            #endif
    645 
    646             for (uint32_t i=0; i < fParameterCount; ++i)
    647                 fCachedParameterValues[kVst3InternalParameterBaseCount + i] = fPlugin.getParameterDefault(i);
    648 
    649             fParameterValuesChangedDuringProcessing = new bool[extraParameterCount];
    650             std::memset(fParameterValuesChangedDuringProcessing, 0, sizeof(bool)*extraParameterCount);
    651 
    652            #if DISTRHO_PLUGIN_HAS_UI
    653             fParameterValueChangesForUI = new bool[extraParameterCount];
    654             std::memset(fParameterValueChangesForUI, 0, sizeof(bool)*extraParameterCount);
    655            #endif
    656         }
    657 
    658        #if DISTRHO_PLUGIN_WANT_STATE
    659         for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
    660         {
    661             const String& dkey(fPlugin.getStateKey(i));
    662             fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
    663         }
    664        #endif
    665 
    666        #if !DISTRHO_PLUGIN_HAS_UI
    667         // unused
    668         return; (void)host;
    669        #endif
    670     }
    671 
    672     ~PluginVst3()
    673     {
    674         if (fCachedParameterValues != nullptr)
    675         {
    676             delete[] fCachedParameterValues;
    677             fCachedParameterValues = nullptr;
    678         }
    679 
    680         if (fDummyAudioBuffer != nullptr)
    681         {
    682             delete[] fDummyAudioBuffer;
    683             fDummyAudioBuffer = nullptr;
    684         }
    685 
    686         if (fParameterValuesChangedDuringProcessing != nullptr)
    687         {
    688             delete[] fParameterValuesChangedDuringProcessing;
    689             fParameterValuesChangedDuringProcessing = nullptr;
    690         }
    691 
    692        #if DISTRHO_PLUGIN_HAS_UI
    693         if (fParameterValueChangesForUI != nullptr)
    694         {
    695             delete[] fParameterValueChangesForUI;
    696             fParameterValueChangesForUI = nullptr;
    697         }
    698        #endif
    699     }
    700 
    701     // ----------------------------------------------------------------------------------------------------------------
    702     // utilities and common code
    703 
    704     double _getNormalizedParameterValue(const uint32_t index, const double plain)
    705     {
    706         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
    707         return ranges.getFixedAndNormalizedValue(plain);
    708     }
    709 
    710     void _setNormalizedPluginParameterValue(const uint32_t index, const double normalized)
    711     {
    712         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
    713         const uint32_t hints = fPlugin.getParameterHints(index);
    714         float value = ranges.getUnnormalizedValue(normalized);
    715 
    716         // convert as needed as check for changes
    717         if (hints & kParameterIsBoolean)
    718         {
    719             const float midRange = ranges.min + (ranges.max - ranges.min) / 2.f;
    720             const bool isHigh = value > midRange;
    721 
    722             if (isHigh == (fCachedParameterValues[kVst3InternalParameterBaseCount + index] > midRange))
    723                 return;
    724 
    725             value = isHigh ? ranges.max : ranges.min;
    726         }
    727         else if (hints & kParameterIsInteger)
    728         {
    729             const int ivalue = d_roundToInt(value);
    730 
    731             if (d_roundToInt(fCachedParameterValues[kVst3InternalParameterBaseCount + index]) == ivalue)
    732                 return;
    733 
    734             value = ivalue;
    735         }
    736         else
    737         {
    738             // deal with low resolution of some hosts, which convert double to float internally and lose precision
    739             if (std::abs(ranges.getNormalizedValue(static_cast<double>(fCachedParameterValues[kVst3InternalParameterBaseCount + index])) - normalized) < 0.0000001)
    740                 return;
    741         }
    742 
    743         fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value;
    744 
    745       #if DISTRHO_PLUGIN_HAS_UI
    746        #if DPF_VST3_USES_SEPARATE_CONTROLLER
    747         if (!fIsComponent)
    748        #endif
    749         {
    750             fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true;
    751         }
    752       #endif
    753 
    754         if (!fPlugin.isParameterOutputOrTrigger(index))
    755             fPlugin.setParameterValue(index, value);
    756     }
    757 
    758     // ----------------------------------------------------------------------------------------------------------------
    759     // stuff called for UI creation
    760 
    761     void* getInstancePointer() const noexcept
    762     {
    763         return fPlugin.getInstancePointer();
    764     }
    765 
    766     double getSampleRate() const noexcept
    767     {
    768         return fPlugin.getSampleRate();
    769     }
    770 
    771     // ----------------------------------------------------------------------------------------------------------------
    772     // v3_component interface calls
    773 
    774     int32_t getBusCount(const int32_t mediaType, const int32_t busDirection) const noexcept
    775     {
    776         switch (mediaType)
    777         {
    778         case V3_AUDIO:
    779             if (busDirection == V3_INPUT)
    780                 return inputBuses.audio + inputBuses.sidechain + inputBuses.groups + inputBuses.cvPorts;
    781             if (busDirection == V3_OUTPUT)
    782                 return outputBuses.audio + outputBuses.sidechain + outputBuses.groups + outputBuses.cvPorts;
    783             break;
    784         case V3_EVENT:
    785            #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    786             if (busDirection == V3_INPUT)
    787                 return 1;
    788            #endif
    789            #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    790             if (busDirection == V3_OUTPUT)
    791                 return 1;
    792            #endif
    793             break;
    794         }
    795 
    796         return 0;
    797     }
    798 
    799     v3_result getBusInfo(const int32_t mediaType,
    800                          const int32_t busDirection,
    801                          const int32_t busIndex,
    802                          v3_bus_info* const info) const
    803     {
    804         DISTRHO_SAFE_ASSERT_INT_RETURN(mediaType == V3_AUDIO || mediaType == V3_EVENT, mediaType, V3_INVALID_ARG);
    805         DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG);
    806         DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG);
    807 
    808        #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    809         const uint32_t busId = static_cast<uint32_t>(busIndex);
    810        #endif
    811 
    812         if (mediaType == V3_AUDIO)
    813         {
    814            #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    815             if (busDirection == V3_INPUT)
    816             {
    817                #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    818                 return getAudioBusInfo<true>(busId, info);
    819                #else
    820                 d_stderr("invalid input bus %d", busId);
    821                 return V3_INVALID_ARG;
    822                #endif // DISTRHO_PLUGIN_NUM_INPUTS
    823             }
    824             else
    825             {
    826                #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    827                 return getAudioBusInfo<false>(busId, info);
    828                #else
    829                 d_stderr("invalid output bus %d", busId);
    830                 return V3_INVALID_ARG;
    831                #endif // DISTRHO_PLUGIN_NUM_OUTPUTS
    832             }
    833            #else
    834             d_stderr("invalid bus, line %d", __LINE__);
    835             return V3_INVALID_ARG;
    836            #endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS
    837         }
    838         else
    839         {
    840             if (busDirection == V3_INPUT)
    841             {
    842                #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    843                 DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
    844                #else
    845                 d_stderr("invalid bus, line %d", __LINE__);
    846                 return V3_INVALID_ARG;
    847                #endif
    848             }
    849             else
    850             {
    851                #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
    852                 DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
    853                #else
    854                 d_stderr("invalid bus, line %d", __LINE__);
    855                 return V3_INVALID_ARG;
    856                #endif
    857             }
    858             info->media_type = V3_EVENT;
    859             info->direction = busDirection;
    860             info->channel_count = 1;
    861             strncpy_utf16(info->bus_name, busDirection == V3_INPUT ? "Event/MIDI Input"
    862                                                                    : "Event/MIDI Output", 128);
    863             info->bus_type = V3_MAIN;
    864             info->flags = V3_DEFAULT_ACTIVE;
    865             return V3_OK;
    866         }
    867     }
    868 
    869     v3_result getRoutingInfo(v3_routing_info*, v3_routing_info*)
    870     {
    871         /*
    872         output->media_type = V3_AUDIO;
    873         output->bus_idx = 0;
    874         output->channel = -1;
    875         d_stdout("getRoutingInfo %s %d %d",
    876                  v3_media_type_str(input->media_type), input->bus_idx, input->channel);
    877         */
    878         return V3_NOT_IMPLEMENTED;
    879     }
    880 
    881     v3_result activateBus(const int32_t mediaType,
    882                           const int32_t busDirection,
    883                           const int32_t busIndex,
    884                           const bool state) noexcept
    885     {
    886         DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG);
    887         DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG);
    888 
    889         if (mediaType == V3_AUDIO)
    890         {
    891            #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    892             const uint32_t busId = static_cast<uint32_t>(busIndex);
    893 
    894             if (busDirection == V3_INPUT)
    895             {
    896                #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    897                 for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
    898                 {
    899                     const AudioPortWithBusId& port(fPlugin.getAudioPort(true, i));
    900 
    901                     if (port.busId == busId)
    902                         fEnabledInputs[i] = state;
    903                 }
    904                #endif
    905             }
    906             else
    907             {
    908                #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    909                 for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
    910                 {
    911                     const AudioPortWithBusId& port(fPlugin.getAudioPort(false, i));
    912 
    913                     if (port.busId == busId)
    914                         fEnabledOutputs[i] = state;
    915                 }
    916                #endif
    917             }
    918            #endif
    919         }
    920 
    921         return V3_OK;
    922 
    923        #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
    924         // unused
    925         (void)state;
    926        #endif
    927     }
    928 
    929     v3_result setActive(const bool active)
    930     {
    931         if (active)
    932             fPlugin.activate();
    933         else
    934             fPlugin.deactivateIfNeeded();
    935 
    936         return V3_OK;
    937     }
    938 
    939     /* state: we pack pairs of key-value strings each separated by a null/zero byte.
    940      * current-program comes first, then dpf key/value states and then parameters.
    941      * parameters are simply converted to/from strings and floats.
    942      * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely.
    943      * there are markers for begin and end of state and parameters, so they never conflict.
    944      */
    945     v3_result setState(v3_bstream** const stream)
    946     {
    947        #if DISTRHO_PLUGIN_HAS_UI
    948         const bool connectedToUI = fConnectionFromCtrlToView != nullptr && fConnectedToUI;
    949        #endif
    950         bool componentValuesChanged = false;
    951         String key, value;
    952         bool empty = true;
    953         bool hasValue = false;
    954         bool fillingKey = true; // if filling key or value
    955         char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters)
    956 
    957         char buffer[512], orig;
    958         buffer[sizeof(buffer)-1] = '\xff';
    959         v3_result res;
    960 
    961         for (int32_t terminated = 0, read; terminated == 0;)
    962         {
    963             read = -1;
    964             res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read);
    965             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
    966             DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR);
    967 
    968             if (read == 0)
    969                 return empty ? V3_INVALID_ARG : V3_OK;
    970 
    971             empty = false;
    972             for (int32_t i = 0; i < read; ++i)
    973             {
    974                 // found terminator, stop here
    975                 if (buffer[i] == '\xfe')
    976                 {
    977                     terminated = 1;
    978                     break;
    979                 }
    980 
    981                 // store character at read position
    982                 orig = buffer[read];
    983 
    984                 // place null character to create valid string
    985                 buffer[read] = '\0';
    986 
    987                 // append to temporary vars
    988                 if (fillingKey)
    989                 {
    990                     key += buffer + i;
    991                 }
    992                 else
    993                 {
    994                     value += buffer + i;
    995                     hasValue = true;
    996                 }
    997 
    998                 // increase buffer offset by length of string
    999                 i += std::strlen(buffer + i);
   1000 
   1001                 // restore read character
   1002                 buffer[read] = orig;
   1003 
   1004                 // if buffer offset points to null, we found the end of a string, lets check
   1005                 if (buffer[i] == '\0')
   1006                 {
   1007                     // special keys
   1008                     if (key == "__dpf_state_begin__")
   1009                     {
   1010                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
   1011                                                        queryingType, V3_INTERNAL_ERR);
   1012                         queryingType = 's';
   1013                         key.clear();
   1014                         value.clear();
   1015                         hasValue = false;
   1016                         continue;
   1017                     }
   1018                     if (key == "__dpf_state_end__")
   1019                     {
   1020                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, V3_INTERNAL_ERR);
   1021                         queryingType = 'n';
   1022                         key.clear();
   1023                         value.clear();
   1024                         hasValue = false;
   1025                         continue;
   1026                     }
   1027                     if (key == "__dpf_parameters_begin__")
   1028                     {
   1029                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
   1030                                                        queryingType, V3_INTERNAL_ERR);
   1031                         queryingType = 'p';
   1032                         key.clear();
   1033                         value.clear();
   1034                         hasValue = false;
   1035                         continue;
   1036                     }
   1037                     if (key == "__dpf_parameters_end__")
   1038                     {
   1039                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, V3_INTERNAL_ERR);
   1040                         queryingType = 'x';
   1041                         key.clear();
   1042                         value.clear();
   1043                         hasValue = false;
   1044                         continue;
   1045                     }
   1046 
   1047                     // no special key, swap between reading real key and value
   1048                     fillingKey = !fillingKey;
   1049 
   1050                     // if there is no value yet keep reading until we have one
   1051                     if (! hasValue)
   1052                         continue;
   1053 
   1054                     if (key == "__dpf_program__")
   1055                     {
   1056                         DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, V3_INTERNAL_ERR);
   1057                         queryingType = 'n';
   1058 
   1059                         d_debug("found program '%s'", value.buffer());
   1060 
   1061                       #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1062                         const int program = std::atoi(value.buffer());
   1063                         DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0);
   1064 
   1065                         fCurrentProgram = static_cast<uint32_t>(program);
   1066                         fPlugin.loadProgram(fCurrentProgram);
   1067 
   1068                        #if DISTRHO_PLUGIN_HAS_UI
   1069                         if (connectedToUI)
   1070                         {
   1071                             fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
   1072                             sendParameterSetToUI(kVst3InternalParameterProgram, program);
   1073                         }
   1074                        #endif
   1075                       #endif
   1076                     }
   1077                     else if (queryingType == 's')
   1078                     {
   1079                         d_debug("found state '%s' '%s'", key.buffer(), value.buffer());
   1080 
   1081                        #if DISTRHO_PLUGIN_WANT_STATE
   1082                         if (fPlugin.wantStateKey(key))
   1083                         {
   1084                             fStateMap[key] = value;
   1085                             fPlugin.setState(key, value);
   1086 
   1087                            #if DISTRHO_PLUGIN_HAS_UI
   1088                             if (connectedToUI)
   1089                                 sendStateSetToUI(key, value);
   1090                            #endif
   1091                         }
   1092                        #endif
   1093                     }
   1094                     else if (queryingType == 'p')
   1095                     {
   1096                         d_debug("found parameter '%s' '%s'", key.buffer(), value.buffer());
   1097                         float fvalue;
   1098 
   1099                         // find parameter with this symbol, and set its value
   1100                         for (uint32_t j=0; j < fParameterCount; ++j)
   1101                         {
   1102                             if (fPlugin.isParameterOutputOrTrigger(j))
   1103                                 continue;
   1104                             if (fPlugin.getParameterSymbol(j) != key)
   1105                                 continue;
   1106 
   1107                             if (fPlugin.getParameterHints(j) & kParameterIsInteger)
   1108                             {
   1109                                 fvalue = std::atoi(value.buffer());
   1110                             }
   1111                             else
   1112                             {
   1113                                 const ScopedSafeLocale ssl;
   1114                                 fvalue = std::atof(value.buffer());
   1115                             }
   1116 
   1117                             fCachedParameterValues[kVst3InternalParameterBaseCount + j] = fvalue;
   1118 
   1119                            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1120                             // If this is the component make sure the controller also knows about the state change
   1121                             if (fIsComponent)
   1122                             {
   1123                                 componentValuesChanged = true;
   1124                                 fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + j] = true;
   1125                             }
   1126                            #else
   1127                             componentValuesChanged = true;
   1128                            #endif
   1129 
   1130                            #if DISTRHO_PLUGIN_HAS_UI
   1131                             if (connectedToUI)
   1132                             {
   1133                                 // UI parameter updates are handled outside the read loop (after host param restart)
   1134                                 fParameterValueChangesForUI[kVst3InternalParameterBaseCount + j] = true;
   1135                             }
   1136                            #endif
   1137                             fPlugin.setParameterValue(j, fvalue);
   1138                             break;
   1139                         }
   1140                     }
   1141 
   1142                     key.clear();
   1143                     value.clear();
   1144                     hasValue = false;
   1145                 }
   1146             }
   1147         }
   1148 
   1149         if (fComponentHandler != nullptr && componentValuesChanged)
   1150             v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED);
   1151 
   1152        #if DISTRHO_PLUGIN_HAS_UI
   1153         if (connectedToUI)
   1154         {
   1155             for (uint32_t i=0; i<fParameterCount; ++i)
   1156             {
   1157                 if (fPlugin.isParameterOutputOrTrigger(i))
   1158                     continue;
   1159                 fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
   1160                 sendParameterSetToUI(kVst3InternalParameterCount + i,
   1161                                      fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
   1162             }
   1163         }
   1164        #endif
   1165 
   1166         return V3_OK;
   1167     }
   1168 
   1169     v3_result getState(v3_bstream** const stream)
   1170     {
   1171         const uint32_t paramCount = fPlugin.getParameterCount();
   1172        #if DISTRHO_PLUGIN_WANT_STATE
   1173         const uint32_t stateCount = fPlugin.getStateCount();
   1174        #else
   1175         constexpr const uint32_t stateCount = 0;
   1176        #endif
   1177 
   1178         if (stateCount == 0 && paramCount == 0)
   1179         {
   1180             char buffer = '\0';
   1181             int32_t ignored;
   1182             return v3_cpp_obj(stream)->write(stream, &buffer, 1, &ignored);
   1183         }
   1184 
   1185        #if DISTRHO_PLUGIN_WANT_FULL_STATE
   1186         // Update current state
   1187         for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   1188         {
   1189             const String& key(cit->first);
   1190             fStateMap[key] = fPlugin.getStateValue(key);
   1191         }
   1192        #endif
   1193 
   1194         String state;
   1195 
   1196        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1197         {
   1198             String tmpStr("__dpf_program__\xff");
   1199             tmpStr += String(fCurrentProgram);
   1200             tmpStr += "\xff";
   1201 
   1202             state += tmpStr;
   1203         }
   1204        #endif
   1205 
   1206        #if DISTRHO_PLUGIN_WANT_STATE
   1207         if (stateCount != 0)
   1208         {
   1209             state += "__dpf_state_begin__\xff";
   1210 
   1211             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   1212             {
   1213                 const String& key(cit->first);
   1214                 const String& value(cit->second);
   1215 
   1216                 // join key and value
   1217                 String tmpStr;
   1218                 tmpStr  = key;
   1219                 tmpStr += "\xff";
   1220                 tmpStr += value;
   1221                 tmpStr += "\xff";
   1222 
   1223                 state += tmpStr;
   1224             }
   1225 
   1226             state += "__dpf_state_end__\xff";
   1227         }
   1228        #endif
   1229 
   1230         if (paramCount != 0)
   1231         {
   1232             state += "__dpf_parameters_begin__\xff";
   1233 
   1234             for (uint32_t i=0; i<paramCount; ++i)
   1235             {
   1236                 if (fPlugin.isParameterOutputOrTrigger(i))
   1237                     continue;
   1238 
   1239                 // join key and value
   1240                 String tmpStr;
   1241                 tmpStr  = fPlugin.getParameterSymbol(i);
   1242                 tmpStr += "\xff";
   1243                 if (fPlugin.getParameterHints(i) & kParameterIsInteger)
   1244                     tmpStr += String(d_roundToInt(fPlugin.getParameterValue(i)));
   1245                 else
   1246                     tmpStr += String(fPlugin.getParameterValue(i));
   1247                 tmpStr += "\xff";
   1248 
   1249                 state += tmpStr;
   1250             }
   1251 
   1252             state += "__dpf_parameters_end__\xff";
   1253         }
   1254 
   1255         // terminator
   1256         state += "\xfe";
   1257 
   1258         state.replace('\xff', '\0');
   1259 
   1260         // now saving state, carefully until host written bytes matches full state size
   1261         const char* buffer = state.buffer();
   1262         const int32_t size = static_cast<int32_t>(state.length())+1;
   1263         v3_result res;
   1264 
   1265         for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn)
   1266         {
   1267             wrtn = 0;
   1268             res = v3_cpp_obj(stream)->write(stream, const_cast<char*>(buffer) + wrtntotal, size - wrtntotal, &wrtn);
   1269 
   1270             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   1271             DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, V3_INTERNAL_ERR);
   1272         }
   1273 
   1274         return V3_OK;
   1275     }
   1276 
   1277     // ----------------------------------------------------------------------------------------------------------------
   1278     // v3_audio_processor interface calls
   1279 
   1280     v3_result setBusArrangements(v3_speaker_arrangement* const inputs, const int32_t numInputs,
   1281                                  v3_speaker_arrangement* const outputs, const int32_t numOutputs)
   1282     {
   1283        #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   1284         DISTRHO_SAFE_ASSERT_RETURN(numInputs >= 0, V3_INVALID_ARG);
   1285         if (!setAudioBusArrangement<true>(inputs, static_cast<uint32_t>(numInputs)))
   1286             return V3_INTERNAL_ERR;
   1287        #else
   1288         DISTRHO_SAFE_ASSERT_RETURN(numInputs == 0, V3_INVALID_ARG);
   1289         // unused
   1290         (void)inputs;
   1291        #endif
   1292 
   1293        #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   1294         DISTRHO_SAFE_ASSERT_RETURN(numOutputs >= 0, V3_INVALID_ARG);
   1295         if (!setAudioBusArrangement<false>(outputs, static_cast<uint32_t>(numOutputs)))
   1296             return V3_INTERNAL_ERR;
   1297        #else
   1298         DISTRHO_SAFE_ASSERT_RETURN(numOutputs == 0, V3_INVALID_ARG);
   1299         // unused
   1300         (void)outputs;
   1301        #endif
   1302 
   1303         return V3_OK;
   1304     }
   1305 
   1306     v3_result getBusArrangement(const int32_t busDirection, const int32_t busIndex, v3_speaker_arrangement* const speaker) const noexcept
   1307     {
   1308         DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG);
   1309         DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG);
   1310         DISTRHO_SAFE_ASSERT_RETURN(speaker != nullptr, V3_INVALID_ARG);
   1311 
   1312        #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   1313         const uint32_t busId = static_cast<uint32_t>(busIndex);
   1314        #endif
   1315 
   1316         if (busDirection == V3_INPUT)
   1317         {
   1318            #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   1319             if (getAudioBusArrangement<true>(busId, speaker))
   1320                 return V3_OK;
   1321            #endif
   1322             d_stderr("invalid input bus arrangement %d, line %d", busIndex, __LINE__);
   1323             return V3_INVALID_ARG;
   1324         }
   1325         else
   1326         {
   1327            #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   1328             if (getAudioBusArrangement<false>(busId, speaker))
   1329                 return V3_OK;
   1330            #endif
   1331             d_stderr("invalid output bus arrangement %d, line %d", busIndex, __LINE__);
   1332             return V3_INVALID_ARG;
   1333         }
   1334     }
   1335 
   1336     uint32_t getLatencySamples() const noexcept
   1337     {
   1338        #if DISTRHO_PLUGIN_WANT_LATENCY
   1339         return fPlugin.getLatency();
   1340        #else
   1341         return 0;
   1342        #endif
   1343     }
   1344 
   1345     v3_result setupProcessing(v3_process_setup* const setup)
   1346     {
   1347         DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG);
   1348 
   1349         const bool active = fPlugin.isActive();
   1350         fPlugin.deactivateIfNeeded();
   1351 
   1352         // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE
   1353 
   1354         fPlugin.setSampleRate(setup->sample_rate, true);
   1355         fPlugin.setBufferSize(setup->max_block_size, true);
   1356 
   1357       #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1358         fCachedParameterValues[kVst3InternalParameterBufferSize] = setup->max_block_size;
   1359         fParameterValuesChangedDuringProcessing[kVst3InternalParameterBufferSize] = true;
   1360 
   1361         fCachedParameterValues[kVst3InternalParameterSampleRate] = setup->sample_rate;
   1362         fParameterValuesChangedDuringProcessing[kVst3InternalParameterSampleRate] = true;
   1363        #if DISTRHO_PLUGIN_HAS_UI
   1364         fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = true;
   1365        #endif
   1366       #endif
   1367 
   1368         if (active)
   1369             fPlugin.activate();
   1370 
   1371         delete[] fDummyAudioBuffer;
   1372         fDummyAudioBuffer = new float[setup->max_block_size];
   1373 
   1374         return V3_OK;
   1375     }
   1376 
   1377     v3_result setProcessing(const bool processing)
   1378     {
   1379         if (processing)
   1380         {
   1381             if (! fPlugin.isActive())
   1382                 fPlugin.activate();
   1383         }
   1384         else
   1385         {
   1386             fPlugin.deactivateIfNeeded();
   1387         }
   1388 
   1389         return V3_OK;
   1390     }
   1391 
   1392     v3_result process(v3_process_data* const data)
   1393     {
   1394         DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG);
   1395         // d_debug("process %i", data->symbolic_sample_size);
   1396 
   1397         // activate plugin if not done yet
   1398         if (! fPlugin.isActive())
   1399             fPlugin.activate();
   1400 
   1401        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   1402         if (v3_process_context* const ctx = data->ctx)
   1403         {
   1404             fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING;
   1405 
   1406             // ticksPerBeat is not possible with VST3
   1407             fTimePosition.bbt.ticksPerBeat = 1920.0;
   1408 
   1409             if (ctx->state & V3_PROCESS_CTX_PROJECT_TIME_VALID)
   1410                 fTimePosition.frame = ctx->project_time_in_samples;
   1411             else if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID)
   1412                 fTimePosition.frame = ctx->continuous_time_in_samples;
   1413 
   1414             if (ctx->state & V3_PROCESS_CTX_TEMPO_VALID)
   1415                 fTimePosition.bbt.beatsPerMinute = ctx->bpm;
   1416             else
   1417                 fTimePosition.bbt.beatsPerMinute = 120.0;
   1418 
   1419             if ((ctx->state & (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID)) == (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID))
   1420             {
   1421                 const double ppqPos    = std::abs(ctx->project_time_quarters);
   1422                 const int    ppqPerBar = ctx->time_sig_numerator * 4 / ctx->time_sig_denom;
   1423                 const double barBeats  = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * ctx->time_sig_numerator;
   1424                 const double rest      =  std::fmod(barBeats, 1.0);
   1425 
   1426                 fTimePosition.bbt.valid       = true;
   1427                 fTimePosition.bbt.bar         = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
   1428                 fTimePosition.bbt.beat        = static_cast<int32_t>(barBeats - rest) + 1;
   1429                 fTimePosition.bbt.tick        = rest * fTimePosition.bbt.ticksPerBeat;
   1430                 fTimePosition.bbt.beatsPerBar = ctx->time_sig_numerator;
   1431                 fTimePosition.bbt.beatType    = ctx->time_sig_denom;
   1432 
   1433                 if (ctx->project_time_quarters < 0.0)
   1434                 {
   1435                     --fTimePosition.bbt.bar;
   1436                     fTimePosition.bbt.beat = ctx->time_sig_numerator - fTimePosition.bbt.beat + 1;
   1437                     fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1;
   1438                 }
   1439             }
   1440             else
   1441             {
   1442                 fTimePosition.bbt.valid       = false;
   1443                 fTimePosition.bbt.bar         = 1;
   1444                 fTimePosition.bbt.beat        = 1;
   1445                 fTimePosition.bbt.tick        = 0.0;
   1446                 fTimePosition.bbt.beatsPerBar = 4.0f;
   1447                 fTimePosition.bbt.beatType    = 4.0f;
   1448             }
   1449 
   1450             fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
   1451                                              fTimePosition.bbt.beatsPerBar*
   1452                                              (fTimePosition.bbt.bar-1);
   1453 
   1454             fPlugin.setTimePosition(fTimePosition);
   1455         }
   1456        #endif
   1457 
   1458         if (data->nframes <= 0)
   1459         {
   1460             updateParametersFromProcessing(data->output_params, 0);
   1461             return V3_OK;
   1462         }
   1463 
   1464         const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS != 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1];
   1465         /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1];
   1466 
   1467         std::memset(fDummyAudioBuffer, 0, sizeof(float)*data->nframes);
   1468 
   1469         {
   1470             int32_t i = 0;
   1471            #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   1472             if (data->inputs != nullptr)
   1473             {
   1474                 for (int32_t b = 0; b < data->num_input_buses; ++b) {
   1475                     for (int32_t j = 0; j < data->inputs[b].num_channels; ++j)
   1476                     {
   1477                         DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_INPUTS, i);
   1478                         if (!fEnabledInputs[i] && i < DISTRHO_PLUGIN_NUM_INPUTS) {
   1479                             inputs[i++] = fDummyAudioBuffer;
   1480                             continue;
   1481                         }
   1482 
   1483                         inputs[i++] = data->inputs[b].channel_buffers_32[j];
   1484                     }
   1485                 }
   1486             }
   1487            #endif
   1488             for (; i < std::max(1, DISTRHO_PLUGIN_NUM_INPUTS); ++i)
   1489                 inputs[i] = fDummyAudioBuffer;
   1490         }
   1491 
   1492         {
   1493             int32_t i = 0;
   1494            #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   1495             if (data->outputs != nullptr)
   1496             {
   1497                 for (int32_t b = 0; b < data->num_output_buses; ++b) {
   1498                     for (int32_t j = 0; j < data->outputs[b].num_channels; ++j)
   1499                     {
   1500                         DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_OUTPUTS, i);
   1501                         if (!fEnabledOutputs[i] && i < DISTRHO_PLUGIN_NUM_OUTPUTS) {
   1502                             outputs[i++] = fDummyAudioBuffer;
   1503                             continue;
   1504                         }
   1505 
   1506                         outputs[i++] = data->outputs[b].channel_buffers_32[j];
   1507                     }
   1508                 }
   1509             }
   1510            #endif
   1511             for (; i < std::max(1, DISTRHO_PLUGIN_NUM_OUTPUTS); ++i)
   1512                 outputs[i] = fDummyAudioBuffer;
   1513         }
   1514 
   1515        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1516         fHostEventOutputHandle = data->output_events;
   1517        #endif
   1518 
   1519       #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1520         bool canAppendMoreEvents = true;
   1521         inputEventList.init();
   1522 
   1523        #if DISTRHO_PLUGIN_HAS_UI
   1524         while (fNotesRingBuffer.isDataAvailableForReading())
   1525         {
   1526             uint8_t midiData[3];
   1527             if (! fNotesRingBuffer.readCustomData(midiData, 3))
   1528                 break;
   1529 
   1530             if (inputEventList.appendFromUI(midiData))
   1531             {
   1532                 canAppendMoreEvents = false;
   1533                 break;
   1534             }
   1535         }
   1536        #endif
   1537 
   1538         if (canAppendMoreEvents)
   1539         {
   1540             if (v3_event_list** const eventptr = data->input_events)
   1541             {
   1542                 v3_event event;
   1543                 for (uint32_t i = 0, count = v3_cpp_obj(eventptr)->get_event_count(eventptr); i < count; ++i)
   1544                 {
   1545                     if (v3_cpp_obj(eventptr)->get_event(eventptr, i, &event) != V3_OK)
   1546                         break;
   1547 
   1548                     if (inputEventList.appendEvent(event))
   1549                     {
   1550                         canAppendMoreEvents = false;
   1551                         break;
   1552                     }
   1553                 }
   1554             }
   1555         }
   1556       #endif
   1557 
   1558         if (v3_param_changes** const inparamsptr = data->input_params)
   1559         {
   1560             int32_t offset;
   1561             double normalized;
   1562 
   1563             for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i)
   1564             {
   1565                 v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i);
   1566                 DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr);
   1567 
   1568                 const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue);
   1569                 DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex);
   1570 
   1571                #if DPF_VST3_HAS_INTERNAL_PARAMETERS
   1572                 if (rindex < kVst3InternalParameterCount)
   1573                 {
   1574                    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1575                     // if there are any MIDI CC events as parameter changes, handle them here
   1576                     if (canAppendMoreEvents && rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end)
   1577                     {
   1578                         for (int32_t j = 0, pcount = v3_cpp_obj(queue)->get_point_count(queue); j < pcount; ++j)
   1579                         {
   1580                             if (v3_cpp_obj(queue)->get_point(queue, j, &offset, &normalized) != V3_OK)
   1581                                 break;
   1582 
   1583                             if (inputEventList.appendCC(offset, rindex, normalized))
   1584                             {
   1585                                 canAppendMoreEvents = false;
   1586                                 break;
   1587                             }
   1588                         }
   1589                     }
   1590                    #endif
   1591                     continue;
   1592                 }
   1593                #endif
   1594 
   1595                 if (v3_cpp_obj(queue)->get_point_count(queue) <= 0)
   1596                     continue;
   1597 
   1598                 // if there are any parameter changes at frame 0, handle them here
   1599                 if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &normalized) != V3_OK)
   1600                     break;
   1601 
   1602                 if (offset != 0)
   1603                     continue;
   1604 
   1605                 const uint32_t index = rindex - kVst3InternalParameterCount;
   1606                 _setNormalizedPluginParameterValue(index, normalized);
   1607             }
   1608         }
   1609 
   1610        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1611         const uint32_t midiEventCount = inputEventList.convert(fMidiEvents);
   1612         fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount);
   1613        #else
   1614         fPlugin.run(inputs, outputs, data->nframes);
   1615        #endif
   1616 
   1617        #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   1618         fHostEventOutputHandle = nullptr;
   1619        #endif
   1620 
   1621         // if there are any parameter changes after frame 0, set them here
   1622         if (v3_param_changes** const inparamsptr = data->input_params)
   1623         {
   1624             int32_t offset;
   1625             double normalized;
   1626 
   1627             for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i)
   1628             {
   1629                 v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i);
   1630                 DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr);
   1631 
   1632                 const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue);
   1633                 DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex);
   1634 
   1635                #if DPF_VST3_HAS_INTERNAL_PARAMETERS
   1636                 if (rindex < kVst3InternalParameterCount)
   1637                     continue;
   1638                #endif
   1639 
   1640                 const int32_t pcount = v3_cpp_obj(queue)->get_point_count(queue);
   1641 
   1642                 if (pcount <= 0)
   1643                     continue;
   1644 
   1645                 if (v3_cpp_obj(queue)->get_point(queue, pcount - 1, &offset, &normalized) != V3_OK)
   1646                     break;
   1647 
   1648                 if (offset == 0)
   1649                     continue;
   1650 
   1651                 const uint32_t index = rindex - kVst3InternalParameterCount;
   1652                 _setNormalizedPluginParameterValue(index, normalized);
   1653             }
   1654         }
   1655 
   1656         updateParametersFromProcessing(data->output_params, data->nframes - 1);
   1657         return V3_OK;
   1658     }
   1659 
   1660     uint32_t getTailSamples() const noexcept
   1661     {
   1662         return 0;
   1663     }
   1664 
   1665     // ----------------------------------------------------------------------------------------------------------------
   1666     // v3_edit_controller interface calls
   1667 
   1668     int32_t getParameterCount() const noexcept
   1669     {
   1670         return fVst3ParameterCount;
   1671     }
   1672 
   1673     v3_result getParameterInfo(const int32_t rindex, v3_param_info* const info) const noexcept
   1674     {
   1675         std::memset(info, 0, sizeof(v3_param_info));
   1676         DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG);
   1677 
   1678         // TODO hash the parameter symbol
   1679         info->param_id = rindex;
   1680 
   1681       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   1682         switch (rindex)
   1683         {
   1684        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1685         case kVst3InternalParameterBufferSize:
   1686             info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
   1687             info->step_count = DPF_VST3_MAX_BUFFER_SIZE - 1;
   1688             strncpy_utf16(info->title, "Buffer Size", 128);
   1689             strncpy_utf16(info->short_title, "Buffer Size", 128);
   1690             strncpy_utf16(info->units, "frames", 128);
   1691             return V3_OK;
   1692         case kVst3InternalParameterSampleRate:
   1693             info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
   1694             strncpy_utf16(info->title, "Sample Rate", 128);
   1695             strncpy_utf16(info->short_title, "Sample Rate", 128);
   1696             strncpy_utf16(info->units, "frames", 128);
   1697             return V3_OK;
   1698        #endif
   1699        #if DISTRHO_PLUGIN_WANT_LATENCY
   1700         case kVst3InternalParameterLatency:
   1701             info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
   1702             strncpy_utf16(info->title, "Latency", 128);
   1703             strncpy_utf16(info->short_title, "Latency", 128);
   1704             strncpy_utf16(info->units, "frames", 128);
   1705             return V3_OK;
   1706        #endif
   1707        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1708         case kVst3InternalParameterProgram:
   1709             info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE | V3_PARAM_IS_HIDDEN;
   1710             info->step_count = fProgramCountMinusOne;
   1711             strncpy_utf16(info->title, "Current Program", 128);
   1712             strncpy_utf16(info->short_title, "Program", 128);
   1713             return V3_OK;
   1714        #endif
   1715         }
   1716       #endif
   1717 
   1718        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1719         if (rindex < kVst3InternalParameterCount)
   1720         {
   1721             const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterMidiCC_start);
   1722             info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN;
   1723             info->step_count = 127;
   1724             char ccstr[24];
   1725             snprintf(ccstr, sizeof(ccstr), "MIDI Ch. %d CC %d", static_cast<uint8_t>(index / 130) + 1, index % 130);
   1726             strncpy_utf16(info->title, ccstr, 128);
   1727             snprintf(ccstr, sizeof(ccstr), "Ch.%d CC%d", index / 130 + 1, index % 130);
   1728             strncpy_utf16(info->short_title, ccstr+5, 128);
   1729             return V3_OK;
   1730         }
   1731        #endif
   1732 
   1733         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   1734         DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
   1735 
   1736         // set up flags
   1737         int32_t flags = 0;
   1738 
   1739         const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
   1740         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1741         const uint32_t hints = fPlugin.getParameterHints(index);
   1742 
   1743         switch (fPlugin.getParameterDesignation(index))
   1744         {
   1745         case kParameterDesignationNull:
   1746             break;
   1747         case kParameterDesignationBypass:
   1748             flags |= V3_PARAM_IS_BYPASS;
   1749             break;
   1750         }
   1751 
   1752         if (hints & kParameterIsOutput)
   1753             flags |= V3_PARAM_READ_ONLY;
   1754         else if (hints & kParameterIsAutomatable)
   1755             flags |= V3_PARAM_CAN_AUTOMATE;
   1756 
   1757         // set up step_count
   1758         int32_t step_count = 0;
   1759 
   1760         if (hints & kParameterIsBoolean)
   1761             step_count = 1;
   1762         else if (hints & kParameterIsInteger)
   1763             step_count = ranges.max - ranges.min;
   1764 
   1765         if (enumValues.count >= 2 && enumValues.restrictedMode)
   1766         {
   1767             flags |= V3_PARAM_IS_LIST;
   1768             step_count = enumValues.count - 1;
   1769         }
   1770 
   1771         info->flags = flags;
   1772         info->step_count = step_count;
   1773         info->default_normalised_value = ranges.getNormalizedValue(ranges.def);
   1774         // int32_t unit_id;
   1775         strncpy_utf16(info->title,       fPlugin.getParameterName(index), 128);
   1776         strncpy_utf16(info->short_title, fPlugin.getParameterShortName(index), 128);
   1777         strncpy_utf16(info->units,       fPlugin.getParameterUnit(index), 128);
   1778         return V3_OK;
   1779     }
   1780 
   1781     v3_result getParameterStringForValue(const v3_param_id rindex, const double normalized, v3_str_128 output)
   1782     {
   1783         DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG);
   1784 
   1785       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   1786         switch (rindex)
   1787         {
   1788        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1789         case kVst3InternalParameterBufferSize:
   1790             snprintf_i32_utf16(output, d_roundToIntPositive(normalized * DPF_VST3_MAX_BUFFER_SIZE), 128);
   1791             return V3_OK;
   1792         case kVst3InternalParameterSampleRate:
   1793             snprintf_i32_utf16(output, d_roundToIntPositive(normalized * DPF_VST3_MAX_SAMPLE_RATE), 128);
   1794             return V3_OK;
   1795        #endif
   1796        #if DISTRHO_PLUGIN_WANT_LATENCY
   1797         case kVst3InternalParameterLatency:
   1798             snprintf_i32_utf16(output, d_roundToIntPositive(normalized * DPF_VST3_MAX_LATENCY), 128);
   1799             return V3_OK;
   1800        #endif
   1801        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1802         case kVst3InternalParameterProgram:
   1803             const uint32_t program = d_roundToIntPositive(normalized * fProgramCountMinusOne);
   1804             strncpy_utf16(output, fPlugin.getProgramName(program), 128);
   1805             return V3_OK;
   1806        #endif
   1807         }
   1808       #endif
   1809 
   1810        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1811         if (rindex < kVst3InternalParameterCount)
   1812         {
   1813             snprintf_i32_utf16(output, d_roundToIntPositive(normalized * 127), 128);
   1814             return V3_OK;
   1815         }
   1816        #endif
   1817 
   1818         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   1819         DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
   1820 
   1821         const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
   1822         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1823         const uint32_t hints = fPlugin.getParameterHints(index);
   1824         float value = ranges.getUnnormalizedValue(normalized);
   1825 
   1826         if (hints & kParameterIsBoolean)
   1827         {
   1828             const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
   1829             value = value > midRange ? ranges.max : ranges.min;
   1830         }
   1831         else if (hints & kParameterIsInteger)
   1832         {
   1833             value = std::round(value);
   1834         }
   1835 
   1836         for (uint32_t i=0; i < enumValues.count; ++i)
   1837         {
   1838             if (d_isEqual(enumValues.values[i].value, value))
   1839             {
   1840                 strncpy_utf16(output, enumValues.values[i].label, 128);
   1841                 return V3_OK;
   1842             }
   1843         }
   1844 
   1845         if (hints & kParameterIsInteger)
   1846             snprintf_i32_utf16(output, value, 128);
   1847         else
   1848             snprintf_f32_utf16(output, value, 128);
   1849 
   1850         return V3_OK;
   1851     }
   1852 
   1853     v3_result getParameterValueForString(const v3_param_id rindex, int16_t* const input, double* const output)
   1854     {
   1855       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   1856         switch (rindex)
   1857         {
   1858        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1859         case kVst3InternalParameterBufferSize:
   1860             *output = static_cast<double>(std::atoi(ScopedUTF8String(input))) / DPF_VST3_MAX_BUFFER_SIZE;
   1861             return V3_OK;
   1862         case kVst3InternalParameterSampleRate:
   1863             *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_SAMPLE_RATE;
   1864             return V3_OK;
   1865        #endif
   1866        #if DISTRHO_PLUGIN_WANT_LATENCY
   1867         case kVst3InternalParameterLatency:
   1868             *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_LATENCY;
   1869             return V3_OK;
   1870        #endif
   1871        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1872         case kVst3InternalParameterProgram:
   1873             for (uint32_t i=0, count=fPlugin.getProgramCount(); i < count; ++i)
   1874             {
   1875                 if (strcmp_utf16(input, fPlugin.getProgramName(i)))
   1876                 {
   1877                     *output = static_cast<double>(i) / static_cast<double>(fProgramCountMinusOne);
   1878                     return V3_OK;
   1879                 }
   1880             }
   1881             return V3_INVALID_ARG;
   1882        #endif
   1883         }
   1884       #endif
   1885 
   1886        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1887         if (rindex < kVst3InternalParameterCount)
   1888         {
   1889             // TODO find CC/channel based on name
   1890             return V3_NOT_IMPLEMENTED;
   1891         }
   1892        #endif
   1893 
   1894         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   1895         DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
   1896 
   1897         const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
   1898         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1899 
   1900         for (uint32_t i=0; i < enumValues.count; ++i)
   1901         {
   1902             if (strcmp_utf16(input, enumValues.values[i].label))
   1903             {
   1904                 *output = ranges.getNormalizedValue(enumValues.values[i].value);
   1905                 return V3_OK;
   1906             }
   1907         }
   1908 
   1909         const ScopedUTF8String input8(input);
   1910 
   1911         float value;
   1912         if (fPlugin.getParameterHints(index) & kParameterIsInteger)
   1913             value = std::atoi(input8);
   1914         else
   1915             value = std::atof(input8);
   1916 
   1917         *output = ranges.getNormalizedValue(value);
   1918         return V3_OK;
   1919     }
   1920 
   1921     double normalizedParameterToPlain(const v3_param_id rindex, const double normalized)
   1922     {
   1923         DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, 0.0);
   1924 
   1925       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   1926         switch (rindex)
   1927         {
   1928        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1929         case kVst3InternalParameterBufferSize:
   1930             return std::round(normalized * DPF_VST3_MAX_BUFFER_SIZE);
   1931         case kVst3InternalParameterSampleRate:
   1932             return normalized * DPF_VST3_MAX_SAMPLE_RATE;
   1933        #endif
   1934        #if DISTRHO_PLUGIN_WANT_LATENCY
   1935         case kVst3InternalParameterLatency:
   1936             return normalized * DPF_VST3_MAX_LATENCY;
   1937        #endif
   1938        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1939         case kVst3InternalParameterProgram:
   1940             return std::round(normalized * fProgramCountMinusOne);
   1941        #endif
   1942         }
   1943       #endif
   1944 
   1945        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1946         if (rindex < kVst3InternalParameterCount)
   1947             return std::round(normalized * 127);
   1948        #endif
   1949 
   1950         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   1951         DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
   1952 
   1953         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
   1954         const uint32_t hints = fPlugin.getParameterHints(index);
   1955         float value = ranges.getUnnormalizedValue(normalized);
   1956 
   1957         if (hints & kParameterIsBoolean)
   1958         {
   1959             const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
   1960             value = value > midRange ? ranges.max : ranges.min;
   1961         }
   1962         else if (hints & kParameterIsInteger)
   1963         {
   1964             value = std::round(value);
   1965         }
   1966 
   1967         return value;
   1968     }
   1969 
   1970     double plainParameterToNormalized(const v3_param_id rindex, const double plain)
   1971     {
   1972       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   1973         switch (rindex)
   1974         {
   1975        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   1976         case kVst3InternalParameterBufferSize:
   1977             return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_BUFFER_SIZE));
   1978         case kVst3InternalParameterSampleRate:
   1979             return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_SAMPLE_RATE));
   1980        #endif
   1981        #if DISTRHO_PLUGIN_WANT_LATENCY
   1982         case kVst3InternalParameterLatency:
   1983             return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_LATENCY));
   1984        #endif
   1985        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   1986         case kVst3InternalParameterProgram:
   1987             return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne));
   1988        #endif
   1989         }
   1990       #endif
   1991 
   1992        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   1993         if (rindex < kVst3InternalParameterCount)
   1994             return std::max(0.0, std::min(1.0, plain / 127));
   1995        #endif
   1996 
   1997         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   1998         DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
   1999 
   2000         return _getNormalizedParameterValue(index, plain);
   2001     }
   2002 
   2003     double getParameterNormalized(const v3_param_id rindex)
   2004     {
   2005        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2006         // TODO something to do here?
   2007         if (
   2008            #if !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS
   2009             rindex >= kVst3InternalParameterMidiCC_start &&
   2010            #endif
   2011             rindex <= kVst3InternalParameterMidiCC_end)
   2012             return 0.0;
   2013        #endif
   2014 
   2015       #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   2016         switch (rindex)
   2017         {
   2018        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2019         case kVst3InternalParameterBufferSize:
   2020         case kVst3InternalParameterSampleRate:
   2021        #endif
   2022        #if DISTRHO_PLUGIN_WANT_LATENCY
   2023         case kVst3InternalParameterLatency:
   2024        #endif
   2025        #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2026         case kVst3InternalParameterProgram:
   2027        #endif
   2028             return plainParameterToNormalized(rindex, fCachedParameterValues[rindex]);
   2029         }
   2030       #endif
   2031 
   2032         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   2033         DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
   2034 
   2035         return _getNormalizedParameterValue(index, fCachedParameterValues[kVst3InternalParameterBaseCount + index]);
   2036     }
   2037 
   2038     v3_result setParameterNormalized(const v3_param_id rindex, const double normalized)
   2039     {
   2040         DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG);
   2041 
   2042        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2043         // TODO something to do here?
   2044         if (
   2045            #if !DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS
   2046             rindex >= kVst3InternalParameterMidiCC_start &&
   2047            #endif
   2048             rindex <= kVst3InternalParameterMidiCC_end)
   2049             return V3_INVALID_ARG;
   2050        #endif
   2051 
   2052        #if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS
   2053         if (rindex < kVst3InternalParameterBaseCount)
   2054         {
   2055             fCachedParameterValues[rindex] = normalizedParameterToPlain(rindex, normalized);
   2056             int flags = 0;
   2057 
   2058             switch (rindex)
   2059             {
   2060            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2061             case kVst3InternalParameterBufferSize:
   2062                 fPlugin.setBufferSize(fCachedParameterValues[rindex], true);
   2063                 break;
   2064             case kVst3InternalParameterSampleRate:
   2065                 fPlugin.setSampleRate(fCachedParameterValues[rindex], true);
   2066                 break;
   2067            #endif
   2068            #if DISTRHO_PLUGIN_WANT_LATENCY
   2069             case kVst3InternalParameterLatency:
   2070                 flags = V3_RESTART_LATENCY_CHANGED;
   2071                 break;
   2072            #endif
   2073            #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2074             case kVst3InternalParameterProgram:
   2075                 flags = V3_RESTART_PARAM_VALUES_CHANGED;
   2076                 fCurrentProgram = fCachedParameterValues[rindex];
   2077                 fPlugin.loadProgram(fCurrentProgram);
   2078 
   2079                 for (uint32_t i=0; i<fParameterCount; ++i)
   2080                 {
   2081                     if (fPlugin.isParameterOutputOrTrigger(i))
   2082                         continue;
   2083                     fCachedParameterValues[kVst3InternalParameterBaseCount + i] = fPlugin.getParameterValue(i);
   2084                 }
   2085 
   2086                #if DISTRHO_PLUGIN_HAS_UI
   2087                 fParameterValueChangesForUI[kVst3InternalParameterProgram] = true;
   2088                #endif
   2089                 break;
   2090            #endif
   2091             }
   2092 
   2093             if (fComponentHandler != nullptr && flags != 0)
   2094                 v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, flags);
   2095 
   2096             return V3_OK;
   2097         }
   2098        #endif
   2099 
   2100         DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG);
   2101 
   2102        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2103         const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
   2104         DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, V3_INVALID_ARG);
   2105 
   2106         if (fIsComponent) {
   2107             DISTRHO_SAFE_ASSERT_RETURN(!fPlugin.isParameterOutputOrTrigger(index), V3_INVALID_ARG);
   2108         }
   2109 
   2110         _setNormalizedPluginParameterValue(index, normalized);
   2111        #endif
   2112 
   2113         return V3_OK;
   2114     }
   2115 
   2116     v3_result setComponentHandler(v3_component_handler** const handler) noexcept
   2117     {
   2118         fComponentHandler = handler;
   2119         return V3_OK;
   2120     }
   2121 
   2122 #if DISTRHO_PLUGIN_HAS_UI
   2123     // ----------------------------------------------------------------------------------------------------------------
   2124     // v3_connection_point interface calls
   2125 
   2126    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2127     void comp2ctrl_connect(v3_connection_point** const other)
   2128     {
   2129         fConnectionFromCompToCtrl = other;
   2130     }
   2131 
   2132     void comp2ctrl_disconnect()
   2133     {
   2134         fConnectionFromCompToCtrl = nullptr;
   2135     }
   2136 
   2137     v3_result comp2ctrl_notify(v3_message** const message)
   2138     {
   2139         const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
   2140         DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
   2141 
   2142         v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
   2143         DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
   2144 
   2145        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2146         if (std::strcmp(msgid, "midi") == 0)
   2147             return notify_midi(attrs);
   2148        #endif
   2149 
   2150        #if DISTRHO_PLUGIN_WANT_STATE
   2151         if (std::strcmp(msgid, "state-set") == 0)
   2152             return notify_state(attrs);
   2153        #endif
   2154 
   2155         d_stderr("comp2ctrl_notify received unknown msg '%s'", msgid);
   2156 
   2157         return V3_NOT_IMPLEMENTED;
   2158     }
   2159    #endif // DPF_VST3_USES_SEPARATE_CONTROLLER
   2160 
   2161     // ----------------------------------------------------------------------------------------------------------------
   2162 
   2163     void ctrl2view_connect(v3_connection_point** const other)
   2164     {
   2165         DISTRHO_SAFE_ASSERT(fConnectedToUI == false);
   2166 
   2167         fConnectionFromCtrlToView = other;
   2168         fConnectedToUI = false;
   2169     }
   2170 
   2171     void ctrl2view_disconnect()
   2172     {
   2173         fConnectedToUI = false;
   2174         fConnectionFromCtrlToView = nullptr;
   2175     }
   2176 
   2177     v3_result ctrl2view_notify(v3_message** const message)
   2178     {
   2179         DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCtrlToView != nullptr, V3_INTERNAL_ERR);
   2180 
   2181         const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
   2182         DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
   2183 
   2184         if (std::strcmp(msgid, "init") == 0)
   2185         {
   2186             fConnectedToUI = true;
   2187 
   2188            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2189             fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false;
   2190             sendParameterSetToUI(kVst3InternalParameterSampleRate,
   2191                                  fCachedParameterValues[kVst3InternalParameterSampleRate]);
   2192            #endif
   2193 
   2194            #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2195             fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
   2196             sendParameterSetToUI(kVst3InternalParameterProgram, fCurrentProgram);
   2197            #endif
   2198 
   2199            #if DISTRHO_PLUGIN_WANT_FULL_STATE
   2200             // Update current state from plugin side
   2201             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   2202             {
   2203                 const String& key(cit->first);
   2204                 fStateMap[key] = fPlugin.getStateValue(key);
   2205             }
   2206            #endif
   2207 
   2208            #if DISTRHO_PLUGIN_WANT_STATE
   2209             // Set state
   2210             for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
   2211             {
   2212                 const String& key(cit->first);
   2213                 const String& value(cit->second);
   2214 
   2215                 sendStateSetToUI(key, value);
   2216             }
   2217            #endif
   2218 
   2219             for (uint32_t i=0; i<fParameterCount; ++i)
   2220             {
   2221                 fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
   2222                 sendParameterSetToUI(kVst3InternalParameterCount + i,
   2223                                      fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
   2224             }
   2225 
   2226             sendReadyToUI();
   2227             return V3_OK;
   2228         }
   2229 
   2230         DISTRHO_SAFE_ASSERT_RETURN(fConnectedToUI, V3_INTERNAL_ERR);
   2231 
   2232         v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
   2233         DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
   2234 
   2235         if (std::strcmp(msgid, "idle") == 0)
   2236         {
   2237            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2238             if (fParameterValueChangesForUI[kVst3InternalParameterSampleRate])
   2239             {
   2240                 fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false;
   2241                 sendParameterSetToUI(kVst3InternalParameterSampleRate,
   2242                                      fCachedParameterValues[kVst3InternalParameterSampleRate]);
   2243             }
   2244            #endif
   2245 
   2246            #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2247             if (fParameterValueChangesForUI[kVst3InternalParameterProgram])
   2248             {
   2249                 fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
   2250                 sendParameterSetToUI(kVst3InternalParameterProgram, fCurrentProgram);
   2251             }
   2252            #endif
   2253 
   2254             for (uint32_t i=0; i<fParameterCount; ++i)
   2255             {
   2256                 if (! fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i])
   2257                     continue;
   2258 
   2259                 fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
   2260                 sendParameterSetToUI(kVst3InternalParameterCount + i,
   2261                                      fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
   2262             }
   2263 
   2264             sendReadyToUI();
   2265             return V3_OK;
   2266         }
   2267 
   2268         if (std::strcmp(msgid, "close") == 0)
   2269         {
   2270             fConnectedToUI = false;
   2271             return V3_OK;
   2272         }
   2273 
   2274         if (std::strcmp(msgid, "parameter-edit") == 0)
   2275         {
   2276             DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR);
   2277 
   2278             int64_t rindex;
   2279             int64_t started;
   2280             v3_result res;
   2281 
   2282             res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
   2283             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2284             DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount,
   2285                                             rindex, fParameterCount, V3_INTERNAL_ERR);
   2286             DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount,
   2287                                             rindex, fParameterCount, V3_INTERNAL_ERR);
   2288 
   2289             res = v3_cpp_obj(attrs)->get_int(attrs, "started", &started);
   2290             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2291             DISTRHO_SAFE_ASSERT_INT_RETURN(started == 0 || started == 1, started, V3_INTERNAL_ERR);
   2292 
   2293             return started != 0 ? v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex)
   2294                                 : v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex);
   2295         }
   2296 
   2297         if (std::strcmp(msgid, "parameter-set") == 0)
   2298         {
   2299             DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR);
   2300 
   2301             int64_t rindex;
   2302             double value;
   2303             v3_result res;
   2304 
   2305             res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
   2306             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2307             DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount,
   2308                                             rindex, fParameterCount, V3_INTERNAL_ERR);
   2309             DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount,
   2310                                             rindex, fParameterCount, V3_INTERNAL_ERR);
   2311 
   2312             res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value);
   2313             DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2314 
   2315             const uint32_t index = rindex - kVst3InternalParameterCount;
   2316             const double normalized = _getNormalizedParameterValue(index, value);
   2317 
   2318             fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value;
   2319 
   2320             if (! fPlugin.isParameterOutputOrTrigger(index))
   2321                 fPlugin.setParameterValue(index, value);
   2322 
   2323             return v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized);
   2324         }
   2325 
   2326        #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2327         if (std::strcmp(msgid, "midi") == 0)
   2328         {
   2329            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2330             DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR);
   2331             return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message);
   2332            #else
   2333             return notify_midi(attrs);
   2334            #endif
   2335         }
   2336        #endif
   2337 
   2338        #if DISTRHO_PLUGIN_WANT_STATE
   2339         if (std::strcmp(msgid, "state-set") == 0)
   2340         {
   2341             const v3_result res = notify_state(attrs);
   2342 
   2343            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2344             if (res != V3_OK)
   2345                 return res;
   2346 
   2347             // notify component of the change
   2348             DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR);
   2349             return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message);
   2350            #else
   2351             return res;
   2352            #endif
   2353         }
   2354        #endif
   2355 
   2356         d_stderr("ctrl2view_notify received unknown msg '%s'", msgid);
   2357 
   2358         return V3_NOT_IMPLEMENTED;
   2359     }
   2360 
   2361    #if DISTRHO_PLUGIN_WANT_STATE
   2362     v3_result notify_state(v3_attribute_list** const attrs)
   2363     {
   2364         int64_t keyLength = -1;
   2365         int64_t valueLength = -1;
   2366         v3_result res;
   2367 
   2368         res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength);
   2369         DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2370         DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR);
   2371 
   2372         res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength);
   2373         DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2374         DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR);
   2375 
   2376         int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1));
   2377         DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM);
   2378 
   2379         int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1));
   2380         DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM);
   2381 
   2382         res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*(keyLength+1));
   2383         DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res);
   2384 
   2385         if (valueLength != 0)
   2386         {
   2387             res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*(valueLength+1));
   2388             DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res);
   2389         }
   2390 
   2391         // do cheap inline conversion
   2392         char* const key = (char*)key16;
   2393         char* const value = (char*)value16;
   2394 
   2395         for (int64_t i=0; i<keyLength; ++i)
   2396             key[i] = key16[i];
   2397         for (int64_t i=0; i<valueLength; ++i)
   2398             value[i] = value16[i];
   2399 
   2400         key[keyLength] = '\0';
   2401         value[valueLength] = '\0';
   2402 
   2403         fPlugin.setState(key, value);
   2404 
   2405         // save this key as needed
   2406         if (fPlugin.wantStateKey(key))
   2407         {
   2408             const String dkey(key);
   2409             fStateMap[dkey] = value;
   2410         }
   2411 
   2412         std::free(key16);
   2413         std::free(value16);
   2414         return V3_OK;
   2415     }
   2416    #endif // DISTRHO_PLUGIN_WANT_STATE
   2417 
   2418    #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2419     v3_result notify_midi(v3_attribute_list** const attrs)
   2420     {
   2421         uint8_t* data;
   2422         uint32_t size;
   2423         v3_result res;
   2424 
   2425         res = v3_cpp_obj(attrs)->get_binary(attrs, "data", (const void**)&data, &size);
   2426         DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
   2427 
   2428         // known maximum size
   2429         DISTRHO_SAFE_ASSERT_UINT_RETURN(size == 3, size, V3_INTERNAL_ERR);
   2430 
   2431         return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM;
   2432     }
   2433    #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2434 #endif
   2435 
   2436     // ----------------------------------------------------------------------------------------------------------------
   2437 
   2438 private:
   2439     // Plugin
   2440     PluginExporter fPlugin;
   2441 
   2442     // VST3 stuff
   2443     v3_component_handler** fComponentHandler;
   2444   #if DISTRHO_PLUGIN_HAS_UI
   2445    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2446     v3_connection_point** fConnectionFromCompToCtrl;
   2447    #endif
   2448     v3_connection_point** fConnectionFromCtrlToView;
   2449     v3_host_application** const fHostApplication;
   2450   #endif
   2451 
   2452     // Temporary data
   2453     const uint32_t fParameterCount;
   2454     const uint32_t fVst3ParameterCount; // full offset + real
   2455     float* fCachedParameterValues; // basic offset + real
   2456     float* fDummyAudioBuffer;
   2457     bool* fParameterValuesChangedDuringProcessing; // basic offset + real
   2458    #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   2459     bool fEnabledInputs[DISTRHO_PLUGIN_NUM_INPUTS];
   2460    #endif
   2461    #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   2462     bool fEnabledOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS];
   2463    #endif
   2464    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2465     const bool fIsComponent;
   2466    #endif
   2467    #if DISTRHO_PLUGIN_HAS_UI
   2468     bool* fParameterValueChangesForUI; // basic offset + real
   2469     bool fConnectedToUI;
   2470    #endif
   2471    #if DISTRHO_PLUGIN_WANT_LATENCY
   2472     uint32_t fLastKnownLatency;
   2473    #endif
   2474   #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   2475     MidiEvent fMidiEvents[kMaxMidiEvents];
   2476    #if DISTRHO_PLUGIN_HAS_UI
   2477     SmallStackRingBuffer fNotesRingBuffer;
   2478    #endif
   2479   #endif
   2480    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   2481     v3_event_list** fHostEventOutputHandle;
   2482    #endif
   2483    #if DISTRHO_PLUGIN_WANT_PROGRAMS
   2484     uint32_t fCurrentProgram;
   2485     const uint32_t fProgramCountMinusOne;
   2486    #endif
   2487    #if DISTRHO_PLUGIN_WANT_STATE
   2488     StringMap fStateMap;
   2489    #endif
   2490    #if DISTRHO_PLUGIN_WANT_TIMEPOS
   2491     TimePosition fTimePosition;
   2492    #endif
   2493 
   2494     // ----------------------------------------------------------------------------------------------------------------
   2495     // helper functions for dealing with buses
   2496 
   2497    #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   2498     template<bool isInput>
   2499     void fillInBusInfoDetails()
   2500     {
   2501         constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
   2502         BusInfo& busInfo(isInput ? inputBuses : outputBuses);
   2503         bool* const enabledPorts = isInput
   2504                                 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   2505                                  ? fEnabledInputs
   2506                                 #else
   2507                                  ? nullptr
   2508                                 #endif
   2509                                 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   2510                                  : fEnabledOutputs;
   2511                                 #else
   2512                                  : nullptr;
   2513                                 #endif
   2514 
   2515         std::vector<uint32_t> visitedPortGroups;
   2516         for (uint32_t i=0; i<numPorts; ++i)
   2517         {
   2518             const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2519 
   2520             if (port.groupId != kPortGroupNone)
   2521             {
   2522                 const std::vector<uint32_t>::iterator end = visitedPortGroups.end();
   2523                 if (std::find(visitedPortGroups.begin(), end, port.groupId) == end)
   2524                 {
   2525                     visitedPortGroups.push_back(port.groupId);
   2526                     ++busInfo.groups;
   2527                 }
   2528                 ++busInfo.groupPorts;
   2529                 continue;
   2530             }
   2531 
   2532             if (port.hints & kAudioPortIsCV)
   2533                 ++busInfo.cvPorts;
   2534             else if (port.hints & kAudioPortIsSidechain)
   2535                 ++busInfo.sidechainPorts;
   2536             else
   2537                 ++busInfo.audioPorts;
   2538         }
   2539 
   2540         if (busInfo.audioPorts != 0)
   2541             busInfo.audio = 1;
   2542         if (busInfo.sidechainPorts != 0)
   2543             busInfo.sidechain = 1;
   2544 
   2545         uint32_t busIdForCV = 0;
   2546         const std::vector<uint32_t>::iterator vpgStart = visitedPortGroups.begin();
   2547         const std::vector<uint32_t>::iterator vpgEnd = visitedPortGroups.end();
   2548 
   2549         for (uint32_t i=0; i<numPorts; ++i)
   2550         {
   2551             AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2552 
   2553             if (port.groupId != kPortGroupNone)
   2554             {
   2555                 port.busId = std::find(vpgStart, vpgEnd, port.groupId) - vpgStart;
   2556 
   2557                 if (busInfo.audio == 0 && (port.hints & kAudioPortIsSidechain) == 0x0)
   2558                     enabledPorts[i] = true;
   2559             }
   2560             else
   2561             {
   2562                 if (port.hints & kAudioPortIsCV)
   2563                 {
   2564                     port.busId = busInfo.audio + busInfo.sidechain + busIdForCV++;
   2565                 }
   2566                 else if (port.hints & kAudioPortIsSidechain)
   2567                 {
   2568                     port.busId = busInfo.audio;
   2569                 }
   2570                 else
   2571                 {
   2572                     port.busId = 0;
   2573                     enabledPorts[i] = true;
   2574                 }
   2575 
   2576                 port.busId += busInfo.groups;
   2577             }
   2578         }
   2579     }
   2580 
   2581     template<bool isInput>
   2582     v3_result getAudioBusInfo(const uint32_t busId, v3_bus_info* const info) const
   2583     {
   2584         constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
   2585         const BusInfo& busInfo(isInput ? inputBuses : outputBuses);
   2586 
   2587         int32_t numChannels;
   2588         uint32_t flags;
   2589         v3_bus_types busType;
   2590         v3_str_128 busName = {};
   2591 
   2592         if (busId < busInfo.groups)
   2593         {
   2594             numChannels = 0;
   2595 
   2596             for (uint32_t i=0; i<numPorts; ++i)
   2597             {
   2598                 const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2599 
   2600                 if (port.busId == busId)
   2601                 {
   2602                     const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId));
   2603 
   2604                     switch (port.groupId)
   2605                     {
   2606                     case kPortGroupStereo:
   2607                     case kPortGroupMono:
   2608                         if (busId == 0)
   2609                         {
   2610                             strncpy_utf16(busName, isInput ? "Audio Input" : "Audio Output", 128);
   2611                             break;
   2612                         }
   2613                     // fall-through
   2614                     default:
   2615                         if (group.name.isNotEmpty())
   2616                             strncpy_utf16(busName, group.name, 128);
   2617                         else
   2618                             strncpy_utf16(busName, port.name, 128);
   2619                         break;
   2620                     }
   2621 
   2622                     numChannels = fPlugin.getAudioPortCountWithGroupId(isInput, port.groupId);
   2623 
   2624                     if (port.hints & kAudioPortIsCV)
   2625                     {
   2626                         busType = V3_MAIN;
   2627                         flags = V3_IS_CONTROL_VOLTAGE;
   2628                     }
   2629                     else if (port.hints & kAudioPortIsSidechain)
   2630                     {
   2631                         busType = V3_AUX;
   2632                         flags = 0;
   2633                     }
   2634                     else
   2635                     {
   2636                         busType = V3_MAIN;
   2637                         flags = busInfo.audio == 0 ? V3_DEFAULT_ACTIVE : 0;
   2638                     }
   2639                     break;
   2640                 }
   2641             }
   2642 
   2643             DISTRHO_SAFE_ASSERT_RETURN(numChannels != 0, V3_INTERNAL_ERR);
   2644         }
   2645         else
   2646         {
   2647             switch (busId - busInfo.groups)
   2648             {
   2649             case 0:
   2650                 if (busInfo.audio)
   2651                 {
   2652                     numChannels = busInfo.audioPorts;
   2653                     busType = V3_MAIN;
   2654                     flags = V3_DEFAULT_ACTIVE;
   2655                     break;
   2656                 }
   2657             // fall-through
   2658             case 1:
   2659                 if (busInfo.sidechain)
   2660                 {
   2661                     numChannels = busInfo.sidechainPorts;
   2662                     busType = V3_AUX;
   2663                     flags = 0;
   2664                     break;
   2665                 }
   2666             // fall-through
   2667             default:
   2668                 numChannels = 1;
   2669                 busType = V3_MAIN;
   2670                 flags = V3_IS_CONTROL_VOLTAGE;
   2671                 break;
   2672             }
   2673 
   2674             if (busType == V3_MAIN && flags != V3_IS_CONTROL_VOLTAGE)
   2675             {
   2676                 strncpy_utf16(busName, isInput ? "Audio Input" : "Audio Output", 128);
   2677             }
   2678             else
   2679             {
   2680                 for (uint32_t i=0; i<numPorts; ++i)
   2681                 {
   2682                     const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2683 
   2684                     if (port.busId == busId)
   2685                     {
   2686                         String groupName;
   2687                         if (busInfo.groups)
   2688                             groupName = fPlugin.getPortGroupById(port.groupId).name;
   2689                         if (groupName.isEmpty())
   2690                             groupName = port.name;
   2691                         strncpy_utf16(busName, groupName, 128);
   2692                         break;
   2693                     }
   2694                 }
   2695             }
   2696         }
   2697 
   2698         // d_debug("getAudioBusInfo %d %d %d", (int)isInput, busId, numChannels);
   2699         std::memset(info, 0, sizeof(v3_bus_info));
   2700         info->media_type = V3_AUDIO;
   2701         info->direction = isInput ? V3_INPUT : V3_OUTPUT;
   2702         info->channel_count = numChannels;
   2703         std::memcpy(info->bus_name, busName, sizeof(busName));
   2704         info->bus_type = busType;
   2705         info->flags = flags;
   2706         return V3_OK;
   2707     }
   2708 
   2709     // someone please tell me what is up with these..
   2710     static inline v3_speaker_arrangement portCountToSpeaker(const uint32_t portCount)
   2711     {
   2712         DISTRHO_SAFE_ASSERT_RETURN(portCount != 0, 0);
   2713 
   2714         switch (portCount)
   2715         {
   2716         // regular mono
   2717         case 1: return V3_SPEAKER_M;
   2718         // regular stereo
   2719         case 2: return V3_SPEAKER_L | V3_SPEAKER_R;
   2720         // stereo with center channel
   2721         case 3: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_C;
   2722         // stereo with surround (quadro)
   2723         case 4: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS;
   2724         // regular 5.0
   2725         case 5: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_C;
   2726         // regular 6.0
   2727         case 6: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR;
   2728         // regular 7.0
   2729         case 7: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C;
   2730         // regular 8.0
   2731         case 8: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C | V3_SPEAKER_S;
   2732         // regular 8.1
   2733         case 9: return V3_SPEAKER_L | V3_SPEAKER_R | V3_SPEAKER_LS | V3_SPEAKER_RS | V3_SPEAKER_SL | V3_SPEAKER_SR | V3_SPEAKER_C | V3_SPEAKER_S | V3_SPEAKER_LFE;
   2734         // cinema 10.0
   2735         case 10: return (
   2736             V3_SPEAKER_L | V3_SPEAKER_R |
   2737             V3_SPEAKER_LS | V3_SPEAKER_RS |
   2738             V3_SPEAKER_SL | V3_SPEAKER_SR |
   2739             V3_SPEAKER_LC | V3_SPEAKER_RC |
   2740             V3_SPEAKER_C | V3_SPEAKER_S);
   2741         // cinema 10.1
   2742         case 11: return (
   2743             V3_SPEAKER_L | V3_SPEAKER_R |
   2744             V3_SPEAKER_LS | V3_SPEAKER_RS |
   2745             V3_SPEAKER_SL | V3_SPEAKER_SR |
   2746             V3_SPEAKER_LC | V3_SPEAKER_RC |
   2747             V3_SPEAKER_C | V3_SPEAKER_S | V3_SPEAKER_LFE);
   2748         default:
   2749             d_stderr("portCountToSpeaker error: got weirdly big number ports %u in a single bus", portCount);
   2750             return 0;
   2751         }
   2752     }
   2753 
   2754     template<bool isInput>
   2755     v3_speaker_arrangement getSpeakerArrangementForAudioPort(const BusInfo& busInfo, const uint32_t portGroupId, const uint32_t busId) const noexcept
   2756     {
   2757         switch (portGroupId)
   2758         {
   2759         case kPortGroupMono:
   2760             return V3_SPEAKER_M;
   2761         case kPortGroupStereo:
   2762             return V3_SPEAKER_L | V3_SPEAKER_R;
   2763         }
   2764 
   2765         if (busId < busInfo.groups)
   2766             return portCountToSpeaker(fPlugin.getAudioPortCountWithGroupId(isInput, portGroupId));
   2767 
   2768         if (busInfo.audio != 0 && busId == busInfo.groups)
   2769             return portCountToSpeaker(busInfo.audioPorts);
   2770 
   2771         if (busInfo.sidechain != 0 && busId == busInfo.groups + busInfo.audio)
   2772             return portCountToSpeaker(busInfo.sidechainPorts);
   2773 
   2774         return V3_SPEAKER_M;
   2775     }
   2776 
   2777     template<bool isInput>
   2778     bool getAudioBusArrangement(uint32_t busId, v3_speaker_arrangement* const speaker) const
   2779     {
   2780         constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
   2781         const BusInfo& busInfo(isInput ? inputBuses : outputBuses);
   2782 
   2783         for (uint32_t i=0; i<numPorts; ++i)
   2784         {
   2785             const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2786 
   2787             if (port.busId != busId)
   2788             {
   2789                 // d_debug("port.busId != busId: %d %d", port.busId, busId);
   2790                 continue;
   2791             }
   2792 
   2793             *speaker = getSpeakerArrangementForAudioPort<isInput>(busInfo, port.groupId, busId);
   2794             // d_debug("getAudioBusArrangement %d enabled by value %lx", busId, *speaker);
   2795             return true;
   2796         }
   2797 
   2798         return false;
   2799     }
   2800 
   2801     template<bool isInput>
   2802     bool setAudioBusArrangement(v3_speaker_arrangement* const speakers, const uint32_t numBuses)
   2803     {
   2804         constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
   2805         BusInfo& busInfo(isInput ? inputBuses : outputBuses);
   2806         bool* const enabledPorts = isInput
   2807                                 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
   2808                                  ? fEnabledInputs
   2809                                 #else
   2810                                  ? nullptr
   2811                                 #endif
   2812                                 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
   2813                                  : fEnabledOutputs;
   2814                                 #else
   2815                                  : nullptr;
   2816                                 #endif
   2817 
   2818         bool ok = true;
   2819 
   2820         for (uint32_t busId=0; busId<numBuses; ++busId)
   2821         {
   2822             const v3_speaker_arrangement arr = speakers[busId];
   2823 
   2824             // d_debug("setAudioBusArrangement %d %d | %d %lx", (int)isInput, numBuses, busId, arr);
   2825 
   2826             for (uint32_t i=0; i<numPorts; ++i)
   2827             {
   2828                 AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2829 
   2830                 if (port.busId != busId)
   2831                 {
   2832                     // d_debug("setAudioBusArrangement port.busId != busId: %d %d", port.busId, busId);
   2833                     continue;
   2834                 }
   2835 
   2836                 // get the only valid speaker arrangement for this bus, assuming enabled
   2837                 const v3_speaker_arrangement earr = getSpeakerArrangementForAudioPort<isInput>(busInfo, port.groupId, busId);
   2838 
   2839                 // fail if host tries to map it to anything else
   2840                 // FIXME should we allow to map speaker to zero as a way to disable it?
   2841                 if (earr != arr /* && arr != 0 */)
   2842                 {
   2843                     ok = false;
   2844                     continue;
   2845                 }
   2846 
   2847                 enabledPorts[i] = arr != 0;
   2848             }
   2849         }
   2850 
   2851         // disable any buses outside of the requested arrangement
   2852         const uint32_t totalBuses = busInfo.audio + busInfo.sidechain + busInfo.groups + busInfo.cvPorts;
   2853 
   2854         for (uint32_t busId=numBuses; busId<totalBuses; ++busId)
   2855         {
   2856             for (uint32_t i=0; i<numPorts; ++i)
   2857             {
   2858                 const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
   2859 
   2860                 if (port.busId == busId)
   2861                 {
   2862                     enabledPorts[i] = false;
   2863                     break;
   2864                 }
   2865             }
   2866         }
   2867 
   2868         return ok;
   2869     }
   2870    #endif
   2871 
   2872     // ----------------------------------------------------------------------------------------------------------------
   2873     // helper functions called during process, cannot block
   2874 
   2875     void updateParametersFromProcessing(v3_param_changes** const outparamsptr, const int32_t offset)
   2876     {
   2877         DISTRHO_SAFE_ASSERT_RETURN(outparamsptr != nullptr,);
   2878 
   2879         float curValue, defValue;
   2880         double normalized;
   2881 
   2882        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   2883         for (v3_param_id i=kVst3InternalParameterBufferSize; i<=kVst3InternalParameterSampleRate; ++i)
   2884         {
   2885             if (! fParameterValuesChangedDuringProcessing[i])
   2886                 continue;
   2887 
   2888             normalized = plainParameterToNormalized(i, fCachedParameterValues[i]);
   2889             fParameterValuesChangedDuringProcessing[i] = false;
   2890             addParameterDataToHostOutputEvents(outparamsptr, i, normalized);
   2891         }
   2892        #endif
   2893 
   2894         for (uint32_t i=0; i<fParameterCount; ++i)
   2895         {
   2896             if (fPlugin.isParameterOutput(i))
   2897             {
   2898                 // NOTE: no output parameter support in VST3, simulate it here
   2899                 curValue = fPlugin.getParameterValue(i);
   2900 
   2901                 if (d_isEqual(curValue, fCachedParameterValues[kVst3InternalParameterBaseCount + i]))
   2902                     continue;
   2903             }
   2904             else if (fPlugin.isParameterTrigger(i))
   2905             {
   2906                 // NOTE: no trigger parameter support in VST3, simulate it here
   2907                 defValue = fPlugin.getParameterDefault(i);
   2908                 curValue = fPlugin.getParameterValue(i);
   2909 
   2910                 if (d_isEqual(curValue, defValue))
   2911                     continue;
   2912 
   2913                 curValue = defValue;
   2914                 fPlugin.setParameterValue(i, curValue);
   2915             }
   2916             else if (fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + i])
   2917             {
   2918                 fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + i] = false;
   2919                 curValue = fPlugin.getParameterValue(i);
   2920             }
   2921             else
   2922             {
   2923                 continue;
   2924             }
   2925 
   2926             fCachedParameterValues[kVst3InternalParameterBaseCount + i] = curValue;
   2927            #if DISTRHO_PLUGIN_HAS_UI
   2928             fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = true;
   2929            #endif
   2930 
   2931             normalized = _getNormalizedParameterValue(i, curValue);
   2932 
   2933             if (! addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterCount + i, normalized, offset))
   2934                 break;
   2935         }
   2936 
   2937        #if DISTRHO_PLUGIN_WANT_LATENCY
   2938         const uint32_t latency = fPlugin.getLatency();
   2939 
   2940         if (fLastKnownLatency != latency)
   2941         {
   2942             fLastKnownLatency = latency;
   2943 
   2944             normalized = plainParameterToNormalized(kVst3InternalParameterLatency,
   2945                                                     fCachedParameterValues[kVst3InternalParameterLatency]);
   2946             addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, normalized);
   2947         }
   2948        #endif
   2949     }
   2950 
   2951     bool addParameterDataToHostOutputEvents(v3_param_changes** const outparamsptr,
   2952                                             const v3_param_id paramId,
   2953                                             const double normalized,
   2954                                             const int32_t offset = 0)
   2955     {
   2956         int32_t index = 0;
   2957         v3_param_value_queue** const queue = v3_cpp_obj(outparamsptr)->add_param_data(outparamsptr,
   2958                                                                                       &paramId, &index);
   2959         DISTRHO_SAFE_ASSERT_RETURN(queue != nullptr, false);
   2960         DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj(queue)->add_point(queue, 0, normalized, &index) == V3_OK, false);
   2961 
   2962         /* FLStudio gets confused with this one, skip it for now
   2963         if (offset != 0)
   2964             v3_cpp_obj(queue)->add_point(queue, offset, normalized, &index);
   2965         */
   2966 
   2967         return true;
   2968 
   2969         // unused at the moment, buggy VST3 hosts :/
   2970         (void)offset;
   2971     }
   2972 
   2973    #if DISTRHO_PLUGIN_HAS_UI
   2974     // ----------------------------------------------------------------------------------------------------------------
   2975     // helper functions called during message passing, can block
   2976 
   2977     v3_message** createMessage(const char* const id) const
   2978     {
   2979         DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr);
   2980 
   2981         v3_tuid iid;
   2982         memcpy(iid, v3_message_iid, sizeof(v3_tuid));
   2983         v3_message** msg = nullptr;
   2984         const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg);
   2985         DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
   2986         DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);
   2987 
   2988         v3_cpp_obj(msg)->set_message_id(msg, id);
   2989         return msg;
   2990     }
   2991 
   2992     void sendParameterSetToUI(const v3_param_id rindex, const double value) const
   2993     {
   2994         v3_message** const message = createMessage("parameter-set");
   2995         DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
   2996 
   2997         v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
   2998         DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
   2999 
   3000         v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
   3001         v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
   3002         v3_cpp_obj(attrlist)->set_float(attrlist, "value", value);
   3003         v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
   3004 
   3005         v3_cpp_obj_unref(message);
   3006     }
   3007 
   3008     void sendStateSetToUI(const char* const key, const char* const value) const
   3009     {
   3010         v3_message** const message = createMessage("state-set");
   3011         DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
   3012 
   3013         v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
   3014         DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
   3015 
   3016         v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
   3017         v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key));
   3018         v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value));
   3019         v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key));
   3020         v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value));
   3021         v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
   3022 
   3023         v3_cpp_obj_unref(message);
   3024     }
   3025 
   3026     void sendReadyToUI() const
   3027     {
   3028         v3_message** const message = createMessage("ready");
   3029         DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
   3030 
   3031         v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
   3032         DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
   3033 
   3034         v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
   3035         v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
   3036 
   3037         v3_cpp_obj_unref(message);
   3038     }
   3039    #endif
   3040 
   3041     // ----------------------------------------------------------------------------------------------------------------
   3042     // DPF callbacks
   3043 
   3044    #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
   3045     bool requestParameterValueChange(const uint32_t index, float)
   3046     {
   3047         fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + index] = true;
   3048         return true;
   3049     }
   3050 
   3051     static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
   3052     {
   3053         return static_cast<PluginVst3*>(ptr)->requestParameterValueChange(index, value);
   3054     }
   3055    #endif
   3056 
   3057    #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
   3058     bool writeMidi(const MidiEvent& midiEvent)
   3059     {
   3060         DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fHostEventOutputHandle != nullptr, false);
   3061 
   3062         v3_event event;
   3063         std::memset(&event, 0, sizeof(event));
   3064         event.sample_offset = midiEvent.frame;
   3065 
   3066         const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
   3067 
   3068         switch (data[0] & 0xf0)
   3069         {
   3070         case 0x80:
   3071             event.type = V3_EVENT_NOTE_OFF;
   3072             event.note_off.channel = data[0] & 0xf;
   3073             event.note_off.pitch = data[1];
   3074             event.note_off.velocity = (float)data[2] / 127.0f;
   3075             // int32_t note_id;
   3076             // float tuning;
   3077             break;
   3078         case 0x90:
   3079             event.type = V3_EVENT_NOTE_ON;
   3080             event.note_on.channel = data[0] & 0xf;
   3081             event.note_on.pitch = data[1];
   3082             // float tuning;
   3083             event.note_on.velocity = (float)data[2] / 127.0f;
   3084             // int32_t length;
   3085             // int32_t note_id;
   3086             break;
   3087         case 0xA0:
   3088             event.type = V3_EVENT_POLY_PRESSURE;
   3089             event.poly_pressure.channel = data[0] & 0xf;
   3090             event.poly_pressure.pitch = data[1];
   3091             event.poly_pressure.pressure = (float)data[2] / 127.0f;
   3092             // int32_t note_id;
   3093             break;
   3094         case 0xB0:
   3095             event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
   3096             event.midi_cc_out.channel = data[0] & 0xf;
   3097             event.midi_cc_out.cc_number = data[1];
   3098             event.midi_cc_out.value = data[2];
   3099             if (midiEvent.size == 4)
   3100                 event.midi_cc_out.value2 = data[3];
   3101             break;
   3102         /* TODO how do we deal with program changes??
   3103         case 0xC0:
   3104             break;
   3105         */
   3106         case 0xD0:
   3107             event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
   3108             event.midi_cc_out.channel = data[0] & 0xf;
   3109             event.midi_cc_out.cc_number = 128;
   3110             event.midi_cc_out.value = data[1];
   3111             break;
   3112         case 0xE0:
   3113             event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
   3114             event.midi_cc_out.channel = data[0] & 0xf;
   3115             event.midi_cc_out.cc_number = 129;
   3116             event.midi_cc_out.value = data[1];
   3117             event.midi_cc_out.value2 = data[2];
   3118             break;
   3119         default:
   3120             return true;
   3121         }
   3122 
   3123         return v3_cpp_obj(fHostEventOutputHandle)->add_event(fHostEventOutputHandle, &event) == V3_OK;
   3124     }
   3125 
   3126     static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
   3127     {
   3128         return static_cast<PluginVst3*>(ptr)->writeMidi(midiEvent);
   3129     }
   3130    #endif
   3131 };
   3132 
   3133 // --------------------------------------------------------------------------------------------------------------------
   3134 
   3135 /**
   3136  * VST3 low-level pointer thingies follow, proceed with care.
   3137  */
   3138 
   3139 // --------------------------------------------------------------------------------------------------------------------
   3140 // v3_funknown for static instances
   3141 
   3142 static uint32_t V3_API dpf_static_ref(void*) { return 1; }
   3143 static uint32_t V3_API dpf_static_unref(void*) { return 0; }
   3144 
   3145 // --------------------------------------------------------------------------------------------------------------------
   3146 // v3_funknown for classes with a single instance
   3147 
   3148 template<class T>
   3149 static uint32_t V3_API dpf_single_instance_ref(void* const self)
   3150 {
   3151     return ++(*static_cast<T**>(self))->refcounter;
   3152 }
   3153 
   3154 template<class T>
   3155 static uint32_t V3_API dpf_single_instance_unref(void* const self)
   3156 {
   3157     return --(*static_cast<T**>(self))->refcounter;
   3158 }
   3159 
   3160 // --------------------------------------------------------------------------------------------------------------------
   3161 // Store components that we can't delete properly, to be cleaned up on module unload
   3162 
   3163 struct dpf_component;
   3164 
   3165 static std::vector<dpf_component**> gComponentGarbage;
   3166 
   3167 static uint32_t handleUncleanComponent(dpf_component** const componentptr)
   3168 {
   3169     gComponentGarbage.push_back(componentptr);
   3170     return 0;
   3171 }
   3172 
   3173 #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3174 // --------------------------------------------------------------------------------------------------------------------
   3175 // Store controllers that we can't delete properly, to be cleaned up on module unload
   3176 
   3177 struct dpf_edit_controller;
   3178 
   3179 static std::vector<dpf_edit_controller**> gControllerGarbage;
   3180 
   3181 static uint32_t handleUncleanController(dpf_edit_controller** const controllerptr)
   3182 {
   3183     gControllerGarbage.push_back(controllerptr);
   3184     return 0;
   3185 }
   3186 
   3187 // --------------------------------------------------------------------------------------------------------------------
   3188 // dpf_comp2ctrl_connection_point
   3189 
   3190 struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp {
   3191     std::atomic_int refcounter;
   3192     ScopedPointer<PluginVst3>& vst3;
   3193     v3_connection_point** other;
   3194 
   3195     dpf_comp2ctrl_connection_point(ScopedPointer<PluginVst3>& v)
   3196         : refcounter(1),
   3197           vst3(v),
   3198           other(nullptr)
   3199     {
   3200         // v3_funknown, single instance
   3201         query_interface = query_interface_connection_point;
   3202         ref = dpf_single_instance_ref<dpf_comp2ctrl_connection_point>;
   3203         unref = dpf_single_instance_unref<dpf_comp2ctrl_connection_point>;
   3204 
   3205         // v3_connection_point
   3206         point.connect = connect;
   3207         point.disconnect = disconnect;
   3208         point.notify = notify;
   3209     }
   3210 
   3211     // ----------------------------------------------------------------------------------------------------------------
   3212     // v3_funknown
   3213 
   3214     static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface)
   3215     {
   3216         dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
   3217 
   3218         if (v3_tuid_match(iid, v3_funknown_iid) ||
   3219             v3_tuid_match(iid, v3_connection_point_iid))
   3220         {
   3221             d_debug("dpf_comp2ctrl_connection_point => %p %s %p | OK", self, tuid2str(iid), iface);
   3222             ++point->refcounter;
   3223             *iface = self;
   3224             return V3_OK;
   3225         }
   3226 
   3227         d_debug("dpf_comp2ctrl_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   3228 
   3229         *iface = nullptr;
   3230         return V3_NO_INTERFACE;
   3231     }
   3232 
   3233     // ----------------------------------------------------------------------------------------------------------------
   3234     // v3_connection_point
   3235 
   3236     static v3_result V3_API connect(void* const self, v3_connection_point** const other)
   3237     {
   3238         d_debug("dpf_comp2ctrl_connection_point::connect => %p %p", self, other);
   3239         dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
   3240         DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
   3241         DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG);
   3242 
   3243         point->other = other;
   3244 
   3245         if (PluginVst3* const vst3 = point->vst3)
   3246             vst3->comp2ctrl_connect(other);
   3247 
   3248         return V3_OK;
   3249     }
   3250 
   3251     static v3_result V3_API disconnect(void* const self, v3_connection_point** const other)
   3252     {
   3253         d_debug("dpf_comp2ctrl_connection_point => %p %p", self, other);
   3254         dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
   3255         DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
   3256         DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG);
   3257 
   3258         if (PluginVst3* const vst3 = point->vst3)
   3259             vst3->comp2ctrl_disconnect();
   3260 
   3261         point->other = nullptr;
   3262 
   3263         return V3_OK;
   3264     }
   3265 
   3266     static v3_result V3_API notify(void* const self, v3_message** const message)
   3267     {
   3268         dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
   3269 
   3270         PluginVst3* const vst3 = point->vst3;
   3271         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3272 
   3273         v3_connection_point** const other = point->other;
   3274         DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED);
   3275 
   3276         v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
   3277         DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG);
   3278 
   3279         int64_t target = 0;
   3280         const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target);
   3281         DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res);
   3282         DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1, target, V3_INTERNAL_ERR);
   3283 
   3284         // view -> edit controller -> component
   3285         return vst3->comp2ctrl_notify(message);
   3286     }
   3287 };
   3288 #endif // DPF_VST3_USES_SEPARATE_CONTROLLER
   3289 
   3290 #if DISTRHO_PLUGIN_HAS_UI
   3291 // --------------------------------------------------------------------------------------------------------------------
   3292 // dpf_ctrl2view_connection_point
   3293 
   3294 struct dpf_ctrl2view_connection_point : v3_connection_point_cpp {
   3295     ScopedPointer<PluginVst3>& vst3;
   3296     v3_connection_point** other;
   3297 
   3298     dpf_ctrl2view_connection_point(ScopedPointer<PluginVst3>& v)
   3299         : vst3(v),
   3300           other(nullptr)
   3301     {
   3302         // v3_funknown, single instance, used internally
   3303         query_interface = nullptr;
   3304         ref = nullptr;
   3305         unref = nullptr;
   3306 
   3307         // v3_connection_point
   3308         point.connect = connect;
   3309         point.disconnect = disconnect;
   3310         point.notify = notify;
   3311     }
   3312 
   3313     // ----------------------------------------------------------------------------------------------------------------
   3314     // v3_connection_point
   3315 
   3316     static v3_result V3_API connect(void* const self, v3_connection_point** const other)
   3317     {
   3318         d_debug("dpf_ctrl2view_connection_point::connect => %p %p", self, other);
   3319         dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
   3320         DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
   3321         DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG);
   3322 
   3323         point->other = other;
   3324 
   3325         if (PluginVst3* const vst3 = point->vst3)
   3326             vst3->ctrl2view_connect(other);
   3327 
   3328         return V3_OK;
   3329     }
   3330 
   3331     static v3_result V3_API disconnect(void* const self, v3_connection_point** const other)
   3332     {
   3333         d_debug("dpf_ctrl2view_connection_point::disconnect => %p %p", self, other);
   3334         dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
   3335         DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
   3336         DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG);
   3337 
   3338         if (PluginVst3* const vst3 = point->vst3)
   3339             vst3->ctrl2view_disconnect();
   3340 
   3341         v3_cpp_obj_unref(point->other);
   3342         point->other = nullptr;
   3343 
   3344         return V3_OK;
   3345     }
   3346 
   3347     static v3_result V3_API notify(void* const self, v3_message** const message)
   3348     {
   3349         dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
   3350 
   3351         PluginVst3* const vst3 = point->vst3;
   3352         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3353 
   3354         v3_connection_point** const other = point->other;
   3355         DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED);
   3356 
   3357         v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
   3358         DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG);
   3359 
   3360         int64_t target = 0;
   3361         const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target);
   3362         DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res);
   3363         DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR);
   3364 
   3365         if (target == 1)
   3366         {
   3367             // view -> edit controller
   3368             return vst3->ctrl2view_notify(message);
   3369         }
   3370         else
   3371         {
   3372             // edit controller -> view
   3373             return v3_cpp_obj(other)->notify(other, message);
   3374         }
   3375     }
   3376 };
   3377 #endif // DISTRHO_PLUGIN_HAS_UI
   3378 
   3379 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   3380 // --------------------------------------------------------------------------------------------------------------------
   3381 // dpf_midi_mapping
   3382 
   3383 struct dpf_midi_mapping : v3_midi_mapping_cpp {
   3384     dpf_midi_mapping()
   3385     {
   3386         // v3_funknown, static
   3387         query_interface = query_interface_midi_mapping;
   3388         ref = dpf_static_ref;
   3389         unref = dpf_static_unref;
   3390 
   3391         // v3_midi_mapping
   3392         map.get_midi_controller_assignment = get_midi_controller_assignment;
   3393     }
   3394 
   3395     // ----------------------------------------------------------------------------------------------------------------
   3396     // v3_funknown
   3397 
   3398     static v3_result V3_API query_interface_midi_mapping(void* const self, const v3_tuid iid, void** const iface)
   3399     {
   3400         if (v3_tuid_match(iid, v3_funknown_iid) ||
   3401             v3_tuid_match(iid, v3_midi_mapping_iid))
   3402         {
   3403             d_debug("query_interface_midi_mapping => %p %s %p | OK", self, tuid2str(iid), iface);
   3404             *iface = self;
   3405             return V3_OK;
   3406         }
   3407 
   3408         d_debug("query_interface_midi_mapping => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   3409 
   3410         *iface = nullptr;
   3411         return V3_NO_INTERFACE;
   3412     }
   3413 
   3414     // ----------------------------------------------------------------------------------------------------------------
   3415     // v3_midi_mapping
   3416 
   3417     static v3_result V3_API get_midi_controller_assignment(void*, const int32_t bus, const int16_t channel, const int16_t cc, v3_param_id* const id)
   3418     {
   3419         DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE);
   3420         DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE);
   3421         DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE);
   3422 
   3423         *id = kVst3InternalParameterMidiCC_start + channel * 130 + cc;
   3424         return V3_TRUE;
   3425     }
   3426 
   3427     DISTRHO_PREVENT_HEAP_ALLOCATION
   3428 };
   3429 #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT
   3430 
   3431 // --------------------------------------------------------------------------------------------------------------------
   3432 // dpf_edit_controller
   3433 
   3434 struct dpf_edit_controller : v3_edit_controller_cpp {
   3435     std::atomic_int refcounter;
   3436    #if DISTRHO_PLUGIN_HAS_UI
   3437     ScopedPointer<dpf_ctrl2view_connection_point> connectionCtrl2View;
   3438    #endif
   3439    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3440     ScopedPointer<dpf_comp2ctrl_connection_point> connectionComp2Ctrl;
   3441     ScopedPointer<PluginVst3> vst3;
   3442    #else
   3443     ScopedPointer<PluginVst3>& vst3;
   3444     bool initialized;
   3445    #endif
   3446     // cached values
   3447     v3_component_handler** handler;
   3448     v3_host_application** const hostApplicationFromFactory;
   3449    #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   3450     v3_host_application** const hostApplicationFromComponent;
   3451     v3_host_application** hostApplicationFromComponentInitialize;
   3452    #endif
   3453     v3_host_application** hostApplicationFromInitialize;
   3454 
   3455    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3456     dpf_edit_controller(v3_host_application** const hostApp)
   3457         : refcounter(1),
   3458           vst3(nullptr),
   3459    #else
   3460     dpf_edit_controller(ScopedPointer<PluginVst3>& v, v3_host_application** const hostApp, v3_host_application** const hostComp)
   3461         : refcounter(1),
   3462           vst3(v),
   3463           initialized(false),
   3464    #endif
   3465           handler(nullptr),
   3466           hostApplicationFromFactory(hostApp),
   3467          #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   3468           hostApplicationFromComponent(hostComp),
   3469           hostApplicationFromComponentInitialize(nullptr),
   3470          #endif
   3471           hostApplicationFromInitialize(nullptr)
   3472     {
   3473         d_debug("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory);
   3474 
   3475         // make sure host application is valid through out this controller lifetime
   3476         if (hostApplicationFromFactory != nullptr)
   3477             v3_cpp_obj_ref(hostApplicationFromFactory);
   3478        #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   3479         if (hostApplicationFromComponent != nullptr)
   3480             v3_cpp_obj_ref(hostApplicationFromComponent);
   3481        #endif
   3482 
   3483         // v3_funknown, everything custom
   3484         query_interface = query_interface_edit_controller;
   3485         ref = ref_edit_controller;
   3486         unref = unref_edit_controller;
   3487 
   3488         // v3_plugin_base
   3489         base.initialize = initialize;
   3490         base.terminate = terminate;
   3491 
   3492         // v3_edit_controller
   3493         ctrl.set_component_state = set_component_state;
   3494         ctrl.set_state = set_state;
   3495         ctrl.get_state = get_state;
   3496         ctrl.get_parameter_count = get_parameter_count;
   3497         ctrl.get_parameter_info = get_parameter_info;
   3498         ctrl.get_parameter_string_for_value = get_parameter_string_for_value;
   3499         ctrl.get_parameter_value_for_string = get_parameter_value_for_string;
   3500         ctrl.normalised_parameter_to_plain = normalised_parameter_to_plain;
   3501         ctrl.plain_parameter_to_normalised = plain_parameter_to_normalised;
   3502         ctrl.get_parameter_normalised = get_parameter_normalised;
   3503         ctrl.set_parameter_normalised = set_parameter_normalised;
   3504         ctrl.set_component_handler = set_component_handler;
   3505         ctrl.create_view = create_view;
   3506     }
   3507 
   3508     ~dpf_edit_controller()
   3509     {
   3510         d_debug("~dpf_edit_controller()");
   3511        #if DISTRHO_PLUGIN_HAS_UI
   3512         connectionCtrl2View = nullptr;
   3513        #endif
   3514        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3515         connectionComp2Ctrl = nullptr;
   3516         vst3 = nullptr;
   3517        #endif
   3518 
   3519        #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   3520         if (hostApplicationFromComponent != nullptr)
   3521             v3_cpp_obj_unref(hostApplicationFromComponent);
   3522        #endif
   3523         if (hostApplicationFromFactory != nullptr)
   3524             v3_cpp_obj_unref(hostApplicationFromFactory);
   3525     }
   3526 
   3527     // ----------------------------------------------------------------------------------------------------------------
   3528     // v3_funknown
   3529 
   3530     static v3_result V3_API query_interface_edit_controller(void* const self, const v3_tuid iid, void** const iface)
   3531     {
   3532         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3533 
   3534         if (v3_tuid_match(iid, v3_funknown_iid) ||
   3535             v3_tuid_match(iid, v3_plugin_base_iid) ||
   3536             v3_tuid_match(iid, v3_edit_controller_iid))
   3537         {
   3538             d_debug("query_interface_edit_controller => %p %s %p | OK", self, tuid2str(iid), iface);
   3539             ++controller->refcounter;
   3540             *iface = self;
   3541             return V3_OK;
   3542         }
   3543 
   3544         if (v3_tuid_match(iid, v3_midi_mapping_iid))
   3545         {
   3546            #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   3547             d_debug("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface);
   3548             static dpf_midi_mapping midi_mapping;
   3549             static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping;
   3550             *iface = &midi_mapping_ptr;
   3551             return V3_OK;
   3552            #else
   3553             d_debug("query_interface_edit_controller => %p %s %p | reject unused", self, tuid2str(iid), iface);
   3554             *iface = nullptr;
   3555             return V3_NO_INTERFACE;
   3556            #endif
   3557         }
   3558 
   3559         if (v3_tuid_match(iid, v3_connection_point_iid))
   3560         {
   3561            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3562             d_debug("query_interface_edit_controller => %p %s %p | OK convert %p",
   3563                     self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get());
   3564 
   3565             if (controller->connectionComp2Ctrl == nullptr)
   3566                 controller->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(controller->vst3);
   3567             else
   3568                 ++controller->connectionComp2Ctrl->refcounter;
   3569             *iface = &controller->connectionComp2Ctrl;
   3570             return V3_OK;
   3571            #else
   3572             d_debug("query_interface_edit_controller => %p %s %p | reject unwanted", self, tuid2str(iid), iface);
   3573             *iface = nullptr;
   3574             return V3_NO_INTERFACE;
   3575            #endif
   3576         }
   3577 
   3578         d_debug("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   3579         *iface = nullptr;
   3580         return V3_NO_INTERFACE;
   3581     }
   3582 
   3583     static uint32_t V3_API ref_edit_controller(void* const self)
   3584     {
   3585         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3586         const int refcount = ++controller->refcounter;
   3587         d_debug("dpf_edit_controller::ref => %p | refcount %i", self, refcount);
   3588         return refcount;
   3589     }
   3590 
   3591     static uint32_t V3_API unref_edit_controller(void* const self)
   3592     {
   3593         dpf_edit_controller** const controllerptr = static_cast<dpf_edit_controller**>(self);
   3594         dpf_edit_controller* const controller = *controllerptr;
   3595 
   3596         if (const int refcount = --controller->refcounter)
   3597         {
   3598             d_debug("dpf_edit_controller::unref => %p | refcount %i", self, refcount);
   3599             return refcount;
   3600         }
   3601 
   3602        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3603         /**
   3604          * Some hosts will have unclean instances of a few of the controller child classes at this point.
   3605          * We check for those here, going through the whole possible chain to see if it is safe to delete.
   3606          * If not, we add this controller to the `gControllerGarbage` global which will take care of it during unload.
   3607          */
   3608 
   3609         bool unclean = false;
   3610 
   3611         if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl)
   3612         {
   3613             if (const int refcount = point->refcounter)
   3614             {
   3615                 unclean = true;
   3616                 d_stderr("DPF warning: asked to delete controller while component connection point still active (refcount %d)", refcount);
   3617             }
   3618         }
   3619 
   3620         if (unclean)
   3621             return handleUncleanController(controllerptr);
   3622 
   3623         d_debug("dpf_edit_controller::unref => %p | refcount is zero, deleting everything now!", self);
   3624 
   3625         delete controller;
   3626         delete controllerptr;
   3627        #else
   3628         d_debug("dpf_edit_controller::unref => %p | refcount is zero, deletion will be done by component later", self);
   3629        #endif
   3630         return 0;
   3631     }
   3632 
   3633     // ----------------------------------------------------------------------------------------------------------------
   3634     // v3_plugin_base
   3635 
   3636     static v3_result V3_API initialize(void* const self, v3_funknown** const context)
   3637     {
   3638         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3639 
   3640         // check if already initialized
   3641        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3642         DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 == nullptr, V3_INVALID_ARG);
   3643        #else
   3644         DISTRHO_SAFE_ASSERT_RETURN(! controller->initialized, V3_INVALID_ARG);
   3645        #endif
   3646 
   3647         // query for host application
   3648         v3_host_application** hostApplication = nullptr;
   3649         if (context != nullptr)
   3650             v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication);
   3651 
   3652         d_debug("dpf_edit_controller::initialize => %p %p | host %p", self, context, hostApplication);
   3653 
   3654         // save it for later so we can unref it
   3655         controller->hostApplicationFromInitialize = hostApplication;
   3656 
   3657        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3658         // provide the factory application to the plugin if this new one is missing
   3659         if (hostApplication == nullptr)
   3660             hostApplication = controller->hostApplicationFromFactory;
   3661 
   3662         // default early values
   3663         if (d_nextBufferSize == 0)
   3664             d_nextBufferSize = 1024;
   3665         if (d_nextSampleRate <= 0.0)
   3666             d_nextSampleRate = 44100.0;
   3667 
   3668         d_nextCanRequestParameterValueChanges = true;
   3669 
   3670         // create the actual plugin
   3671         controller->vst3 = new PluginVst3(hostApplication, false);
   3672 
   3673         // set connection point if needed
   3674         if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl)
   3675         {
   3676             if (point->other != nullptr)
   3677                 controller->vst3->comp2ctrl_connect(point->other);
   3678         }
   3679        #else
   3680         // mark as initialized
   3681         controller->initialized = true;
   3682        #endif
   3683 
   3684         return V3_OK;
   3685     }
   3686 
   3687     static v3_result V3_API terminate(void* self)
   3688     {
   3689         d_debug("dpf_edit_controller::terminate => %p", self);
   3690         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3691 
   3692        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3693         // check if already terminated
   3694         DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_INVALID_ARG);
   3695 
   3696         // delete actual plugin
   3697         controller->vst3 = nullptr;
   3698        #else
   3699         // check if already terminated
   3700         DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG);
   3701 
   3702         // mark as uninitialzed
   3703         controller->initialized = false;
   3704        #endif
   3705 
   3706         // unref host application received during initialize
   3707         if (controller->hostApplicationFromInitialize != nullptr)
   3708         {
   3709             v3_cpp_obj_unref(controller->hostApplicationFromInitialize);
   3710             controller->hostApplicationFromInitialize = nullptr;
   3711         }
   3712 
   3713         return V3_OK;
   3714     }
   3715 
   3716     // ----------------------------------------------------------------------------------------------------------------
   3717     // v3_edit_controller
   3718 
   3719     static v3_result V3_API set_component_state(void* const self, v3_bstream** const stream)
   3720     {
   3721         d_debug("dpf_edit_controller::set_component_state => %p %p", self, stream);
   3722 
   3723        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3724         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3725 
   3726         PluginVst3* const vst3 = controller->vst3;
   3727         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3728 
   3729         return vst3->setState(stream);
   3730        #else
   3731         return V3_OK;
   3732 
   3733         // unused
   3734         (void)self;
   3735         (void)stream;
   3736        #endif
   3737     }
   3738 
   3739     static v3_result V3_API set_state(void* const self, v3_bstream** const stream)
   3740     {
   3741         d_debug("dpf_edit_controller::set_state => %p %p", self, stream);
   3742 
   3743        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3744         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3745         DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED);
   3746        #endif
   3747 
   3748         return V3_NOT_IMPLEMENTED;
   3749 
   3750         // maybe unused
   3751         (void)self;
   3752         (void)stream;
   3753     }
   3754 
   3755     static v3_result V3_API get_state(void* const self, v3_bstream** const stream)
   3756     {
   3757         d_debug("dpf_edit_controller::get_state => %p %p", self, stream);
   3758 
   3759        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   3760         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3761         DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED);
   3762        #endif
   3763 
   3764         return V3_NOT_IMPLEMENTED;
   3765 
   3766         // maybe unused
   3767         (void)self;
   3768         (void)stream;
   3769     }
   3770 
   3771     static int32_t V3_API get_parameter_count(void* self)
   3772     {
   3773         // d_debug("dpf_edit_controller::get_parameter_count => %p", self);
   3774         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3775 
   3776         PluginVst3* const vst3 = controller->vst3;
   3777         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3778 
   3779         return vst3->getParameterCount();
   3780     }
   3781 
   3782     static v3_result V3_API get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info)
   3783     {
   3784         // d_debug("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx);
   3785         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3786 
   3787         PluginVst3* const vst3 = controller->vst3;
   3788         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3789 
   3790         return vst3->getParameterInfo(param_idx, param_info);
   3791     }
   3792 
   3793     static v3_result V3_API get_parameter_string_for_value(void* self, v3_param_id index, double normalized, v3_str_128 output)
   3794     {
   3795         // NOTE very noisy, called many times
   3796         // d_debug("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalized, output);
   3797         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3798 
   3799         PluginVst3* const vst3 = controller->vst3;
   3800         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3801 
   3802         return vst3->getParameterStringForValue(index, normalized, output);
   3803     }
   3804 
   3805     static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output)
   3806     {
   3807         d_debug("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output);
   3808         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3809 
   3810         PluginVst3* const vst3 = controller->vst3;
   3811         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3812 
   3813         return vst3->getParameterValueForString(index, input, output);
   3814     }
   3815 
   3816     static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalized)
   3817     {
   3818         d_debug("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalized);
   3819         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3820 
   3821         PluginVst3* const vst3 = controller->vst3;
   3822         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3823 
   3824         return vst3->normalizedParameterToPlain(index, normalized);
   3825     }
   3826 
   3827     static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain)
   3828     {
   3829         d_debug("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain);
   3830         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3831 
   3832         PluginVst3* const vst3 = controller->vst3;
   3833         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3834 
   3835         return vst3->plainParameterToNormalized(index, plain);
   3836     }
   3837 
   3838     static double V3_API get_parameter_normalised(void* self, v3_param_id index)
   3839     {
   3840         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3841 
   3842         PluginVst3* const vst3 = controller->vst3;
   3843         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0);
   3844 
   3845         return vst3->getParameterNormalized(index);
   3846     }
   3847 
   3848     static v3_result V3_API set_parameter_normalised(void* const self, const v3_param_id index, const double normalized)
   3849     {
   3850         // d_debug("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalized);
   3851         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3852 
   3853         PluginVst3* const vst3 = controller->vst3;
   3854         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   3855 
   3856         return vst3->setParameterNormalized(index, normalized);
   3857     }
   3858 
   3859     static v3_result V3_API set_component_handler(void* self, v3_component_handler** handler)
   3860     {
   3861         d_debug("dpf_edit_controller::set_component_handler => %p %p", self, handler);
   3862         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3863 
   3864         controller->handler = handler;
   3865 
   3866         if (PluginVst3* const vst3 = controller->vst3)
   3867             return vst3->setComponentHandler(handler);
   3868 
   3869         return V3_NOT_INITIALIZED;
   3870     }
   3871 
   3872     static v3_plugin_view** V3_API create_view(void* self, const char* name)
   3873     {
   3874         d_debug("dpf_edit_controller::create_view => %p %s", self, name);
   3875 
   3876        #if DISTRHO_PLUGIN_HAS_UI
   3877         dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
   3878 
   3879         d_debug("create_view has contexts %p %p",
   3880                 controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize);
   3881 
   3882         // plugin must be initialized
   3883         PluginVst3* const vst3 = controller->vst3;
   3884         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr);
   3885 
   3886         d_debug("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, factory %p",
   3887                 self, name,
   3888                 controller->hostApplicationFromInitialize,
   3889                 controller->hostApplicationFromFactory);
   3890 
   3891         // we require a host application for message creation
   3892         v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr
   3893                                          ? controller->hostApplicationFromInitialize
   3894                                         #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   3895                                          : controller->hostApplicationFromComponent != nullptr
   3896                                          ? controller->hostApplicationFromComponent
   3897                                          : controller->hostApplicationFromComponentInitialize != nullptr
   3898                                          ? controller->hostApplicationFromComponentInitialize
   3899                                         #endif
   3900                                          : controller->hostApplicationFromFactory;
   3901         DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr);
   3902 
   3903         v3_plugin_view** const view = dpf_plugin_view_create(host,
   3904                                                              vst3->getInstancePointer(),
   3905                                                              vst3->getSampleRate());
   3906         DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr);
   3907 
   3908         v3_connection_point** uiconn = nullptr;
   3909         if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK)
   3910         {
   3911             d_debug("view connection query ok %p", uiconn);
   3912             controller->connectionCtrl2View = new dpf_ctrl2view_connection_point(controller->vst3);
   3913 
   3914             v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionCtrl2View;
   3915 
   3916             v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn);
   3917             v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn);
   3918         }
   3919         else
   3920         {
   3921             controller->connectionCtrl2View = nullptr;
   3922         }
   3923 
   3924         return view;
   3925        #else
   3926         return nullptr;
   3927        #endif
   3928 
   3929         // maybe unused
   3930         (void)self;
   3931         (void)name;
   3932     }
   3933 };
   3934 
   3935 // --------------------------------------------------------------------------------------------------------------------
   3936 // dpf_process_context_requirements
   3937 
   3938 struct dpf_process_context_requirements : v3_process_context_requirements_cpp {
   3939     dpf_process_context_requirements()
   3940     {
   3941         // v3_funknown, static
   3942         query_interface = query_interface_process_context_requirements;
   3943         ref = dpf_static_ref;
   3944         unref = dpf_static_unref;
   3945 
   3946         // v3_process_context_requirements
   3947         req.get_process_context_requirements = get_process_context_requirements;
   3948     }
   3949 
   3950     // ----------------------------------------------------------------------------------------------------------------
   3951     // v3_funknown
   3952 
   3953     static v3_result V3_API query_interface_process_context_requirements(void* const self, const v3_tuid iid, void** const iface)
   3954     {
   3955         if (v3_tuid_match(iid, v3_funknown_iid) ||
   3956             v3_tuid_match(iid, v3_process_context_requirements_iid))
   3957         {
   3958             d_debug("query_interface_process_context_requirements => %p %s %p | OK", self, tuid2str(iid), iface);
   3959             *iface = self;
   3960             return V3_OK;
   3961         }
   3962 
   3963         d_debug("query_interface_process_context_requirements => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   3964 
   3965         *iface = nullptr;
   3966         return V3_NO_INTERFACE;
   3967     }
   3968 
   3969     // ----------------------------------------------------------------------------------------------------------------
   3970     // v3_process_context_requirements
   3971 
   3972     static uint32_t V3_API get_process_context_requirements(void*)
   3973     {
   3974        #if DISTRHO_PLUGIN_WANT_TIMEPOS
   3975         return 0x0
   3976             | V3_PROCESS_CTX_NEED_CONTINUOUS_TIME  // V3_PROCESS_CTX_CONT_TIME_VALID
   3977             | V3_PROCESS_CTX_NEED_PROJECT_TIME     // V3_PROCESS_CTX_PROJECT_TIME_VALID
   3978             | V3_PROCESS_CTX_NEED_TEMPO            // V3_PROCESS_CTX_TEMPO_VALID
   3979             | V3_PROCESS_CTX_NEED_TIME_SIG         // V3_PROCESS_CTX_TIME_SIG_VALID
   3980             | V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING
   3981        #else
   3982         return 0x0;
   3983        #endif
   3984     }
   3985 
   3986     DISTRHO_PREVENT_HEAP_ALLOCATION
   3987 };
   3988 
   3989 // --------------------------------------------------------------------------------------------------------------------
   3990 // dpf_audio_processor
   3991 
   3992 struct dpf_audio_processor : v3_audio_processor_cpp {
   3993     std::atomic_int refcounter;
   3994     ScopedPointer<PluginVst3>& vst3;
   3995 
   3996     dpf_audio_processor(ScopedPointer<PluginVst3>& v)
   3997         : refcounter(1),
   3998           vst3(v)
   3999     {
   4000         // v3_funknown, single instance
   4001         query_interface = query_interface_audio_processor;
   4002         ref = dpf_single_instance_ref<dpf_audio_processor>;
   4003         unref = dpf_single_instance_unref<dpf_audio_processor>;
   4004 
   4005         // v3_audio_processor
   4006         proc.set_bus_arrangements = set_bus_arrangements;
   4007         proc.get_bus_arrangement = get_bus_arrangement;
   4008         proc.can_process_sample_size = can_process_sample_size;
   4009         proc.get_latency_samples = get_latency_samples;
   4010         proc.setup_processing = setup_processing;
   4011         proc.set_processing = set_processing;
   4012         proc.process = process;
   4013         proc.get_tail_samples = get_tail_samples;
   4014     }
   4015 
   4016     // ----------------------------------------------------------------------------------------------------------------
   4017     // v3_funknown
   4018 
   4019     static v3_result V3_API query_interface_audio_processor(void* const self, const v3_tuid iid, void** const iface)
   4020     {
   4021         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4022 
   4023         if (v3_tuid_match(iid, v3_funknown_iid) ||
   4024             v3_tuid_match(iid, v3_audio_processor_iid))
   4025         {
   4026             d_debug("query_interface_audio_processor => %p %s %p | OK", self, tuid2str(iid), iface);
   4027             ++processor->refcounter;
   4028             *iface = self;
   4029             return V3_OK;
   4030         }
   4031 
   4032         if (v3_tuid_match(iid, v3_process_context_requirements_iid))
   4033         {
   4034             d_debug("query_interface_audio_processor => %p %s %p | OK convert static", self, tuid2str(iid), iface);
   4035             static dpf_process_context_requirements context_req;
   4036             static dpf_process_context_requirements* context_req_ptr = &context_req;
   4037             *iface = &context_req_ptr;
   4038             return V3_OK;
   4039         }
   4040 
   4041         d_debug("query_interface_audio_processor => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   4042 
   4043         *iface = nullptr;
   4044         return V3_NO_INTERFACE;
   4045     }
   4046 
   4047     // ----------------------------------------------------------------------------------------------------------------
   4048     // v3_audio_processor
   4049 
   4050     static v3_result V3_API set_bus_arrangements(void* const self,
   4051                                                  v3_speaker_arrangement* const inputs, const int32_t num_inputs,
   4052                                                  v3_speaker_arrangement* const outputs, const int32_t num_outputs)
   4053     {
   4054         // NOTE this is called a bunch of times in JUCE hosts
   4055         d_debug("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i",
   4056                 self, inputs, num_inputs, outputs, num_outputs);
   4057         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4058 
   4059         PluginVst3* const vst3 = processor->vst3;
   4060         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4061 
   4062         return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs);
   4063     }
   4064 
   4065     static v3_result V3_API get_bus_arrangement(void* const self, const int32_t bus_direction,
   4066                                                 const int32_t idx, v3_speaker_arrangement* const arr)
   4067     {
   4068         d_debug("dpf_audio_processor::get_bus_arrangement => %p %s %i %p",
   4069                 self, v3_bus_direction_str(bus_direction), idx, arr);
   4070         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4071 
   4072         PluginVst3* const vst3 = processor->vst3;
   4073         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4074 
   4075         return processor->vst3->getBusArrangement(bus_direction, idx, arr);
   4076     }
   4077 
   4078     static v3_result V3_API can_process_sample_size(void*, const int32_t symbolic_sample_size)
   4079     {
   4080         // NOTE runs during RT
   4081         // d_debug("dpf_audio_processor::can_process_sample_size => %i", symbolic_sample_size);
   4082         return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED;
   4083     }
   4084 
   4085     static uint32_t V3_API get_latency_samples(void* const self)
   4086     {
   4087         d_debug("dpf_audio_processor::get_latency_samples => %p", self);
   4088         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4089 
   4090         PluginVst3* const vst3 = processor->vst3;
   4091         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0);
   4092 
   4093         return processor->vst3->getLatencySamples();
   4094     }
   4095 
   4096     static v3_result V3_API setup_processing(void* const self, v3_process_setup* const setup)
   4097     {
   4098         d_debug("dpf_audio_processor::setup_processing => %p %p", self, setup);
   4099         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4100 
   4101         PluginVst3* const vst3 = processor->vst3;
   4102         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4103 
   4104         d_debug("dpf_audio_processor::setup_processing => %p %p | %d %f", self, setup, setup->max_block_size, setup->sample_rate);
   4105 
   4106         d_nextBufferSize = setup->max_block_size;
   4107         d_nextSampleRate = setup->sample_rate;
   4108         return processor->vst3->setupProcessing(setup);
   4109     }
   4110 
   4111     static v3_result V3_API set_processing(void* const self, const v3_bool state)
   4112     {
   4113         d_debug("dpf_audio_processor::set_processing => %p %u", self, state);
   4114         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4115 
   4116         PluginVst3* const vst3 = processor->vst3;
   4117         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4118 
   4119         return processor->vst3->setProcessing(state);
   4120     }
   4121 
   4122     static v3_result V3_API process(void* const self, v3_process_data* const data)
   4123     {
   4124         // NOTE runs during RT
   4125         // d_debug("dpf_audio_processor::process => %p", self);
   4126         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4127 
   4128         PluginVst3* const vst3 = processor->vst3;
   4129         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4130 
   4131         return processor->vst3->process(data);
   4132     }
   4133 
   4134     static uint32_t V3_API get_tail_samples(void* const self)
   4135     {
   4136         d_debug("dpf_audio_processor::get_tail_samples => %p", self);
   4137         dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
   4138 
   4139         PluginVst3* const vst3 = processor->vst3;
   4140         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0);
   4141 
   4142         return processor->vst3->getTailSamples();
   4143     }
   4144 };
   4145 
   4146 // --------------------------------------------------------------------------------------------------------------------
   4147 // dpf_component
   4148 
   4149 struct dpf_component : v3_component_cpp {
   4150     std::atomic_int refcounter;
   4151     ScopedPointer<dpf_audio_processor> processor;
   4152    #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4153     ScopedPointer<dpf_comp2ctrl_connection_point> connectionComp2Ctrl;
   4154    #else
   4155     ScopedPointer<dpf_edit_controller> controller;
   4156    #endif
   4157     ScopedPointer<PluginVst3> vst3;
   4158     v3_host_application** const hostApplicationFromFactory;
   4159     v3_host_application** hostApplicationFromInitialize;
   4160 
   4161     dpf_component(v3_host_application** const host)
   4162         : refcounter(1),
   4163           hostApplicationFromFactory(host),
   4164           hostApplicationFromInitialize(nullptr)
   4165     {
   4166         d_debug("dpf_component() with hostApplication %p", hostApplicationFromFactory);
   4167 
   4168         // make sure host application is valid through out this component lifetime
   4169         if (hostApplicationFromFactory != nullptr)
   4170             v3_cpp_obj_ref(hostApplicationFromFactory);
   4171 
   4172         // v3_funknown, everything custom
   4173         query_interface = query_interface_component;
   4174         ref = ref_component;
   4175         unref = unref_component;
   4176 
   4177         // v3_plugin_base
   4178         base.initialize = initialize;
   4179         base.terminate = terminate;
   4180 
   4181         // v3_component
   4182         comp.get_controller_class_id = get_controller_class_id;
   4183         comp.set_io_mode = set_io_mode;
   4184         comp.get_bus_count = get_bus_count;
   4185         comp.get_bus_info = get_bus_info;
   4186         comp.get_routing_info = get_routing_info;
   4187         comp.activate_bus = activate_bus;
   4188         comp.set_active = set_active;
   4189         comp.set_state = set_state;
   4190         comp.get_state = get_state;
   4191     }
   4192 
   4193     ~dpf_component()
   4194     {
   4195         d_debug("~dpf_component()");
   4196         processor = nullptr;
   4197        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4198         connectionComp2Ctrl = nullptr;
   4199        #else
   4200         controller = nullptr;
   4201        #endif
   4202         vst3 = nullptr;
   4203 
   4204         if (hostApplicationFromFactory != nullptr)
   4205             v3_cpp_obj_unref(hostApplicationFromFactory);
   4206     }
   4207 
   4208     // ----------------------------------------------------------------------------------------------------------------
   4209     // v3_funknown
   4210 
   4211     static v3_result V3_API query_interface_component(void* const self, const v3_tuid iid, void** const iface)
   4212     {
   4213         dpf_component* const component = *static_cast<dpf_component**>(self);
   4214 
   4215         if (v3_tuid_match(iid, v3_funknown_iid) ||
   4216             v3_tuid_match(iid, v3_plugin_base_iid) ||
   4217             v3_tuid_match(iid, v3_component_iid))
   4218         {
   4219             d_debug("query_interface_component => %p %s %p | OK", self, tuid2str(iid), iface);
   4220             ++component->refcounter;
   4221             *iface = self;
   4222             return V3_OK;
   4223         }
   4224 
   4225         if (v3_tuid_match(iid, v3_midi_mapping_iid))
   4226         {
   4227            #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
   4228             d_debug("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface);
   4229             static dpf_midi_mapping midi_mapping;
   4230             static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping;
   4231             *iface = &midi_mapping_ptr;
   4232             return V3_OK;
   4233            #else
   4234             d_debug("query_interface_component => %p %s %p | reject unused", self, tuid2str(iid), iface);
   4235             *iface = nullptr;
   4236             return V3_NO_INTERFACE;
   4237            #endif
   4238         }
   4239 
   4240         if (v3_tuid_match(iid, v3_audio_processor_iid))
   4241         {
   4242             d_debug("query_interface_component => %p %s %p | OK convert %p",
   4243                     self, tuid2str(iid), iface, component->processor.get());
   4244 
   4245             if (component->processor == nullptr)
   4246                 component->processor = new dpf_audio_processor(component->vst3);
   4247             else
   4248                 ++component->processor->refcounter;
   4249             *iface = &component->processor;
   4250             return V3_OK;
   4251         }
   4252 
   4253         if (v3_tuid_match(iid, v3_connection_point_iid))
   4254         {
   4255            #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4256             d_debug("query_interface_component => %p %s %p | OK convert %p",
   4257                     self, tuid2str(iid), iface, component->connectionComp2Ctrl.get());
   4258 
   4259             if (component->connectionComp2Ctrl == nullptr)
   4260                 component->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(component->vst3);
   4261             else
   4262                 ++component->connectionComp2Ctrl->refcounter;
   4263             *iface = &component->connectionComp2Ctrl;
   4264             return V3_OK;
   4265            #else
   4266             d_debug("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface);
   4267             *iface = nullptr;
   4268             return V3_NO_INTERFACE;
   4269            #endif
   4270         }
   4271 
   4272         if (v3_tuid_match(iid, v3_edit_controller_iid))
   4273         {
   4274            #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   4275             d_debug("query_interface_component => %p %s %p | OK convert %p",
   4276                     self, tuid2str(iid), iface, component->controller.get());
   4277 
   4278             if (component->controller == nullptr)
   4279                 component->controller = new dpf_edit_controller(component->vst3,
   4280                                                                 component->hostApplicationFromFactory,
   4281                                                                 component->hostApplicationFromInitialize);
   4282             else
   4283                 ++component->controller->refcounter;
   4284             *iface = &component->controller;
   4285             return V3_OK;
   4286            #else
   4287             d_debug("query_interface_component => %p %s %p | reject unwanted", self, tuid2str(iid), iface);
   4288             *iface = nullptr;
   4289             return V3_NO_INTERFACE;
   4290            #endif
   4291         }
   4292 
   4293         d_debug("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   4294         *iface = nullptr;
   4295         return V3_NO_INTERFACE;
   4296     }
   4297 
   4298     static uint32_t V3_API ref_component(void* const self)
   4299     {
   4300         dpf_component* const component = *static_cast<dpf_component**>(self);
   4301         const int refcount = ++component->refcounter;
   4302         d_debug("dpf_component::ref => %p | refcount %i", self, refcount);
   4303         return refcount;
   4304     }
   4305 
   4306     static uint32_t V3_API unref_component(void* const self)
   4307     {
   4308         dpf_component** const componentptr = static_cast<dpf_component**>(self);
   4309         dpf_component* const component = *componentptr;
   4310 
   4311         if (const int refcount = --component->refcounter)
   4312         {
   4313             d_debug("dpf_component::unref => %p | refcount %i", self, refcount);
   4314             return refcount;
   4315         }
   4316 
   4317         /**
   4318          * Some hosts will have unclean instances of a few of the component child classes at this point.
   4319          * We check for those here, going through the whole possible chain to see if it is safe to delete.
   4320          * If not, we add this component to the `gComponentGarbage` global which will take care of it during unload.
   4321          */
   4322 
   4323         bool unclean = false;
   4324 
   4325         if (dpf_audio_processor* const proc = component->processor)
   4326         {
   4327             if (const int refcount = proc->refcounter)
   4328             {
   4329                 unclean = true;
   4330                 d_stderr("DPF warning: asked to delete component while audio processor still active (refcount %d)", refcount);
   4331             }
   4332         }
   4333 
   4334        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4335         if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl)
   4336         {
   4337             if (const int refcount = point->refcounter)
   4338             {
   4339                 unclean = true;
   4340                 d_stderr("DPF warning: asked to delete component while connection point still active (refcount %d)", refcount);
   4341             }
   4342         }
   4343        #else
   4344         if (dpf_edit_controller* const controller = component->controller)
   4345         {
   4346             if (const int refcount = controller->refcounter)
   4347             {
   4348                 unclean = true;
   4349                 d_stderr("DPF warning: asked to delete component while edit controller still active (refcount %d)", refcount);
   4350             }
   4351         }
   4352        #endif
   4353 
   4354         if (unclean)
   4355             return handleUncleanComponent(componentptr);
   4356 
   4357         d_debug("dpf_component::unref => %p | refcount is zero, deleting everything now!", self);
   4358 
   4359         delete component;
   4360         delete componentptr;
   4361         return 0;
   4362     }
   4363 
   4364     // ----------------------------------------------------------------------------------------------------------------
   4365     // v3_plugin_base
   4366 
   4367     static v3_result V3_API initialize(void* const self, v3_funknown** const context)
   4368     {
   4369         dpf_component* const component = *static_cast<dpf_component**>(self);
   4370 
   4371         // check if already initialized
   4372         DISTRHO_SAFE_ASSERT_RETURN(component->vst3 == nullptr, V3_INVALID_ARG);
   4373 
   4374         // query for host application
   4375         v3_host_application** hostApplication = nullptr;
   4376         if (context != nullptr)
   4377             v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication);
   4378 
   4379         d_debug("dpf_component::initialize => %p %p | hostApplication %p", self, context, hostApplication);
   4380 
   4381         // save it for later so we can unref it
   4382         component->hostApplicationFromInitialize = hostApplication;
   4383 
   4384        #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   4385         // save it in edit controller too, needed for some hosts
   4386         if (component->controller != nullptr)
   4387             component->controller->hostApplicationFromComponentInitialize = hostApplication;
   4388        #endif
   4389 
   4390         // provide the factory application to the plugin if this new one is missing
   4391         if (hostApplication == nullptr)
   4392             hostApplication = component->hostApplicationFromFactory;
   4393 
   4394         // default early values
   4395         if (d_nextBufferSize == 0)
   4396             d_nextBufferSize = 1024;
   4397         if (d_nextSampleRate <= 0.0)
   4398             d_nextSampleRate = 44100.0;
   4399 
   4400         d_nextCanRequestParameterValueChanges = true;
   4401 
   4402         // create the actual plugin
   4403         component->vst3 = new PluginVst3(hostApplication, true);
   4404 
   4405        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4406         // set connection point if needed
   4407         if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl)
   4408         {
   4409             if (point->other != nullptr)
   4410                 component->vst3->comp2ctrl_connect(point->other);
   4411         }
   4412        #endif
   4413 
   4414         return V3_OK;
   4415     }
   4416 
   4417     static v3_result V3_API terminate(void* const self)
   4418     {
   4419         d_debug("dpf_component::terminate => %p", self);
   4420         dpf_component* const component = *static_cast<dpf_component**>(self);
   4421 
   4422         // check if already terminated
   4423         DISTRHO_SAFE_ASSERT_RETURN(component->vst3 != nullptr, V3_INVALID_ARG);
   4424 
   4425         // delete actual plugin
   4426         component->vst3 = nullptr;
   4427 
   4428        #if !DPF_VST3_USES_SEPARATE_CONTROLLER
   4429         // remove previous host application saved during initialize
   4430         if (component->controller != nullptr)
   4431             component->controller->hostApplicationFromComponentInitialize = nullptr;
   4432        #endif
   4433 
   4434         // unref host application received during initialize
   4435         if (component->hostApplicationFromInitialize != nullptr)
   4436         {
   4437             v3_cpp_obj_unref(component->hostApplicationFromInitialize);
   4438             component->hostApplicationFromInitialize = nullptr;
   4439         }
   4440 
   4441         return V3_OK;
   4442     }
   4443 
   4444     // ----------------------------------------------------------------------------------------------------------------
   4445     // v3_component
   4446 
   4447     static v3_result V3_API get_controller_class_id(void*, v3_tuid class_id)
   4448     {
   4449         d_debug("dpf_component::get_controller_class_id => %p", class_id);
   4450 
   4451         std::memcpy(class_id, dpf_tuid_controller, sizeof(v3_tuid));
   4452         return V3_OK;
   4453     }
   4454 
   4455     static v3_result V3_API set_io_mode(void* const self, const int32_t io_mode)
   4456     {
   4457         d_debug("dpf_component::set_io_mode => %p %i", self, io_mode);
   4458 
   4459         dpf_component* const component = *static_cast<dpf_component**>(self);
   4460 
   4461         PluginVst3* const vst3 = component->vst3;
   4462         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4463 
   4464         // TODO
   4465         return V3_NOT_IMPLEMENTED;
   4466 
   4467         // unused
   4468         (void)io_mode;
   4469     }
   4470 
   4471     static int32_t V3_API get_bus_count(void* const self, const int32_t media_type, const int32_t bus_direction)
   4472     {
   4473         // NOTE runs during RT
   4474         // d_debug("dpf_component::get_bus_count => %p %s %s",
   4475         //          self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction));
   4476         dpf_component* const component = *static_cast<dpf_component**>(self);
   4477 
   4478         PluginVst3* const vst3 = component->vst3;
   4479         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4480 
   4481         const int32_t ret = vst3->getBusCount(media_type, bus_direction);
   4482         // d_debug("dpf_component::get_bus_count returns %i", ret);
   4483         return ret;
   4484     }
   4485 
   4486     static v3_result V3_API get_bus_info(void* const self, const int32_t media_type, const int32_t bus_direction,
   4487                                          const int32_t bus_idx, v3_bus_info* const info)
   4488     {
   4489         d_debug("dpf_component::get_bus_info => %p %s %s %i %p",
   4490                 self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, info);
   4491         dpf_component* const component = *static_cast<dpf_component**>(self);
   4492 
   4493         PluginVst3* const vst3 = component->vst3;
   4494         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4495 
   4496         return vst3->getBusInfo(media_type, bus_direction, bus_idx, info);
   4497     }
   4498 
   4499     static v3_result V3_API get_routing_info(void* const self, v3_routing_info* const input, v3_routing_info* const output)
   4500     {
   4501         d_debug("dpf_component::get_routing_info => %p %p %p", self, input, output);
   4502         dpf_component* const component = *static_cast<dpf_component**>(self);
   4503 
   4504         PluginVst3* const vst3 = component->vst3;
   4505         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4506 
   4507         return vst3->getRoutingInfo(input, output);
   4508     }
   4509 
   4510     static v3_result V3_API activate_bus(void* const self, const int32_t media_type, const int32_t bus_direction,
   4511                                          const int32_t bus_idx, const v3_bool state)
   4512     {
   4513         // NOTE this is called a bunch of times
   4514         // d_debug("dpf_component::activate_bus => %p %s %s %i %u",
   4515         //         self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, state);
   4516         dpf_component* const component = *static_cast<dpf_component**>(self);
   4517 
   4518         PluginVst3* const vst3 = component->vst3;
   4519         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4520 
   4521         return vst3->activateBus(media_type, bus_direction, bus_idx, state);
   4522     }
   4523 
   4524     static v3_result V3_API set_active(void* const self, const v3_bool state)
   4525     {
   4526         d_debug("dpf_component::set_active => %p %u", self, state);
   4527         dpf_component* const component = *static_cast<dpf_component**>(self);
   4528 
   4529         PluginVst3* const vst3 = component->vst3;
   4530         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4531 
   4532         return component->vst3->setActive(state);
   4533     }
   4534 
   4535     static v3_result V3_API set_state(void* const self, v3_bstream** const stream)
   4536     {
   4537         d_debug("dpf_component::set_state => %p", self);
   4538         dpf_component* const component = *static_cast<dpf_component**>(self);
   4539 
   4540         PluginVst3* const vst3 = component->vst3;
   4541         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4542 
   4543         return vst3->setState(stream);
   4544     }
   4545 
   4546     static v3_result V3_API get_state(void* const self, v3_bstream** const stream)
   4547     {
   4548         d_debug("dpf_component::get_state => %p %p", self, stream);
   4549         dpf_component* const component = *static_cast<dpf_component**>(self);
   4550 
   4551         PluginVst3* const vst3 = component->vst3;
   4552         DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
   4553 
   4554         return vst3->getState(stream);
   4555     }
   4556 };
   4557 
   4558 // --------------------------------------------------------------------------------------------------------------------
   4559 // Dummy plugin to get data from
   4560 
   4561 static ScopedPointer<PluginExporter> sPlugin;
   4562 
   4563 static const char* getPluginCategories()
   4564 {
   4565     static String categories;
   4566     static bool firstInit = true;
   4567 
   4568     if (firstInit)
   4569     {
   4570        #ifdef DISTRHO_PLUGIN_VST3_CATEGORIES
   4571         categories = DISTRHO_PLUGIN_VST3_CATEGORIES;
   4572        #elif DISTRHO_PLUGIN_IS_SYNTH
   4573         categories = "Instrument";
   4574        #else
   4575         categories = "Fx";
   4576        #endif
   4577         firstInit = false;
   4578 
   4579         // An empty category is considered invalid in Cubase
   4580         DISTRHO_SAFE_ASSERT(categories.isNotEmpty());
   4581     }
   4582 
   4583     return categories.buffer();
   4584 }
   4585 
   4586 static const char* getPluginVersion()
   4587 {
   4588     static String version;
   4589 
   4590     if (version.isEmpty())
   4591     {
   4592         const uint32_t versionNum = sPlugin->getVersion();
   4593 
   4594         char versionBuf[64];
   4595         std::snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d",
   4596                       (versionNum >> 16) & 0xff,
   4597                       (versionNum >>  8) & 0xff,
   4598                       (versionNum >>  0) & 0xff);
   4599         versionBuf[sizeof(versionBuf)-1] = '\0';
   4600         version = versionBuf;
   4601     }
   4602 
   4603     return version.buffer();
   4604 }
   4605 
   4606 // --------------------------------------------------------------------------------------------------------------------
   4607 // dpf_factory
   4608 
   4609 struct dpf_factory : v3_plugin_factory_cpp {
   4610     std::atomic_int refcounter;
   4611 
   4612     // cached values
   4613     v3_funknown** hostContext;
   4614 
   4615     dpf_factory()
   4616         : refcounter(1),
   4617           hostContext(nullptr)
   4618     {
   4619         // v3_funknown, static
   4620         query_interface = query_interface_factory;
   4621         ref = ref_factory;
   4622         unref = unref_factory;
   4623 
   4624         // v3_plugin_factory
   4625         v1.get_factory_info = get_factory_info;
   4626         v1.num_classes = num_classes;
   4627         v1.get_class_info = get_class_info;
   4628         v1.create_instance = create_instance;
   4629 
   4630         // v3_plugin_factory_2
   4631         v2.get_class_info_2 = get_class_info_2;
   4632 
   4633         // v3_plugin_factory_3
   4634         v3.get_class_info_utf16 = get_class_info_utf16;
   4635         v3.set_host_context = set_host_context;
   4636     }
   4637 
   4638     ~dpf_factory()
   4639     {
   4640         // unref old context if there is one
   4641         if (hostContext != nullptr)
   4642             v3_cpp_obj_unref(hostContext);
   4643 
   4644        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4645         if (gControllerGarbage.size() != 0)
   4646         {
   4647             d_debug("DPF notice: cleaning up previously undeleted controllers now");
   4648 
   4649             for (std::vector<dpf_edit_controller**>::iterator it = gControllerGarbage.begin();
   4650                 it != gControllerGarbage.end(); ++it)
   4651             {
   4652                 dpf_edit_controller** const controllerptr = *it;
   4653                 dpf_edit_controller* const controller = *controllerptr;
   4654                 delete controller;
   4655                 delete controllerptr;
   4656             }
   4657 
   4658             gControllerGarbage.clear();
   4659         }
   4660        #endif
   4661 
   4662         if (gComponentGarbage.size() != 0)
   4663         {
   4664             d_debug("DPF notice: cleaning up previously undeleted components now");
   4665 
   4666             for (std::vector<dpf_component**>::iterator it = gComponentGarbage.begin();
   4667                 it != gComponentGarbage.end(); ++it)
   4668             {
   4669                 dpf_component** const componentptr = *it;
   4670                 dpf_component* const component = *componentptr;
   4671                 delete component;
   4672                 delete componentptr;
   4673             }
   4674 
   4675             gComponentGarbage.clear();
   4676         }
   4677     }
   4678 
   4679     // ----------------------------------------------------------------------------------------------------------------
   4680     // v3_funknown
   4681 
   4682     static v3_result V3_API query_interface_factory(void* const self, const v3_tuid iid, void** const iface)
   4683     {
   4684         dpf_factory* const factory = *static_cast<dpf_factory**>(self);
   4685 
   4686         if (v3_tuid_match(iid, v3_funknown_iid) ||
   4687             v3_tuid_match(iid, v3_plugin_factory_iid) ||
   4688             v3_tuid_match(iid, v3_plugin_factory_2_iid) ||
   4689             v3_tuid_match(iid, v3_plugin_factory_3_iid))
   4690         {
   4691             d_debug("query_interface_factory => %p %s %p | OK", self, tuid2str(iid), iface);
   4692             ++factory->refcounter;
   4693             *iface = self;
   4694             return V3_OK;
   4695         }
   4696 
   4697         d_debug("query_interface_factory => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
   4698 
   4699         *iface = nullptr;
   4700         return V3_NO_INTERFACE;
   4701     }
   4702 
   4703     static uint32_t V3_API ref_factory(void* const self)
   4704     {
   4705         dpf_factory* const factory = *static_cast<dpf_factory**>(self);
   4706         const int refcount = ++factory->refcounter;
   4707         d_debug("ref_factory::ref => %p | refcount %i", self, refcount);
   4708         return refcount;
   4709     }
   4710 
   4711     static uint32_t V3_API unref_factory(void* const self)
   4712     {
   4713         dpf_factory** const factoryptr = static_cast<dpf_factory**>(self);
   4714         dpf_factory* const factory = *factoryptr;
   4715 
   4716         if (const int refcount = --factory->refcounter)
   4717         {
   4718             d_debug("unref_factory::unref => %p | refcount %i", self, refcount);
   4719             return refcount;
   4720         }
   4721 
   4722         d_debug("unref_factory::unref => %p | refcount is zero, deleting factory", self);
   4723 
   4724         delete factory;
   4725         delete factoryptr;
   4726         return 0;
   4727     }
   4728 
   4729     // ----------------------------------------------------------------------------------------------------------------
   4730     // v3_plugin_factory
   4731 
   4732     static v3_result V3_API get_factory_info(void*, v3_factory_info* const info)
   4733     {
   4734         d_debug("dpf_factory::get_factory_info => %p", info);
   4735         std::memset(info, 0, sizeof(*info));
   4736 
   4737         info->flags = 0x10; // unicode
   4738         d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor));
   4739         d_strncpy(info->url, sPlugin->getHomePage(), ARRAY_SIZE(info->url));
   4740         // d_strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO
   4741         return V3_OK;
   4742     }
   4743 
   4744     static int32_t V3_API num_classes(void*)
   4745     {
   4746         d_debug("dpf_factory::num_classes");
   4747        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4748         return 2; // factory can create component and edit-controller
   4749        #else
   4750         return 1; // factory can only create component, edit-controller must be casted
   4751        #endif
   4752     }
   4753 
   4754     static v3_result V3_API get_class_info(void*, const int32_t idx, v3_class_info* const info)
   4755     {
   4756         d_debug("dpf_factory::get_class_info => %i %p", idx, info);
   4757         std::memset(info, 0, sizeof(*info));
   4758         DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG);
   4759 
   4760         info->cardinality = 0x7FFFFFFF;
   4761         d_strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name));
   4762 
   4763         if (idx == 0)
   4764         {
   4765             std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
   4766             d_strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
   4767         }
   4768         else
   4769         {
   4770             std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid));
   4771             d_strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category));
   4772         }
   4773 
   4774         return V3_OK;
   4775     }
   4776 
   4777     static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** const instance)
   4778     {
   4779         d_debug("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance);
   4780         dpf_factory* const factory = *static_cast<dpf_factory**>(self);
   4781 
   4782         // query for host application
   4783         v3_host_application** hostApplication = nullptr;
   4784         if (factory->hostContext != nullptr)
   4785             v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &hostApplication);
   4786 
   4787         // create component
   4788         if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && (v3_tuid_match(iid, v3_component_iid) ||
   4789                                                                           v3_tuid_match(iid, v3_funknown_iid)))
   4790         {
   4791             dpf_component** const componentptr = new dpf_component*;
   4792             *componentptr = new dpf_component(hostApplication);
   4793             *instance = static_cast<void*>(componentptr);
   4794             return V3_OK;
   4795         }
   4796 
   4797        #if DPF_VST3_USES_SEPARATE_CONTROLLER
   4798         // create edit controller
   4799         if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && (v3_tuid_match(iid, v3_edit_controller_iid) ||
   4800                                                                                v3_tuid_match(iid, v3_funknown_iid)))
   4801         {
   4802             dpf_edit_controller** const controllerptr = new dpf_edit_controller*;
   4803             *controllerptr = new dpf_edit_controller(hostApplication);
   4804             *instance = static_cast<void*>(controllerptr);
   4805             return V3_OK;
   4806         }
   4807        #endif
   4808 
   4809         // unsupported, roll back host application
   4810         if (hostApplication != nullptr)
   4811             v3_cpp_obj_unref(hostApplication);
   4812 
   4813         return V3_NO_INTERFACE;
   4814     }
   4815 
   4816     // ----------------------------------------------------------------------------------------------------------------
   4817     // v3_plugin_factory_2
   4818 
   4819     static v3_result V3_API get_class_info_2(void*, const int32_t idx, v3_class_info_2* const info)
   4820     {
   4821         d_debug("dpf_factory::get_class_info_2 => %i %p", idx, info);
   4822         std::memset(info, 0, sizeof(*info));
   4823         DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG);
   4824 
   4825         info->cardinality = 0x7FFFFFFF;
   4826        #if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI
   4827         info->class_flags = V3_DISTRIBUTABLE;
   4828        #endif
   4829         d_strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories));
   4830         d_strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name));
   4831         d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor));
   4832         d_strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
   4833         d_strncpy(info->sdk_version, "VST 3.7.4", ARRAY_SIZE(info->sdk_version));
   4834 
   4835         if (idx == 0)
   4836         {
   4837             std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
   4838             d_strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
   4839         }
   4840         else
   4841         {
   4842             std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid));
   4843             d_strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category));
   4844         }
   4845 
   4846         return V3_OK;
   4847     }
   4848 
   4849     // ------------------------------------------------------------------------------------------------------------
   4850     // v3_plugin_factory_3
   4851 
   4852     static v3_result V3_API get_class_info_utf16(void*, const int32_t idx, v3_class_info_3* const info)
   4853     {
   4854         d_debug("dpf_factory::get_class_info_utf16 => %i %p", idx, info);
   4855         std::memset(info, 0, sizeof(*info));
   4856         DISTRHO_SAFE_ASSERT_RETURN(idx <= 2, V3_INVALID_ARG);
   4857 
   4858         info->cardinality = 0x7FFFFFFF;
   4859        #if DPF_VST3_USES_SEPARATE_CONTROLLER || !DISTRHO_PLUGIN_HAS_UI
   4860         info->class_flags = V3_DISTRIBUTABLE;
   4861        #endif
   4862         d_strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories));
   4863         DISTRHO_NAMESPACE::strncpy_utf16(info->name, sPlugin->getName(), ARRAY_SIZE(info->name));
   4864         DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor));
   4865         DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
   4866         DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "VST 3.7.4", ARRAY_SIZE(info->sdk_version));
   4867 
   4868         if (idx == 0)
   4869         {
   4870             std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
   4871             d_strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
   4872         }
   4873         else
   4874         {
   4875             std::memcpy(info->class_id, dpf_tuid_controller, sizeof(v3_tuid));
   4876             d_strncpy(info->category, "Component Controller Class", ARRAY_SIZE(info->category));
   4877         }
   4878 
   4879         return V3_OK;
   4880     }
   4881 
   4882     static v3_result V3_API set_host_context(void* const self, v3_funknown** const context)
   4883     {
   4884         d_debug("dpf_factory::set_host_context => %p %p", self, context);
   4885         dpf_factory* const factory = *static_cast<dpf_factory**>(self);
   4886 
   4887         // unref old context if there is one
   4888         if (factory->hostContext != nullptr)
   4889             v3_cpp_obj_unref(factory->hostContext);
   4890 
   4891         // store new context
   4892         factory->hostContext = context;
   4893 
   4894         // make sure the object keeps being valid for a while
   4895         if (context != nullptr)
   4896             v3_cpp_obj_ref(context);
   4897 
   4898         return V3_OK;
   4899     }
   4900 };
   4901 
   4902 END_NAMESPACE_DISTRHO
   4903 
   4904 // --------------------------------------------------------------------------------------------------------------------
   4905 // VST3 entry point
   4906 
   4907 DISTRHO_PLUGIN_EXPORT
   4908 const void* GetPluginFactory(void);
   4909 
   4910 const void* GetPluginFactory(void)
   4911 {
   4912     USE_NAMESPACE_DISTRHO;
   4913     dpf_factory** const factoryptr = new dpf_factory*;
   4914     *factoryptr = new dpf_factory;
   4915     return static_cast<void*>(factoryptr);
   4916 }
   4917 
   4918 // --------------------------------------------------------------------------------------------------------------------
   4919 // OS specific module load
   4920 
   4921 #if defined(DISTRHO_OS_MAC)
   4922 # define ENTRYFNNAME bundleEntry
   4923 # define ENTRYFNNAMEARGS void*
   4924 # define EXITFNNAME bundleExit
   4925 #elif defined(DISTRHO_OS_WINDOWS)
   4926 # define ENTRYFNNAME InitDll
   4927 # define ENTRYFNNAMEARGS void
   4928 # define EXITFNNAME ExitDll
   4929 #else
   4930 # define ENTRYFNNAME ModuleEntry
   4931 # define ENTRYFNNAMEARGS void*
   4932 # define EXITFNNAME ModuleExit
   4933 #endif
   4934 
   4935 DISTRHO_PLUGIN_EXPORT
   4936 bool ENTRYFNNAME(ENTRYFNNAMEARGS);
   4937 
   4938 bool ENTRYFNNAME(ENTRYFNNAMEARGS)
   4939 {
   4940     USE_NAMESPACE_DISTRHO;
   4941 
   4942     // find plugin bundle
   4943     static String bundlePath;
   4944     if (bundlePath.isEmpty())
   4945     {
   4946         String tmpPath(getBinaryFilename());
   4947         tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   4948         tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   4949 
   4950         if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents"))
   4951         {
   4952             tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
   4953             bundlePath = tmpPath;
   4954             d_nextBundlePath = bundlePath.buffer();
   4955         }
   4956         else
   4957         {
   4958             bundlePath = "error";
   4959         }
   4960     }
   4961 
   4962     // init dummy plugin and set uniqueId
   4963     if (sPlugin == nullptr)
   4964     {
   4965         // set valid but dummy values
   4966         d_nextBufferSize = 512;
   4967         d_nextSampleRate = 44100.0;
   4968         d_nextPluginIsDummy = true;
   4969         d_nextCanRequestParameterValueChanges = true;
   4970 
   4971         // Create dummy plugin to get data from
   4972         sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
   4973 
   4974         // unset
   4975         d_nextBufferSize = 0;
   4976         d_nextSampleRate = 0.0;
   4977         d_nextPluginIsDummy = false;
   4978         d_nextCanRequestParameterValueChanges = false;
   4979 
   4980         dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2]
   4981             = dpf_tuid_processor[2] = dpf_tuid_view[2] = sPlugin->getUniqueId();
   4982     }
   4983 
   4984     return true;
   4985 }
   4986 
   4987 DISTRHO_PLUGIN_EXPORT
   4988 bool EXITFNNAME(void);
   4989 
   4990 bool EXITFNNAME(void)
   4991 {
   4992     DISTRHO_NAMESPACE::sPlugin = nullptr;
   4993     return true;
   4994 }
   4995 
   4996 #undef ENTRYFNNAME
   4997 #undef EXITFNNAME
   4998 
   4999 // --------------------------------------------------------------------------------------------------------------------