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