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