1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2018 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 
23 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
24 # include "dssi/dssi.h"
25 # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
26 #  error DSSI does not support MIDI output
27 # endif
28 #else
29 # include "ladspa/ladspa.h"
30 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
31 #  error Cannot use MIDI with LADSPA
32 # endif
33 # if DISTRHO_PLUGIN_WANT_STATE && !defined(DISTRHO_NO_WARNINGS)
34 #  warning LADSPA cannot handle states
35 # endif
36 #endif
37 
38 #if DISTRHO_PLUGIN_WANT_TIMEPOS && !defined(DISTRHO_NO_WARNINGS)
39 # warning LADSPA/DSSI does not support TimePos
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 class DescriptorInitializer
547 {
548 public:
DescriptorInitializer()549     DescriptorInitializer()
550     {
551         // Create dummy plugin to get data from
552         d_lastBufferSize = 512;
553         d_lastSampleRate = 44100.0;
554         PluginExporter plugin(nullptr, nullptr);
555         d_lastBufferSize = 0;
556         d_lastSampleRate = 0.0;
557 
558         // Get port count, init
559         ulong port = 0;
560         ulong portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.getParameterCount();
561 #if DISTRHO_PLUGIN_WANT_LATENCY
562         portCount += 1;
563 #endif
564         const char** const     portNames       = new const char*[portCount];
565         LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor[portCount];
566         LADSPA_PortRangeHint*  portRangeHints  = new LADSPA_PortRangeHint [portCount];
567 
568         // Set ports
569 #if DISTRHO_PLUGIN_NUM_INPUTS > 0
570         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++port)
571         {
572             const AudioPort& aport(plugin.getAudioPort(true, i));
573 
574             portNames[port]       = strdup(aport.name);
575             portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT;
576 
577             portRangeHints[port].HintDescriptor = 0x0;
578             portRangeHints[port].LowerBound = 0.0f;
579             portRangeHints[port].UpperBound = 1.0f;
580         }
581 #endif
582 
583 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
584         for (ulong i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++port)
585         {
586             const AudioPort& aport(plugin.getAudioPort(false, i));
587 
588             portNames[port]       = strdup(aport.name);
589             portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT;
590 
591             portRangeHints[port].HintDescriptor = 0x0;
592             portRangeHints[port].LowerBound = 0.0f;
593             portRangeHints[port].UpperBound = 1.0f;
594         }
595 #endif
596 
597 #if DISTRHO_PLUGIN_WANT_LATENCY
598         // Set latency port
599         portNames[port]       = strdup("_latency");
600         portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT;
601         portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE|LADSPA_HINT_INTEGER;
602         portRangeHints[port].LowerBound     = 0.0f;
603         portRangeHints[port].UpperBound     = 1.0f;
604         ++port;
605 #endif
606 
607         for (ulong i=0, count=plugin.getParameterCount(); i < count; ++i, ++port)
608         {
609             portNames[port]       = strdup((const char*)plugin.getParameterName(i));
610             portDescriptors[port] = LADSPA_PORT_CONTROL;
611 
612             if (plugin.isParameterOutput(i))
613                 portDescriptors[port] |= LADSPA_PORT_OUTPUT;
614             else
615                 portDescriptors[port] |= LADSPA_PORT_INPUT;
616 
617             {
618                 const ParameterRanges& ranges(plugin.getParameterRanges(i));
619                 const float defValue(ranges.def);
620 
621                 portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
622                 portRangeHints[port].LowerBound     = ranges.min;
623                 portRangeHints[port].UpperBound     = ranges.max;
624 
625                 if (defValue == 0.0f)
626                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0;
627                 else if (defValue == 1.0f)
628                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1;
629                 else if (defValue == 100.0f)
630                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100;
631                 else if (defValue == 440.0f)
632                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440;
633                 else if (ranges.min == defValue)
634                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM;
635                 else if (ranges.max == defValue)
636                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM;
637                 else
638                 {
639                     const float middleValue =  ranges.min/2.0f + ranges.max/2.0f;
640                     const float middleLow   = (ranges.min/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f;
641                     const float middleHigh  = (ranges.max/2.0f + middleValue/2.0f)/2.0f + middleValue/2.0f;
642 
643                     if (defValue < middleLow)
644                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW;
645                     else if (defValue > middleHigh)
646                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH;
647                     else
648                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE;
649                 }
650             }
651 
652             {
653                 const uint32_t hints(plugin.getParameterHints(i));
654 
655                 if (hints & kParameterIsBoolean)
656                 {
657                     portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED;
658                 }
659                 else
660                 {
661                     if (hints & kParameterIsInteger)
662                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER;
663                     if (hints & kParameterIsLogarithmic)
664                         portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC;
665                 }
666             }
667         }
668 
669         // Set data
670         sLadspaDescriptor.UniqueID  = plugin.getUniqueId();
671         sLadspaDescriptor.Label     = strdup(plugin.getLabel());
672         sLadspaDescriptor.Name      = strdup(plugin.getName());
673         sLadspaDescriptor.Maker     = strdup(plugin.getMaker());
674         sLadspaDescriptor.Copyright = strdup(plugin.getLicense());
675         sLadspaDescriptor.PortCount = portCount;
676         sLadspaDescriptor.PortNames = portNames;
677         sLadspaDescriptor.PortDescriptors = portDescriptors;
678         sLadspaDescriptor.PortRangeHints  = portRangeHints;
679     }
680 
~DescriptorInitializer()681     ~DescriptorInitializer()
682     {
683         if (sLadspaDescriptor.Label != nullptr)
684         {
685             std::free((void*)sLadspaDescriptor.Label);
686             sLadspaDescriptor.Label = nullptr;
687         }
688 
689         if (sLadspaDescriptor.Name != nullptr)
690         {
691             std::free((void*)sLadspaDescriptor.Name);
692             sLadspaDescriptor.Name = nullptr;
693         }
694 
695         if (sLadspaDescriptor.Maker != nullptr)
696         {
697             std::free((void*)sLadspaDescriptor.Maker);
698             sLadspaDescriptor.Maker = nullptr;
699         }
700 
701         if (sLadspaDescriptor.Copyright != nullptr)
702         {
703             std::free((void*)sLadspaDescriptor.Copyright);
704             sLadspaDescriptor.Copyright = nullptr;
705         }
706 
707         if (sLadspaDescriptor.PortDescriptors != nullptr)
708         {
709             delete[] sLadspaDescriptor.PortDescriptors;
710             sLadspaDescriptor.PortDescriptors = nullptr;
711         }
712 
713         if (sLadspaDescriptor.PortRangeHints != nullptr)
714         {
715             delete[] sLadspaDescriptor.PortRangeHints;
716             sLadspaDescriptor.PortRangeHints = nullptr;
717         }
718 
719         if (sLadspaDescriptor.PortNames != nullptr)
720         {
721             for (ulong i=0; i < sLadspaDescriptor.PortCount; ++i)
722             {
723                 if (sLadspaDescriptor.PortNames[i] != nullptr)
724                     std::free((void*)sLadspaDescriptor.PortNames[i]);
725             }
726 
727             delete[] sLadspaDescriptor.PortNames;
728             sLadspaDescriptor.PortNames = nullptr;
729         }
730     }
731 };
732 
733 static DescriptorInitializer sDescInit;
734 
735 // -----------------------------------------------------------------------
736 
737 END_NAMESPACE_DISTRHO
738 
739 DISTRHO_PLUGIN_EXPORT
ladspa_descriptor(ulong index)740 const LADSPA_Descriptor* ladspa_descriptor(ulong index)
741 {
742     USE_NAMESPACE_DISTRHO
743     return (index == 0) ? &sLadspaDescriptor : nullptr;
744 }
745 
746 #ifdef DISTRHO_PLUGIN_TARGET_DSSI
747 DISTRHO_PLUGIN_EXPORT
dssi_descriptor(ulong index)748 const DSSI_Descriptor* dssi_descriptor(ulong index)
749 {
750     USE_NAMESPACE_DISTRHO
751     return (index == 0) ? &sDssiDescriptor : nullptr;
752 }
753 #endif
754 
755 // -----------------------------------------------------------------------
756