1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2021 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_HAS_UI
20 # include "DistrhoUIInternal.hpp"
21 #endif
22 
23 #include "CarlaNative.hpp"
24 
25 // TODO
26 #undef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
27 #define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0
28 
29 // -----------------------------------------------------------------------
30 
31 START_NAMESPACE_DISTRHO
32 
33 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
34 static const writeMidiFunc writeMidiCallback = nullptr;
35 #endif
36 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
37 static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
38 #endif
39 
40 #if DISTRHO_PLUGIN_HAS_UI
41 // -----------------------------------------------------------------------
42 // Carla UI
43 
44 #if ! DISTRHO_PLUGIN_WANT_STATE
45 static const setStateFunc setStateCallback = nullptr;
46 #endif
47 #if ! DISTRHO_PLUGIN_IS_SYNTH
48 static const sendNoteFunc sendNoteCallback = nullptr;
49 #endif
50 
51 class UICarla
52 {
53 public:
UICarla(const NativeHostDescriptor * const host,PluginExporter * const plugin)54     UICarla(const NativeHostDescriptor* const host, PluginExporter* const plugin)
55         : fHost(host),
56           fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer())
57     {
58         fUI.setWindowTitle(host->uiName);
59 
60         if (host->uiParentId != 0)
61             fUI.setWindowTransientWinId(host->uiParentId);
62     }
63 
~UICarla()64     ~UICarla()
65     {
66         fUI.quit();
67     }
68 
69     // ---------------------------------------------
70 
carla_show(const bool yesNo)71     void carla_show(const bool yesNo)
72     {
73         fUI.setWindowVisible(yesNo);
74     }
75 
carla_idle()76     bool carla_idle()
77     {
78         return fUI.idle();
79     }
80 
carla_setParameterValue(const uint32_t index,const float value)81     void carla_setParameterValue(const uint32_t index, const float value)
82     {
83         fUI.parameterChanged(index, value);
84     }
85 
86 #if DISTRHO_PLUGIN_WANT_PROGRAMS
carla_setMidiProgram(const uint32_t realProgram)87     void carla_setMidiProgram(const uint32_t realProgram)
88     {
89         fUI.programLoaded(realProgram);
90     }
91 #endif
92 
93 #if DISTRHO_PLUGIN_WANT_STATE
carla_setCustomData(const char * const key,const char * const value)94     void carla_setCustomData(const char* const key, const char* const value)
95     {
96         fUI.stateChanged(key, value);
97     }
98 #endif
99 
carla_setUiTitle(const char * const uiTitle)100     void carla_setUiTitle(const char* const uiTitle)
101     {
102         fUI.setWindowTitle(uiTitle);
103     }
104 
105     // ---------------------------------------------
106 
107 protected:
handleEditParameter(const uint32_t,const bool)108     void handleEditParameter(const uint32_t, const bool)
109     {
110         // TODO
111     }
112 
handleSetParameterValue(const uint32_t rindex,const float value)113     void handleSetParameterValue(const uint32_t rindex, const float value)
114     {
115         fHost->ui_parameter_changed(fHost->handle, rindex, value);
116     }
117 
118 #if DISTRHO_PLUGIN_WANT_STATE
handleSetState(const char * const key,const char * const value)119     void handleSetState(const char* const key, const char* const value)
120     {
121         fHost->ui_custom_data_changed(fHost->handle, key, value);
122     }
123 #endif
124 
125 #if DISTRHO_PLUGIN_IS_SYNTH
handleSendNote(const uint8_t,const uint8_t,const uint8_t)126     void handleSendNote(const uint8_t, const uint8_t, const uint8_t)
127     {
128         // TODO
129     }
130 #endif
131 
handleSetSize(const uint width,const uint height)132     void handleSetSize(const uint width, const uint height)
133     {
134         fUI.setWindowSize(width, height);
135     }
136 
137     // ---------------------------------------------
138 
139 private:
140     // Plugin stuff
141     const NativeHostDescriptor* const fHost;
142 
143     // UI
144     UIExporter fUI;
145 
146     // ---------------------------------------------
147     // Callbacks
148 
149     #define handlePtr ((UICarla*)ptr)
150 
editParameterCallback(void * ptr,uint32_t index,bool started)151     static void editParameterCallback(void* ptr, uint32_t index, bool started)
152     {
153         handlePtr->handleEditParameter(index, started);
154     }
155 
setParameterCallback(void * ptr,uint32_t rindex,float value)156     static void setParameterCallback(void* ptr, uint32_t rindex, float value)
157     {
158         handlePtr->handleSetParameterValue(rindex, value);
159     }
160 
161 #if DISTRHO_PLUGIN_WANT_STATE
setStateCallback(void * ptr,const char * key,const char * value)162     static void setStateCallback(void* ptr, const char* key, const char* value)
163     {
164         handlePtr->handleSetState(key, value);
165     }
166 #endif
167 
168 #if DISTRHO_PLUGIN_IS_SYNTH
sendNoteCallback(void * ptr,uint8_t channel,uint8_t note,uint8_t velocity)169     static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
170     {
171         handlePtr->handleSendNote(channel, note, velocity);
172     }
173 #endif
174 
setSizeCallback(void * ptr,uint width,uint height)175     static void setSizeCallback(void* ptr, uint width, uint height)
176     {
177         handlePtr->handleSetSize(width, height);
178     }
179 
180     #undef handlePtr
181 
182     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UICarla)
183 };
184 #endif // DISTRHO_PLUGIN_HAS_UI
185 
186 // -----------------------------------------------------------------------
187 // Carla Plugin
188 
189 class PluginCarla : public NativePluginClass
190 {
191 public:
PluginCarla(const NativeHostDescriptor * const host)192     PluginCarla(const NativeHostDescriptor* const host)
193         : NativePluginClass(host),
194           fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
195           fScalePointsCache(nullptr)
196     {
197 #if DISTRHO_PLUGIN_HAS_UI
198         fUiPtr = nullptr;
199 #endif
200     }
201 
~PluginCarla()202     ~PluginCarla() override
203     {
204 #if DISTRHO_PLUGIN_HAS_UI
205         if (fUiPtr != nullptr)
206         {
207             delete fUiPtr;
208             fUiPtr = nullptr;
209         }
210 #endif
211 
212         if (fScalePointsCache != nullptr)
213         {
214             delete[] fScalePointsCache;
215             fScalePointsCache = nullptr;
216         }
217     }
218 
219 protected:
220     // -------------------------------------------------------------------
221     // Plugin parameter calls
222 
getParameterCount() const223     uint32_t getParameterCount() const override
224     {
225         return fPlugin.getParameterCount();
226     }
227 
getParameterInfo(const uint32_t index) const228     const NativeParameter* getParameterInfo(const uint32_t index) const override
229     {
230         CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), nullptr);
231 
232         static NativeParameter param;
233 
234         param.scalePointCount = 0;
235         param.scalePoints = nullptr;
236 
237         {
238             int      nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED;
239             const uint32_t paramHints = fPlugin.getParameterHints(index);
240 
241             if (paramHints & kParameterIsAutomable)
242                 nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE;
243             if (paramHints & kParameterIsBoolean)
244                 nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN;
245             if (paramHints & kParameterIsInteger)
246                 nativeParamHints |= ::NATIVE_PARAMETER_IS_INTEGER;
247             if (paramHints & kParameterIsLogarithmic)
248                 nativeParamHints |= ::NATIVE_PARAMETER_IS_LOGARITHMIC;
249             if (paramHints & kParameterIsOutput)
250                 nativeParamHints |= ::NATIVE_PARAMETER_IS_OUTPUT;
251 
252             param.hints = static_cast<NativeParameterHints>(nativeParamHints);
253         }
254 
255         param.name = fPlugin.getParameterName(index);
256         param.unit = fPlugin.getParameterUnit(index);
257 
258         {
259             const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
260 
261             param.ranges.def = ranges.def;
262             param.ranges.min = ranges.min;
263             param.ranges.max = ranges.max;
264         }
265 
266         {
267             const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
268 
269             if (const uint32_t scalePointCount = enumValues.count)
270             {
271                 NativeParameterScalePoint* const scalePoints = new NativeParameterScalePoint[scalePointCount];
272 
273                 for (uint32_t i=0; i<scalePointCount; ++i)
274                 {
275                     scalePoints[i].label = enumValues.values[i].label.buffer();
276                     scalePoints[i].value = enumValues.values[i].value;
277                 }
278 
279                 param.scalePoints     = scalePoints;
280                 param.scalePointCount = scalePointCount;
281 
282                 if (enumValues.restrictedMode)
283                     param.hints = static_cast<NativeParameterHints>(param.hints|::NATIVE_PARAMETER_USES_SCALEPOINTS);
284             }
285             else if (fScalePointsCache != nullptr)
286             {
287                 delete[] fScalePointsCache;
288                 fScalePointsCache = nullptr;
289             }
290         }
291 
292         return &param;
293     }
294 
getParameterValue(const uint32_t index) const295     float getParameterValue(const uint32_t index) const override
296     {
297         CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), 0.0f);
298 
299         return fPlugin.getParameterValue(index);
300     }
301 
302     // -------------------------------------------------------------------
303     // Plugin midi-program calls
304 
305 #if DISTRHO_PLUGIN_WANT_PROGRAMS
getMidiProgramCount() const306     uint32_t getMidiProgramCount() const override
307     {
308         return fPlugin.getProgramCount();
309     }
310 
getMidiProgramInfo(const uint32_t index) const311     const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const override
312     {
313         CARLA_SAFE_ASSERT_RETURN(index < getMidiProgramCount(), nullptr);
314 
315         static NativeMidiProgram midiProgram;
316 
317         midiProgram.bank    = index / 128;
318         midiProgram.program = index % 128;
319         midiProgram.name    = fPlugin.getProgramName(index);
320 
321         return &midiProgram;
322     }
323 #endif
324 
325     // -------------------------------------------------------------------
326     // Plugin state calls
327 
setParameterValue(const uint32_t index,const float value)328     void setParameterValue(const uint32_t index, const float value) override
329     {
330         CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),);
331 
332         fPlugin.setParameterValue(index, value);
333     }
334 
335 #if DISTRHO_PLUGIN_WANT_PROGRAMS
setMidiProgram(const uint8_t,const uint32_t bank,const uint32_t program)336     void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override
337     {
338         const uint32_t realProgram(bank * 128 + program);
339 
340         CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),);
341 
342         fPlugin.loadProgram(realProgram);
343     }
344 #endif
345 
346 #if DISTRHO_PLUGIN_WANT_STATE
setCustomData(const char * const key,const char * const value)347     void setCustomData(const char* const key, const char* const value) override
348     {
349         CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
350         CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
351 
352         fPlugin.setState(key, value);
353     }
354 #endif
355 
356     // -------------------------------------------------------------------
357     // Plugin process calls
358 
activate()359     void activate() override
360     {
361         fPlugin.activate();
362     }
363 
deactivate()364     void deactivate() override
365     {
366         fPlugin.deactivate();
367     }
368 
369 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
process(float ** const inBuffer,float ** const outBuffer,const uint32_t frames,const NativeMidiEvent * const midiEvents,const uint32_t midiEventCount)370     void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
371     {
372         MidiEvent realMidiEvents[midiEventCount];
373 
374         for (uint32_t i=0; i < midiEventCount; ++i)
375         {
376             const NativeMidiEvent& midiEvent(midiEvents[i]);
377             MidiEvent& realMidiEvent(realMidiEvents[i]);
378 
379             realMidiEvent.frame = midiEvent.time;
380             realMidiEvent.size  = midiEvent.size;
381 
382             uint8_t j=0;
383             for (; j<midiEvent.size; ++j)
384                 realMidiEvent.data[j] = midiEvent.data[j];
385             for (; j<midiEvent.size; ++j)
386                 realMidiEvent.data[j] = midiEvent.data[j];
387 
388             realMidiEvent.dataExt = nullptr;
389         }
390 
391         fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount);
392     }
393 #else
process(float ** const inBuffer,float ** const outBuffer,const uint32_t frames,const NativeMidiEvent * const,const uint32_t)394     void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override
395     {
396         fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames);
397     }
398 #endif
399 
400     // -------------------------------------------------------------------
401     // Plugin UI calls
402 
403 #if DISTRHO_PLUGIN_HAS_UI
uiShow(const bool show)404     void uiShow(const bool show) override
405     {
406         if (show)
407         {
408             createUiIfNeeded();
409             CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
410 
411             fUiPtr->carla_show(show);
412         }
413         else if (fUiPtr != nullptr)
414         {
415             delete fUiPtr;
416             fUiPtr = nullptr;
417         }
418     }
419 
uiIdle()420     void uiIdle() override
421     {
422         CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
423 
424         if (! fUiPtr->carla_idle())
425         {
426             uiClosed();
427 
428             delete fUiPtr;
429             fUiPtr = nullptr;
430         }
431     }
432 
uiSetParameterValue(const uint32_t index,const float value)433     void uiSetParameterValue(const uint32_t index, const float value) override
434     {
435         CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
436         CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),);
437 
438         fUiPtr->carla_setParameterValue(index, value);
439     }
440 
441 # if DISTRHO_PLUGIN_WANT_PROGRAMS
uiSetMidiProgram(const uint8_t,const uint32_t bank,const uint32_t program)442     void uiSetMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override
443     {
444         CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
445 
446         const uint32_t realProgram(bank * 128 + program);
447 
448         CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),);
449 
450         fUiPtr->carla_setMidiProgram(realProgram);
451     }
452 # endif
453 
454 # if DISTRHO_PLUGIN_WANT_STATE
uiSetCustomData(const char * const key,const char * const value)455     void uiSetCustomData(const char* const key, const char* const value) override
456     {
457         CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
458         CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
459         CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
460 
461         fUiPtr->carla_setCustomData(key, value);
462     }
463 # endif
464 #endif
465 
466     // -------------------------------------------------------------------
467     // Plugin dispatcher calls
468 
bufferSizeChanged(const uint32_t bufferSize)469     void bufferSizeChanged(const uint32_t bufferSize) override
470     {
471         fPlugin.setBufferSize(bufferSize, true);
472     }
473 
sampleRateChanged(const double sampleRate)474     void sampleRateChanged(const double sampleRate) override
475     {
476         fPlugin.setSampleRate(sampleRate, true);
477     }
478 
479 #if DISTRHO_PLUGIN_HAS_UI
uiNameChanged(const char * const uiName)480     void uiNameChanged(const char* const uiName) override
481     {
482         CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,);
483 
484         fUiPtr->carla_setUiTitle(uiName);
485     }
486 #endif
487 
488     // -------------------------------------------------------------------
489 
490 private:
491     PluginExporter fPlugin;
492     mutable NativeParameterScalePoint* fScalePointsCache;
493 
494 #if DISTRHO_PLUGIN_HAS_UI
495     // UI
496     UICarla* fUiPtr;
497 
createUiIfNeeded()498     void createUiIfNeeded()
499     {
500         if (fUiPtr == nullptr)
501         {
502             d_lastUiSampleRate = getSampleRate();
503             fUiPtr = new UICarla(getHostHandle(), &fPlugin);
504         }
505     }
506 #endif
507 
508 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
writeMidiCallback(void * ptr,const MidiEvent & midiEvent)509     static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
510     {
511         if (midiEvent.size > 4)
512             return;
513 
514         const NativeMidiEvent event = {
515             midiEvent.frame, 0, midiEvent.size, midiEvent.data
516         };
517 
518         return ((PluginCarla*)ptr)->fPlugin.writeMidiEvent(midiEvent);
519     }
520 #endif
521 
522 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
requestParameterValueChange(const uint32_t index,const float value)523     bool requestParameterValueChange(const uint32_t index, const float value)
524     {
525         // TODO implementation
526         return false;
527     }
528 
requestParameterValueChangeCallback(void * ptr,const uint32_t index,const float value)529     static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value)
530     {
531         return thisPtr->requestParameterValueChange(index, value);
532     }
533 #endif
534 
535     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginCarla)
536 
537     // -------------------------------------------------------------------
538 
539 public:
_instantiate(const NativeHostDescriptor * host)540     static NativePluginHandle _instantiate(const NativeHostDescriptor* host)
541     {
542         d_lastBufferSize = host->get_buffer_size(host->handle);
543         d_lastSampleRate = host->get_sample_rate(host->handle);
544         return new PluginCarla(host);
545     }
546 
_cleanup(NativePluginHandle handle)547     static void _cleanup(NativePluginHandle handle)
548     {
549         delete (PluginCarla*)handle;
550     }
551 };
552 
553 END_NAMESPACE_DISTRHO
554 
555 // -----------------------------------------------------------------------
556