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