DPF

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

DistrhoPluginLADSPA+DSSI.cpp (24555B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include "DistrhoPluginInternal.hpp"
     18 
     19 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
     20 # error Cannot use parameter value change request with LADSPA or DSSI
     21 #endif
     22 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
     23 # error Cannot use MIDI Output with LADSPA or DSSI
     24 #endif
     25 #if DISTRHO_PLUGIN_WANT_FULL_STATE && !defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WITH_LADSPA)
     26 # error Cannot use full state with LADSPA or DSSI
     27 #endif
     28 
     29 #if DISTRHO_PLUGIN_WANT_TIMEPOS && !defined(DISTRHO_NO_WARNINGS)
     30 # warning LADSPA/DSSI does not support TimePos
     31 #endif
     32 
     33 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
     34 # include "dssi/dssi.h"
     35 #else
     36 # include "ladspa/ladspa.h"
     37 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
     38 #  error Cannot use MIDI with LADSPA
     39 # endif
     40 # if DISTRHO_PLUGIN_WANT_STATE && !defined(DISTRHO_NO_WARNINGS)
     41 #  warning LADSPA cannot handle states
     42 # endif
     43 #endif
     44 
     45 START_NAMESPACE_DISTRHO
     46 
     47 // -----------------------------------------------------------------------
     48 
     49 class PluginLadspaDssi
     50 {
     51 public:
     52     PluginLadspaDssi()
     53         : fPlugin(nullptr, nullptr, nullptr, nullptr),
     54           fPortControls(nullptr),
     55           fLastControlValues(nullptr)
     56     {
     57 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
     58         for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
     59             fPortAudioIns[i] = nullptr;
     60 #else
     61         fPortAudioIns = nullptr;
     62 #endif
     63 
     64 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
     65         for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
     66             fPortAudioOuts[i] = nullptr;
     67 #else
     68         fPortAudioOuts = nullptr;
     69 #endif
     70 
     71         if (const uint32_t count = fPlugin.getParameterCount())
     72         {
     73             fPortControls      = new LADSPA_Data*[count];
     74             fLastControlValues = new LADSPA_Data[count];
     75 
     76             for (uint32_t i=0; i < count; ++i)
     77             {
     78                 fPortControls[i] = nullptr;
     79                 fLastControlValues[i] = fPlugin.getParameterValue(i);
     80             }
     81         }
     82         else
     83         {
     84             fPortControls = nullptr;
     85             fLastControlValues = nullptr;
     86         }
     87 
     88 #if DISTRHO_PLUGIN_WANT_LATENCY
     89         fPortLatency = nullptr;
     90 #endif
     91     }
     92 
     93     ~PluginLadspaDssi() noexcept
     94     {
     95         if (fPortControls != nullptr)
     96         {
     97             delete[] fPortControls;
     98             fPortControls = nullptr;
     99         }
    100 
    101         if (fLastControlValues != nullptr)
    102         {
    103             delete[] fLastControlValues;
    104             fLastControlValues = nullptr;
    105         }
    106     }
    107 
    108     // -------------------------------------------------------------------
    109 
    110     void ladspa_activate()
    111     {
    112         fPlugin.activate();
    113     }
    114 
    115     void ladspa_deactivate()
    116     {
    117         fPlugin.deactivate();
    118     }
    119 
    120     // -------------------------------------------------------------------
    121 
    122     void ladspa_connect_port(const ulong port, LADSPA_Data* const dataLocation) noexcept
    123     {
    124         ulong index = 0;
    125 
    126 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    127         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
    128         {
    129             if (port == index++)
    130             {
    131                 fPortAudioIns[i] = dataLocation;
    132                 return;
    133             }
    134         }
    135 #endif
    136 
    137 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    138         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
    139         {
    140             if (port == index++)
    141             {
    142                 fPortAudioOuts[i] = dataLocation;
    143                 return;
    144             }
    145         }
    146 #endif
    147 
    148 #if DISTRHO_PLUGIN_WANT_LATENCY
    149         if (port == index++)
    150         {
    151             fPortLatency = dataLocation;
    152             return;
    153         }
    154 #endif
    155 
    156         for (ulong i=0, count=fPlugin.getParameterCount(); i < count; ++i)
    157         {
    158             if (port == index++)
    159             {
    160                 fPortControls[i] = dataLocation;
    161                 return;
    162             }
    163         }
    164     }
    165 
    166     // -------------------------------------------------------------------
    167 
    168 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
    169     void ladspa_run(const ulong sampleCount)
    170     {
    171         dssi_run_synth(sampleCount, nullptr, 0);
    172     }
    173 
    174     void dssi_run_synth(const ulong sampleCount, snd_seq_event_t* const events, const ulong eventCount)
    175 #else
    176     void ladspa_run(const ulong sampleCount)
    177 #endif
    178     {
    179         // pre-roll
    180         if (sampleCount == 0)
    181             return updateParameterOutputsAndTriggers();
    182 
    183         // Check for updated parameters
    184         float curValue;
    185 
    186         for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
    187         {
    188             if (fPortControls[i] == nullptr)
    189                 continue;
    190 
    191             curValue = *fPortControls[i];
    192 
    193             if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue))
    194             {
    195                 fLastControlValues[i] = curValue;
    196                 fPlugin.setParameterValue(i, curValue);
    197             }
    198         }
    199 
    200 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    201         // Get MIDI Events
    202         uint32_t  midiEventCount = 0;
    203         MidiEvent midiEvents[eventCount];
    204 
    205         for (uint32_t i=0, j; i < eventCount; ++i)
    206         {
    207             const snd_seq_event_t& seqEvent(events[i]);
    208 
    209             // FIXME
    210             if (seqEvent.data.note.channel > 0xF || seqEvent.data.control.channel > 0xF)
    211                 continue;
    212 
    213             switch (seqEvent.type)
    214             {
    215             case SND_SEQ_EVENT_NOTEOFF:
    216                 j = midiEventCount++;
    217                 midiEvents[j].frame   = seqEvent.time.tick;
    218                 midiEvents[j].size    = 3;
    219                 midiEvents[j].data[0] = 0x80 + seqEvent.data.note.channel;
    220                 midiEvents[j].data[1] = seqEvent.data.note.note;
    221                 midiEvents[j].data[2] = 0;
    222                 midiEvents[j].data[3] = 0;
    223                 break;
    224             case SND_SEQ_EVENT_NOTEON:
    225                 j = midiEventCount++;
    226                 midiEvents[j].frame   = seqEvent.time.tick;
    227                 midiEvents[j].size    = 3;
    228                 midiEvents[j].data[0] = 0x90 + seqEvent.data.note.channel;
    229                 midiEvents[j].data[1] = seqEvent.data.note.note;
    230                 midiEvents[j].data[2] = seqEvent.data.note.velocity;
    231                 midiEvents[j].data[3] = 0;
    232                 break;
    233             case SND_SEQ_EVENT_KEYPRESS:
    234                 j = midiEventCount++;
    235                 midiEvents[j].frame   = seqEvent.time.tick;
    236                 midiEvents[j].size    = 3;
    237                 midiEvents[j].data[0] = 0xA0 + seqEvent.data.note.channel;
    238                 midiEvents[j].data[1] = seqEvent.data.note.note;
    239                 midiEvents[j].data[2] = seqEvent.data.note.velocity;
    240                 midiEvents[j].data[3] = 0;
    241                 break;
    242             case SND_SEQ_EVENT_CONTROLLER:
    243                 j = midiEventCount++;
    244                 midiEvents[j].frame   = seqEvent.time.tick;
    245                 midiEvents[j].size    = 3;
    246                 midiEvents[j].data[0] = 0xB0 + seqEvent.data.control.channel;
    247                 midiEvents[j].data[1] = seqEvent.data.control.param;
    248                 midiEvents[j].data[2] = seqEvent.data.control.value;
    249                 midiEvents[j].data[3] = 0;
    250                 break;
    251             case SND_SEQ_EVENT_CHANPRESS:
    252                 j = midiEventCount++;
    253                 midiEvents[j].frame   = seqEvent.time.tick;
    254                 midiEvents[j].size    = 2;
    255                 midiEvents[j].data[0] = 0xD0 + seqEvent.data.control.channel;
    256                 midiEvents[j].data[1] = seqEvent.data.control.value;
    257                 midiEvents[j].data[2] = 0;
    258                 midiEvents[j].data[3] = 0;
    259                 break;
    260             case SND_SEQ_EVENT_PITCHBEND:
    261                 j = midiEventCount++;
    262                 midiEvents[j].frame   = seqEvent.time.tick;
    263                 midiEvents[j].size    = 3;
    264                 midiEvents[j].data[0] = 0xE0 + seqEvent.data.control.channel;
    265                 uint16_t tempvalue = seqEvent.data.control.value + 8192;
    266                 midiEvents[j].data[1] = tempvalue & 0x7F;
    267                 midiEvents[j].data[2] = tempvalue >> 7;
    268                 midiEvents[j].data[3] = 0;
    269                 break;
    270             }
    271         }
    272 
    273         fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, midiEvents, midiEventCount);
    274 #else
    275         fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount);
    276 #endif
    277 
    278         updateParameterOutputsAndTriggers();
    279 
    280 #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
    281         return; // unused
    282         (void)events; (void)eventCount;
    283 #endif
    284     }
    285 
    286     // -------------------------------------------------------------------
    287 
    288 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
    289 # if DISTRHO_PLUGIN_WANT_STATE
    290     char* dssi_configure(const char* const key, const char* const value)
    291     {
    292         if (std::strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, std::strlen(DSSI_RESERVED_CONFIGURE_PREFIX)) == 0)
    293             return nullptr;
    294         if (std::strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, std::strlen(DSSI_GLOBAL_CONFIGURE_PREFIX)) == 0)
    295             return nullptr;
    296 
    297         fPlugin.setState(key, value);
    298         return nullptr;
    299     }
    300 # endif
    301 
    302 # if DISTRHO_PLUGIN_WANT_PROGRAMS
    303     const DSSI_Program_Descriptor* dssi_get_program(const ulong index)
    304     {
    305         if (index >= fPlugin.getProgramCount())
    306             return nullptr;
    307 
    308         static DSSI_Program_Descriptor desc;
    309 
    310         desc.Bank    = index / 128;
    311         desc.Program = index % 128;
    312         desc.Name    = fPlugin.getProgramName(index);
    313 
    314         return &desc;
    315     }
    316 
    317     void dssi_select_program(const ulong bank, const ulong program)
    318     {
    319         const ulong realProgram(bank * 128 + program);
    320 
    321         DISTRHO_SAFE_ASSERT_RETURN(realProgram < fPlugin.getProgramCount(),);
    322 
    323         fPlugin.loadProgram(realProgram);
    324 
    325         // Update control inputs
    326         for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
    327         {
    328             if (fPlugin.isParameterOutput(i))
    329                 continue;
    330 
    331             fLastControlValues[i] = fPlugin.getParameterValue(i);
    332 
    333             if (fPortControls[i] != nullptr)
    334                 *fPortControls[i] = fLastControlValues[i];
    335         }
    336     }
    337 # endif
    338 
    339     int dssi_get_midi_controller_for_port(const ulong port) noexcept
    340     {
    341         const uint32_t parameterOffset = fPlugin.getParameterOffset();
    342 
    343         if (port > parameterOffset)
    344             return DSSI_NONE;
    345 
    346         const uint8_t midiCC = fPlugin.getParameterMidiCC(port-parameterOffset);
    347 
    348         if (midiCC == 0 || midiCC == 32 || midiCC >= 0x78)
    349             return DSSI_NONE;
    350 
    351         return DSSI_CC(midiCC);
    352     }
    353 #endif
    354 
    355     // -------------------------------------------------------------------
    356 
    357 private:
    358     PluginExporter fPlugin;
    359 
    360     // LADSPA ports
    361 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    362     const LADSPA_Data*  fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
    363 #else
    364     const LADSPA_Data** fPortAudioIns;
    365 #endif
    366 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    367     LADSPA_Data*  fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
    368 #else
    369     LADSPA_Data** fPortAudioOuts;
    370 #endif
    371     LADSPA_Data** fPortControls;
    372 #if DISTRHO_PLUGIN_WANT_LATENCY
    373     LADSPA_Data*  fPortLatency;
    374 #endif
    375 
    376     // Temporary data
    377     LADSPA_Data* fLastControlValues;
    378 
    379     // -------------------------------------------------------------------
    380 
    381     void updateParameterOutputsAndTriggers()
    382     {
    383         float value;
    384 
    385         for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
    386         {
    387             if (fPlugin.isParameterOutput(i))
    388             {
    389                 value = fLastControlValues[i] = fPlugin.getParameterValue(i);
    390 
    391                 if (fPortControls[i] != nullptr)
    392                     *fPortControls[i] = value;
    393             }
    394             else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
    395             {
    396                 // NOTE: no trigger support in LADSPA control ports, simulate it here
    397                 value = fPlugin.getParameterRanges(i).def;
    398 
    399                 if (d_isEqual(value, fPlugin.getParameterValue(i)))
    400                     continue;
    401 
    402                 fLastControlValues[i] = value;
    403                 fPlugin.setParameterValue(i, value);
    404 
    405                 if (fPortControls[i] != nullptr)
    406                     *fPortControls[i] = value;
    407             }
    408         }
    409 
    410 #if DISTRHO_PLUGIN_WANT_LATENCY
    411         if (fPortLatency != nullptr)
    412             *fPortLatency = fPlugin.getLatency();
    413 #endif
    414     }
    415 };
    416 
    417 // -----------------------------------------------------------------------
    418 
    419 static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, ulong sampleRate)
    420 {
    421     if (d_nextBufferSize == 0)
    422         d_nextBufferSize = 2048;
    423     d_nextSampleRate = sampleRate;
    424 
    425     return new PluginLadspaDssi();
    426 }
    427 
    428 #define instancePtr ((PluginLadspaDssi*)instance)
    429 
    430 static void ladspa_connect_port(LADSPA_Handle instance, ulong port, LADSPA_Data* dataLocation)
    431 {
    432     instancePtr->ladspa_connect_port(port, dataLocation);
    433 }
    434 
    435 static void ladspa_activate(LADSPA_Handle instance)
    436 {
    437     instancePtr->ladspa_activate();
    438 }
    439 
    440 static void ladspa_run(LADSPA_Handle instance, ulong sampleCount)
    441 {
    442     instancePtr->ladspa_run(sampleCount);
    443 }
    444 
    445 static void ladspa_deactivate(LADSPA_Handle instance)
    446 {
    447     instancePtr->ladspa_deactivate();
    448 }
    449 
    450 static void ladspa_cleanup(LADSPA_Handle instance)
    451 {
    452     delete instancePtr;
    453 }
    454 
    455 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
    456 # if DISTRHO_PLUGIN_WANT_STATE
    457 static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value)
    458 {
    459     return instancePtr->dssi_configure(key, value);
    460 }
    461 # endif
    462 
    463 # if DISTRHO_PLUGIN_WANT_PROGRAMS
    464 static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, ulong index)
    465 {
    466     return instancePtr->dssi_get_program(index);
    467 }
    468 
    469 static void dssi_select_program(LADSPA_Handle instance, ulong bank, ulong program)
    470 {
    471     instancePtr->dssi_select_program(bank, program);
    472 }
    473 # endif
    474 
    475 static int dssi_get_midi_controller_for_port(LADSPA_Handle instance, ulong port)
    476 {
    477     return instancePtr->dssi_get_midi_controller_for_port(port);
    478 }
    479 
    480 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    481 static void dssi_run_synth(LADSPA_Handle instance, ulong sampleCount, snd_seq_event_t* events, ulong eventCount)
    482 {
    483     instancePtr->dssi_run_synth(sampleCount, events, eventCount);
    484 }
    485 # endif
    486 #endif
    487 
    488 #undef instancePtr
    489 
    490 // -----------------------------------------------------------------------
    491 
    492 static LADSPA_Descriptor sLadspaDescriptor = {
    493     /* UniqueID   */ 0,
    494     /* Label      */ nullptr,
    495 #if DISTRHO_PLUGIN_IS_RT_SAFE
    496     /* Properties */ LADSPA_PROPERTY_HARD_RT_CAPABLE,
    497 #else
    498     /* Properties */ 0x0,
    499 #endif
    500     /* Name       */ nullptr,
    501     /* Maker      */ nullptr,
    502     /* Copyright  */ nullptr,
    503     /* PortCount  */ 0,
    504     /* PortDescriptors    */ nullptr,
    505     /* PortNames          */ nullptr,
    506     /* PortRangeHints     */ nullptr,
    507     /* ImplementationData */ nullptr,
    508     ladspa_instantiate,
    509     ladspa_connect_port,
    510     ladspa_activate,
    511     ladspa_run,
    512     /* run_adding */          nullptr,
    513     /* set_run_adding_gain */ nullptr,
    514     ladspa_deactivate,
    515     ladspa_cleanup
    516 };
    517 
    518 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
    519 static DSSI_Descriptor sDssiDescriptor = {
    520     1,
    521     &sLadspaDescriptor,
    522 # if DISTRHO_PLUGIN_WANT_STATE
    523     dssi_configure,
    524 # else
    525     /* configure                    */ nullptr,
    526 # endif
    527 # if DISTRHO_PLUGIN_WANT_PROGRAMS
    528     dssi_get_program,
    529     dssi_select_program,
    530 # else
    531     /* get_program                  */ nullptr,
    532     /* select_program               */ nullptr,
    533 # endif
    534     dssi_get_midi_controller_for_port,
    535 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
    536     dssi_run_synth,
    537 # else
    538     /* run_synth                    */ nullptr,
    539 # endif
    540     /* run_synth_adding             */ nullptr,
    541     /* run_multiple_synths          */ nullptr,
    542     /* run_multiple_synths_adding   */ nullptr,
    543     nullptr, nullptr
    544 };
    545 #endif
    546 
    547 // -----------------------------------------------------------------------
    548 
    549 static const struct DescriptorInitializer
    550 {
    551     DescriptorInitializer()
    552     {
    553         // Create dummy plugin to get data from
    554         d_nextBufferSize = 512;
    555         d_nextSampleRate = 44100.0;
    556         d_nextPluginIsDummy = true;
    557         const PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
    558         d_nextBufferSize = 0;
    559         d_nextSampleRate = 0.0;
    560         d_nextPluginIsDummy = false;
    561 
    562         // Get port count, init
    563         ulong port = 0;
    564         ulong portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.getParameterCount();
    565 #if DISTRHO_PLUGIN_WANT_LATENCY
    566         portCount += 1;
    567 #endif
    568         const char** const     portNames       = new const char*[portCount];
    569         LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor[portCount];
    570         LADSPA_PortRangeHint*  portRangeHints  = new LADSPA_PortRangeHint [portCount];
    571 
    572         // Set ports
    573 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
    574         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port)
    575         {
    576             const AudioPort& aport(plugin.getAudioPort(true, i));
    577 
    578             portNames[port]       = strdup(aport.name);
    579             portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT;
    580 
    581             portRangeHints[port].HintDescriptor = 0x0;
    582             portRangeHints[port].LowerBound = 0.0f;
    583             portRangeHints[port].UpperBound = 1.0f;
    584         }
    585 #endif
    586 
    587 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
    588         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port)
    589         {
    590             const AudioPort& aport(plugin.getAudioPort(false, i));
    591 
    592             portNames[port]       = strdup(aport.name);
    593             portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT;
    594 
    595             portRangeHints[port].HintDescriptor = 0x0;
    596             portRangeHints[port].LowerBound = 0.0f;
    597             portRangeHints[port].UpperBound = 1.0f;
    598         }
    599 #endif
    600 
    601 #if DISTRHO_PLUGIN_WANT_LATENCY
    602         // Set latency port
    603         portNames[port]       = strdup("_latency");
    604         portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT;
    605         portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE|LADSPA_HINT_INTEGER;
    606         portRangeHints[port].LowerBound     = 0.0f;
    607         portRangeHints[port].UpperBound     = 1.0f;
    608         ++port;
    609 #endif
    610 
    611         for (ulong i=0, count=plugin.getParameterCount(); i < count; ++i, ++port)
    612         {
    613             portNames[port]       = strdup((const char*)plugin.getParameterName(i));
    614             portDescriptors[port] = LADSPA_PORT_CONTROL;
    615 
    616             if (plugin.isParameterOutput(i))
    617                 portDescriptors[port] |= LADSPA_PORT_OUTPUT;
    618             else
    619                 portDescriptors[port] |= LADSPA_PORT_INPUT;
    620 
    621             const uint32_t hints = plugin.getParameterHints(i);
    622 
    623             {
    624                 const ParameterRanges& ranges(plugin.getParameterRanges(i));
    625                 const float defValue = ranges.def;
    626 
    627                 // LADSPA doesn't allow bounded hints on toggles
    628                 portRangeHints[port].HintDescriptor = hints & kParameterIsBoolean
    629                                                     ? 0
    630                                                     : LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
    631 
    632                 portRangeHints[port].LowerBound = ranges.min;
    633                 portRangeHints[port].UpperBound = ranges.max;
    634 
    635                 /**/ if (d_isZero(defValue))
    636                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0;
    637                 else if (d_isEqual(defValue, 1.0f))
    638                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1;
    639                 else if (d_isEqual(defValue, 100.0f))
    640                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100;
    641                 else if (d_isEqual(defValue, 440.0f))
    642                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440;
    643                 else if (d_isEqual(ranges.min, defValue))
    644                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
    645                 else if (d_isEqual(ranges.max, defValue))
    646                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
    647                 else
    648                 {
    649                     const float middleValue =  ranges.min/2.0f + ranges.max/2.0f;
    650                     const float middleLow   = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f;
    651                     const float middleHigh  = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f;
    652 
    653                     /**/ if (defValue < middleLow)
    654                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
    655                     else if (defValue > middleHigh)
    656                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
    657                     else
    658                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
    659                 }
    660             }
    661 
    662             {
    663                 if (hints & kParameterIsBoolean)
    664                 {
    665                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED;
    666                 }
    667                 else
    668                 {
    669                     if (hints & kParameterIsInteger)
    670                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER;
    671                     if (hints & kParameterIsLogarithmic)
    672                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
    673                 }
    674             }
    675         }
    676 
    677         // Set data
    678         sLadspaDescriptor.UniqueID  = plugin.getUniqueId();
    679         sLadspaDescriptor.Label     = strdup(plugin.getLabel());
    680         sLadspaDescriptor.Name      = strdup(plugin.getName());
    681         sLadspaDescriptor.Maker     = strdup(plugin.getMaker());
    682         sLadspaDescriptor.Copyright = strdup(plugin.getLicense());
    683         sLadspaDescriptor.PortCount = portCount;
    684         sLadspaDescriptor.PortNames = portNames;
    685         sLadspaDescriptor.PortDescriptors = portDescriptors;
    686         sLadspaDescriptor.PortRangeHints  = portRangeHints;
    687     }
    688 
    689     ~DescriptorInitializer()
    690     {
    691         if (sLadspaDescriptor.Label != nullptr)
    692         {
    693             std::free((void*)sLadspaDescriptor.Label);
    694             sLadspaDescriptor.Label = nullptr;
    695         }
    696 
    697         if (sLadspaDescriptor.Name != nullptr)
    698         {
    699             std::free((void*)sLadspaDescriptor.Name);
    700             sLadspaDescriptor.Name = nullptr;
    701         }
    702 
    703         if (sLadspaDescriptor.Maker != nullptr)
    704         {
    705             std::free((void*)sLadspaDescriptor.Maker);
    706             sLadspaDescriptor.Maker = nullptr;
    707         }
    708 
    709         if (sLadspaDescriptor.Copyright != nullptr)
    710         {
    711             std::free((void*)sLadspaDescriptor.Copyright);
    712             sLadspaDescriptor.Copyright = nullptr;
    713         }
    714 
    715         if (sLadspaDescriptor.PortDescriptors != nullptr)
    716         {
    717             delete[] sLadspaDescriptor.PortDescriptors;
    718             sLadspaDescriptor.PortDescriptors = nullptr;
    719         }
    720 
    721         if (sLadspaDescriptor.PortRangeHints != nullptr)
    722         {
    723             delete[] sLadspaDescriptor.PortRangeHints;
    724             sLadspaDescriptor.PortRangeHints = nullptr;
    725         }
    726 
    727         if (sLadspaDescriptor.PortNames != nullptr)
    728         {
    729             for (ulong i=0; i < sLadspaDescriptor.PortCount; ++i)
    730             {
    731                 if (sLadspaDescriptor.PortNames[i] != nullptr)
    732                     std::free((void*)sLadspaDescriptor.PortNames[i]);
    733             }
    734 
    735             delete[] sLadspaDescriptor.PortNames;
    736             sLadspaDescriptor.PortNames = nullptr;
    737         }
    738     }
    739 } sDescInit;
    740 
    741 // -----------------------------------------------------------------------
    742 
    743 END_NAMESPACE_DISTRHO
    744 
    745 DISTRHO_PLUGIN_EXPORT
    746 const LADSPA_Descriptor* ladspa_descriptor(ulong index)
    747 {
    748     USE_NAMESPACE_DISTRHO
    749     return (index == 0) ? &sLadspaDescriptor : nullptr;
    750 }
    751 
    752 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
    753 DISTRHO_PLUGIN_EXPORT
    754 const DSSI_Descriptor* dssi_descriptor(ulong index)
    755 {
    756     USE_NAMESPACE_DISTRHO
    757     return (index == 0) ? &sDssiDescriptor : nullptr;
    758 }
    759 #endif
    760 
    761 // -----------------------------------------------------------------------