Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginCLAP.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2023 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 /* TODO items: | |
18 * CV: write a specification | |
19 * INFO: define url, manual url, support url and string version | |
20 * PARAMETERS: test parameter triggers | |
21 * States: skip DSP/UI only states as appropriate | |
22 * UI: expose external-only UIs | |
23 */ | |
24 | |
25 #include "DistrhoPluginInternal.hpp" | |
26 #include "extra/ScopedPointer.hpp" | |
27 | |
28 #ifndef DISTRHO_PLUGIN_CLAP_ID | |
29 # error DISTRHO_PLUGIN_CLAP_ID undefined! | |
30 #endif | |
31 | |
32 #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
33 # undef DISTRHO_PLUGIN_HAS_UI | |
34 # define DISTRHO_PLUGIN_HAS_UI 0 | |
35 #endif | |
36 | |
37 #if DISTRHO_PLUGIN_HAS_UI | |
38 # include "DistrhoUIInternal.hpp" | |
39 # include "../extra/Mutex.hpp" | |
40 #endif | |
41 | |
42 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
43 # include "../extra/RingBuffer.hpp" | |
44 #endif | |
45 | |
46 #include <map> | |
47 #include <vector> | |
48 | |
49 #include "clap/entry.h" | |
50 #include "clap/plugin-factory.h" | |
51 #include "clap/ext/audio-ports.h" | |
52 #include "clap/ext/latency.h" | |
53 #include "clap/ext/gui.h" | |
54 #include "clap/ext/note-ports.h" | |
55 #include "clap/ext/params.h" | |
56 #include "clap/ext/state.h" | |
57 #include "clap/ext/thread-check.h" | |
58 #include "clap/ext/timer-support.h" | |
59 | |
60 #if (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
61 # define DPF_CLAP_USING_HOST_TIMER 0 | |
62 #else | |
63 # define DPF_CLAP_USING_HOST_TIMER 1 | |
64 #endif | |
65 | |
66 #ifndef DPF_CLAP_TIMER_INTERVAL | |
67 # define DPF_CLAP_TIMER_INTERVAL 16 /* ~60 fps */ | |
68 #endif | |
69 | |
70 START_NAMESPACE_DISTRHO | |
71 | |
72 // -------------------------------------------------------------------------------------------------------------------- | |
73 | |
74 typedef std::map<const String, String> StringMap; | |
75 | |
76 struct ClapEventQueue | |
77 { | |
78 #if DISTRHO_PLUGIN_HAS_UI | |
79 enum EventType { | |
80 kEventGestureBegin, | |
81 kEventGestureEnd, | |
82 kEventParamSet | |
83 }; | |
84 | |
85 struct Event { | |
86 EventType type; | |
87 uint32_t index; | |
88 float value; | |
89 }; | |
90 | |
91 struct Queue { | |
92 RecursiveMutex lock; | |
93 uint allocated; | |
94 uint used; | |
95 Event* events; | |
96 | |
97 Queue() | |
98 : allocated(0), | |
99 used(0), | |
100 events(nullptr) {} | |
101 | |
102 ~Queue() | |
103 { | |
104 delete[] events; | |
105 } | |
106 | |
107 void addEventFromUI(const Event& event) | |
108 { | |
109 const RecursiveMutexLocker crml(lock); | |
110 | |
111 if (events == nullptr) | |
112 { | |
113 events = static_cast<Event*>(std::malloc(sizeof(Event) * 8)); | |
114 allocated = 8; | |
115 } | |
116 else if (used + 1 > allocated) | |
117 { | |
118 allocated = used * 2; | |
119 events = static_cast<Event*>(std::realloc(events, sizeof(Event) * allocated)); | |
120 } | |
121 | |
122 std::memcpy(&events[used++], &event, sizeof(Event)); | |
123 } | |
124 } fEventQueue; | |
125 | |
126 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
127 SmallStackBuffer fNotesBuffer; | |
128 #endif | |
129 #endif | |
130 | |
131 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
132 uint32_t fCurrentProgram; | |
133 #endif | |
134 | |
135 #if DISTRHO_PLUGIN_WANT_STATE | |
136 StringMap fStateMap; | |
137 #if DISTRHO_PLUGIN_HAS_UI | |
138 virtual void setStateFromUI(const char* key, const char* value) = 0; | |
139 #endif | |
140 #endif | |
141 | |
142 struct CachedParameters { | |
143 uint numParams; | |
144 bool* changed; | |
145 float* values; | |
146 | |
147 CachedParameters() | |
148 : numParams(0), | |
149 changed(nullptr), | |
150 values(nullptr) {} | |
151 | |
152 ~CachedParameters() | |
153 { | |
154 delete[] changed; | |
155 delete[] values; | |
156 } | |
157 | |
158 void setup(const uint numParameters) | |
159 { | |
160 if (numParameters == 0) | |
161 return; | |
162 | |
163 numParams = numParameters; | |
164 changed = new bool[numParameters]; | |
165 values = new float[numParameters]; | |
166 | |
167 std::memset(changed, 0, sizeof(bool)*numParameters); | |
168 std::memset(values, 0, sizeof(float)*numParameters); | |
169 } | |
170 } fCachedParameters; | |
171 | |
172 ClapEventQueue() | |
173 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
174 : fNotesBuffer(StackBuffer_INIT) | |
175 #endif | |
176 { | |
177 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) | |
178 std::memset(&fNotesBuffer, 0, sizeof(fNotesBuffer)); | |
179 #endif | |
180 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
181 fCurrentProgram = 0; | |
182 #endif | |
183 } | |
184 | |
185 virtual ~ClapEventQueue() {} | |
186 }; | |
187 | |
188 // -------------------------------------------------------------------------------------------------------------------- | |
189 | |
190 #if DISTRHO_PLUGIN_HAS_UI | |
191 | |
192 #if ! DISTRHO_PLUGIN_WANT_STATE | |
193 static constexpr const setStateFunc setStateCallback = nullptr; | |
194 #endif | |
195 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
196 static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |
197 #endif | |
198 | |
199 /** | |
200 * CLAP UI class. | |
201 */ | |
202 class ClapUI : public DGL_NAMESPACE::IdleCallback | |
203 { | |
204 public: | |
205 ClapUI(PluginExporter& plugin, | |
206 ClapEventQueue* const eventQueue, | |
207 const clap_host_t* const host, | |
208 const clap_host_gui_t* const hostGui, | |
209 #if DPF_CLAP_USING_HOST_TIMER | |
210 const clap_host_timer_support_t* const hostTimer, | |
211 #endif | |
212 const bool isFloating) | |
213 : fPlugin(plugin), | |
214 fPluginEventQueue(eventQueue), | |
215 fEventQueue(eventQueue->fEventQueue), | |
216 fCachedParameters(eventQueue->fCachedParameters), | |
217 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
218 fCurrentProgram(eventQueue->fCurrentProgram), | |
219 #endif | |
220 #if DISTRHO_PLUGIN_WANT_STATE | |
221 fStateMap(eventQueue->fStateMap), | |
222 #endif | |
223 fHost(host), | |
224 fHostGui(hostGui), | |
225 #if DPF_CLAP_USING_HOST_TIMER | |
226 fTimerId(0), | |
227 fHostTimer(hostTimer), | |
228 #else | |
229 fCallbackRegistered(false), | |
230 #endif | |
231 fIsFloating(isFloating), | |
232 fScaleFactor(0.0), | |
233 fParentWindow(0), | |
234 fTransientWindow(0) | |
235 { | |
236 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
237 fNotesRingBuffer.setRingBuffer(&eventQueue->fNotesBuffer, false); | |
238 #endif | |
239 } | |
240 | |
241 ~ClapUI() override | |
242 { | |
243 #if DPF_CLAP_USING_HOST_TIMER | |
244 if (fTimerId != 0) | |
245 fHostTimer->unregister_timer(fHost, fTimerId); | |
246 #else | |
247 if (fCallbackRegistered && fUI != nullptr) | |
248 fUI->removeIdleCallbackForNativeIdle(this); | |
249 #endif | |
250 } | |
251 | |
252 #ifndef DISTRHO_OS_MAC | |
253 bool setScaleFactor(const double scaleFactor) | |
254 { | |
255 if (d_isEqual(fScaleFactor, scaleFactor)) | |
256 return true; | |
257 | |
258 fScaleFactor = scaleFactor; | |
259 | |
260 if (UIExporter* const ui = fUI.get()) | |
261 ui->notifyScaleFactorChanged(scaleFactor); | |
262 | |
263 return true; | |
264 } | |
265 #endif | |
266 | |
267 bool getSize(uint32_t* const width, uint32_t* const height) const | |
268 { | |
269 if (UIExporter* const ui = fUI.get()) | |
270 { | |
271 *width = ui->getWidth(); | |
272 *height = ui->getHeight(); | |
273 #ifdef DISTRHO_OS_MAC | |
274 const double scaleFactor = ui->getScaleFactor(); | |
275 *width /= scaleFactor; | |
276 *height /= scaleFactor; | |
277 #endif | |
278 return true; | |
279 } | |
280 | |
281 double scaleFactor = fScaleFactor; | |
282 #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) | |
283 *width = DISTRHO_UI_DEFAULT_WIDTH; | |
284 *height = DISTRHO_UI_DEFAULT_HEIGHT; | |
285 if (d_isZero(scaleFactor)) | |
286 scaleFactor = 1.0; | |
287 #else | |
288 UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), | |
289 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, | |
290 fPlugin.getInstancePointer(), scaleFactor); | |
291 *width = tmpUI.getWidth(); | |
292 *height = tmpUI.getHeight(); | |
293 scaleFactor = tmpUI.getScaleFactor(); | |
294 tmpUI.quit(); | |
295 #endif | |
296 | |
297 #ifdef DISTRHO_OS_MAC | |
298 *width /= scaleFactor; | |
299 *height /= scaleFactor; | |
300 #endif | |
301 | |
302 return true; | |
303 } | |
304 | |
305 bool canResize() const noexcept | |
306 { | |
307 #if DISTRHO_UI_USER_RESIZABLE | |
308 if (UIExporter* const ui = fUI.get()) | |
309 return ui->isResizable(); | |
310 #endif | |
311 return false; | |
312 } | |
313 | |
314 bool getResizeHints(clap_gui_resize_hints_t* const hints) const | |
315 { | |
316 if (canResize()) | |
317 { | |
318 uint minimumWidth, minimumHeight; | |
319 bool keepAspectRatio; | |
320 fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); | |
321 | |
322 #ifdef DISTRHO_OS_MAC | |
323 const double scaleFactor = fUI->getScaleFactor(); | |
324 minimumWidth /= scaleFactor; | |
325 minimumHeight /= scaleFactor; | |
326 #endif | |
327 | |
328 hints->can_resize_horizontally = true; | |
329 hints->can_resize_vertically = true; | |
330 hints->preserve_aspect_ratio = keepAspectRatio; | |
331 hints->aspect_ratio_width = minimumWidth; | |
332 hints->aspect_ratio_height = minimumHeight; | |
333 | |
334 return true; | |
335 } | |
336 | |
337 hints->can_resize_horizontally = false; | |
338 hints->can_resize_vertically = false; | |
339 hints->preserve_aspect_ratio = false; | |
340 hints->aspect_ratio_width = 0; | |
341 hints->aspect_ratio_height = 0; | |
342 | |
343 return false; | |
344 } | |
345 | |
346 bool adjustSize(uint32_t* const width, uint32_t* const height) const | |
347 { | |
348 if (canResize()) | |
349 { | |
350 uint minimumWidth, minimumHeight; | |
351 bool keepAspectRatio; | |
352 fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); | |
353 | |
354 #ifdef DISTRHO_OS_MAC | |
355 const double scaleFactor = fUI->getScaleFactor(); | |
356 minimumWidth /= scaleFactor; | |
357 minimumHeight /= scaleFactor; | |
358 #endif | |
359 | |
360 if (keepAspectRatio) | |
361 { | |
362 if (*width < 1) | |
363 *width = 1; | |
364 if (*height < 1) | |
365 *height = 1; | |
366 | |
367 const double ratio = static_cast<double>(minimumWidth) / static_cast<double>(minimumHeight); | |
368 const double reqRatio = static_cast<double>(*width) / static_cast<double>(*height); | |
369 | |
370 if (d_isNotEqual(ratio, reqRatio)) | |
371 { | |
372 // fix width | |
373 if (reqRatio > ratio) | |
374 *width = static_cast<int32_t>(*height * ratio + 0.5); | |
375 // fix height | |
376 else | |
377 *height = static_cast<int32_t>(static_cast<double>(*width) / ratio + 0.5); | |
378 } | |
379 } | |
380 | |
381 if (minimumWidth > *width) | |
382 *width = minimumWidth; | |
383 if (minimumHeight > *height) | |
384 *height = minimumHeight; | |
385 | |
386 return true; | |
387 } | |
388 | |
389 return false; | |
390 } | |
391 | |
392 bool setSizeFromHost(uint32_t width, uint32_t height) | |
393 { | |
394 if (UIExporter* const ui = fUI.get()) | |
395 { | |
396 #ifdef DISTRHO_OS_MAC | |
397 const double scaleFactor = ui->getScaleFactor(); | |
398 width *= scaleFactor; | |
399 height *= scaleFactor; | |
400 #endif | |
401 ui->setWindowSizeFromHost(width, height); | |
402 return true; | |
403 } | |
404 | |
405 return false; | |
406 } | |
407 | |
408 bool setParent(const clap_window_t* const window) | |
409 { | |
410 if (fIsFloating) | |
411 return false; | |
412 | |
413 fParentWindow = window->uptr; | |
414 | |
415 if (fUI == nullptr) | |
416 { | |
417 createUI(); | |
418 fHostGui->resize_hints_changed(fHost); | |
419 } | |
420 | |
421 return true; | |
422 } | |
423 | |
424 bool setTransient(const clap_window_t* const window) | |
425 { | |
426 if (! fIsFloating) | |
427 return false; | |
428 | |
429 fTransientWindow = window->uptr; | |
430 | |
431 if (UIExporter* const ui = fUI.get()) | |
432 ui->setWindowTransientWinId(window->uptr); | |
433 | |
434 return true; | |
435 } | |
436 | |
437 void suggestTitle(const char* const title) | |
438 { | |
439 if (! fIsFloating) | |
440 return; | |
441 | |
442 fWindowTitle = title; | |
443 | |
444 if (UIExporter* const ui = fUI.get()) | |
445 ui->setWindowTitle(title); | |
446 } | |
447 | |
448 bool show() | |
449 { | |
450 if (fUI == nullptr) | |
451 { | |
452 createUI(); | |
453 fHostGui->resize_hints_changed(fHost); | |
454 } | |
455 | |
456 if (fIsFloating) | |
457 fUI->setWindowVisible(true); | |
458 | |
459 #if DPF_CLAP_USING_HOST_TIMER | |
460 fHostTimer->register_timer(fHost, DPF_CLAP_TIMER_INTERVAL, &fTimerId); | |
461 #else | |
462 fCallbackRegistered = true; | |
463 fUI->addIdleCallbackForNativeIdle(this, DPF_CLAP_TIMER_INTERVAL); | |
464 #endif | |
465 return true; | |
466 } | |
467 | |
468 bool hide() | |
469 { | |
470 if (UIExporter* const ui = fUI.get()) | |
471 { | |
472 ui->setWindowVisible(false); | |
473 #if DPF_CLAP_USING_HOST_TIMER | |
474 fHostTimer->unregister_timer(fHost, fTimerId); | |
475 fTimerId = 0; | |
476 #else | |
477 ui->removeIdleCallbackForNativeIdle(this); | |
478 fCallbackRegistered = false; | |
479 #endif | |
480 } | |
481 | |
482 return true; | |
483 } | |
484 | |
485 // ---------------------------------------------------------------------------------------------------------------- | |
486 | |
487 void idleCallback() override | |
488 { | |
489 if (UIExporter* const ui = fUI.get()) | |
490 { | |
491 #if DPF_CLAP_USING_HOST_TIMER | |
492 ui->plugin_idle(); | |
493 #else | |
494 ui->idleFromNativeIdle(); | |
495 #endif | |
496 | |
497 for (uint i=0; i<fCachedParameters.numParams; ++i) | |
498 { | |
499 if (fCachedParameters.changed[i]) | |
500 { | |
501 fCachedParameters.changed[i] = false; | |
502 ui->parameterChanged(i, fCachedParameters.values[i]); | |
503 } | |
504 } | |
505 } | |
506 } | |
507 | |
508 // ---------------------------------------------------------------------------------------------------------------- | |
509 | |
510 void setParameterValueFromPlugin(const uint index, const float value) | |
511 { | |
512 if (UIExporter* const ui = fUI.get()) | |
513 ui->parameterChanged(index, value); | |
514 } | |
515 | |
516 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
517 void setProgramFromPlugin(const uint index) | |
518 { | |
519 if (UIExporter* const ui = fUI.get()) | |
520 ui->programLoaded(index); | |
521 } | |
522 #endif | |
523 | |
524 #if DISTRHO_PLUGIN_WANT_STATE | |
525 void setStateFromPlugin(const char* const key, const char* const value) | |
526 { | |
527 if (UIExporter* const ui = fUI.get()) | |
528 ui->stateChanged(key, value); | |
529 } | |
530 #endif | |
531 | |
532 // ---------------------------------------------------------------------------------------------------------------- | |
533 | |
534 private: | |
535 // Plugin and UI | |
536 PluginExporter& fPlugin; | |
537 ClapEventQueue* const fPluginEventQueue; | |
538 ClapEventQueue::Queue& fEventQueue; | |
539 ClapEventQueue::CachedParameters& fCachedParameters; | |
540 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
541 uint32_t& fCurrentProgram; | |
542 #endif | |
543 #if DISTRHO_PLUGIN_WANT_STATE | |
544 StringMap& fStateMap; | |
545 #endif | |
546 const clap_host_t* const fHost; | |
547 const clap_host_gui_t* const fHostGui; | |
548 #if DPF_CLAP_USING_HOST_TIMER | |
549 clap_id fTimerId; | |
550 const clap_host_timer_support_t* const fHostTimer; | |
551 #else | |
552 bool fCallbackRegistered; | |
553 #endif | |
554 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
555 RingBufferControl<SmallStackBuffer> fNotesRingBuffer; | |
556 #endif | |
557 ScopedPointer<UIExporter> fUI; | |
558 | |
559 const bool fIsFloating; | |
560 | |
561 // Temporary data | |
562 double fScaleFactor; | |
563 uintptr_t fParentWindow; | |
564 uintptr_t fTransientWindow; | |
565 String fWindowTitle; | |
566 | |
567 // ---------------------------------------------------------------------------------------------------------------- | |
568 | |
569 void createUI() | |
570 { | |
571 DISTRHO_SAFE_ASSERT_RETURN(fUI == nullptr,); | |
572 | |
573 fUI = new UIExporter(this, | |
574 fParentWindow, | |
575 fPlugin.getSampleRate(), | |
576 editParameterCallback, | |
577 setParameterCallback, | |
578 setStateCallback, | |
579 sendNoteCallback, | |
580 setSizeCallback, | |
581 nullptr, // TODO fileRequestCallback, | |
582 d_nextBundlePath, | |
583 fPlugin.getInstancePointer(), | |
584 fScaleFactor); | |
585 | |
586 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
587 fUI->programLoaded(fCurrentProgram); | |
588 #endif | |
589 | |
590 #if DISTRHO_PLUGIN_WANT_FULL_STATE | |
591 // Update current state from plugin side | |
592 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
593 { | |
594 const String& key = cit->first; | |
595 fStateMap[key] = fPlugin.getStateValue(key); | |
596 } | |
597 #endif | |
598 | |
599 #if DISTRHO_PLUGIN_WANT_STATE | |
600 // Set state | |
601 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
602 { | |
603 const String& key = cit->first; | |
604 const String& value = cit->second; | |
605 | |
606 // TODO skip DSP only states | |
607 | |
608 fUI->stateChanged(key, value); | |
609 } | |
610 #endif | |
611 | |
612 for (uint32_t i=0; i<fCachedParameters.numParams; ++i) | |
613 { | |
614 const float value = fCachedParameters.values[i] = fPlugin.getParameterValue(i); | |
615 fCachedParameters.changed[i] = false; | |
616 fUI->parameterChanged(i, value); | |
617 } | |
618 | |
619 if (fIsFloating) | |
620 { | |
621 if (fWindowTitle.isNotEmpty()) | |
622 fUI->setWindowTitle(fWindowTitle); | |
623 | |
624 if (fTransientWindow != 0) | |
625 fUI->setWindowTransientWinId(fTransientWindow); | |
626 } | |
627 } | |
628 | |
629 // ---------------------------------------------------------------------------------------------------------------- | |
630 // DPF callbacks | |
631 | |
632 void editParameter(const uint32_t rindex, const bool started) const | |
633 { | |
634 const ClapEventQueue::Event ev = { | |
635 started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureEnd, | |
636 rindex, 0.f | |
637 }; | |
638 fEventQueue.addEventFromUI(ev); | |
639 } | |
640 | |
641 static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) | |
642 { | |
643 static_cast<ClapUI*>(ptr)->editParameter(rindex, started); | |
644 } | |
645 | |
646 void setParameterValue(const uint32_t rindex, const float value) | |
647 { | |
648 const ClapEventQueue::Event ev = { | |
649 ClapEventQueue::kEventParamSet, | |
650 rindex, value | |
651 }; | |
652 fEventQueue.addEventFromUI(ev); | |
653 } | |
654 | |
655 static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) | |
656 { | |
657 static_cast<ClapUI*>(ptr)->setParameterValue(rindex, value); | |
658 } | |
659 | |
660 void setSizeFromPlugin(const uint width, const uint height) | |
661 { | |
662 DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |
663 | |
664 #ifdef DISTRHO_OS_MAC | |
665 const double scaleFactor = fUI->getScaleFactor(); | |
666 const uint hostWidth = width / scaleFactor; | |
667 const uint hostHeight = height / scaleFactor; | |
668 #else | |
669 const uint hostWidth = width; | |
670 const uint hostHeight = height; | |
671 #endif | |
672 | |
673 if (fHostGui->request_resize(fHost, hostWidth, hostHeight)) | |
674 fUI->setWindowSizeFromHost(width, height); | |
675 } | |
676 | |
677 static void setSizeCallback(void* const ptr, const uint width, const uint height) | |
678 { | |
679 static_cast<ClapUI*>(ptr)->setSizeFromPlugin(width, height); | |
680 } | |
681 | |
682 #if DISTRHO_PLUGIN_WANT_STATE | |
683 void setState(const char* const key, const char* const value) | |
684 { | |
685 fPluginEventQueue->setStateFromUI(key, value); | |
686 } | |
687 | |
688 static void setStateCallback(void* const ptr, const char* key, const char* value) | |
689 { | |
690 static_cast<ClapUI*>(ptr)->setState(key, value); | |
691 } | |
692 #endif | |
693 | |
694 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
695 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
696 { | |
697 uint8_t midiData[3]; | |
698 midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; | |
699 midiData[1] = note; | |
700 midiData[2] = velocity; | |
701 fNotesRingBuffer.writeCustomData(midiData, 3); | |
702 fNotesRingBuffer.commitWrite(); | |
703 } | |
704 | |
705 static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
706 { | |
707 static_cast<ClapUI*>(ptr)->sendNote(channel, note, velocity); | |
708 } | |
709 #endif | |
710 | |
711 /* TODO | |
712 bool fileRequest(const char*) | |
713 { | |
714 return true; | |
715 } | |
716 | |
717 static bool fileRequestCallback(void* const ptr, const char* const key) | |
718 { | |
719 return static_cast<ClapUI*>(ptr)->fileRequest(key); | |
720 } | |
721 */ | |
722 }; | |
723 | |
724 // -------------------------------------------------------------------------------------------------------------------- | |
725 | |
726 #endif // DISTRHO_PLUGIN_HAS_UI | |
727 | |
728 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
729 static constexpr const writeMidiFunc writeMidiCallback = nullptr; | |
730 #endif | |
731 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
732 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | |
733 #endif | |
734 #if ! DISTRHO_PLUGIN_WANT_STATE | |
735 static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; | |
736 #endif | |
737 | |
738 // -------------------------------------------------------------------------------------------------------------------- | |
739 | |
740 /** | |
741 * CLAP plugin class. | |
742 */ | |
743 class PluginCLAP : ClapEventQueue | |
744 { | |
745 public: | |
746 PluginCLAP(const clap_host_t* const host) | |
747 : fPlugin(this, | |
748 writeMidiCallback, | |
749 requestParameterValueChangeCallback, | |
750 updateStateValueCallback), | |
751 fHost(host), | |
752 fOutputEvents(nullptr), | |
753 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
754 fUsingCV(false), | |
755 #endif | |
756 #if DISTRHO_PLUGIN_WANT_LATENCY | |
757 fLatencyChanged(false), | |
758 fLastKnownLatency(0), | |
759 #endif | |
760 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
761 fMidiEventCount(0), | |
762 #endif | |
763 fHostExtensions(host) | |
764 { | |
765 fCachedParameters.setup(fPlugin.getParameterCount()); | |
766 | |
767 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
768 fNotesRingBuffer.setRingBuffer(&fNotesBuffer, true); | |
769 #endif | |
770 | |
771 #if DISTRHO_PLUGIN_WANT_STATE | |
772 for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i) | |
773 { | |
774 const String& dkey(fPlugin.getStateKey(i)); | |
775 fStateMap[dkey] = fPlugin.getStateDefaultValue(i); | |
776 } | |
777 #endif | |
778 | |
779 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 | |
780 fillInBusInfoDetails<true>(); | |
781 #endif | |
782 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
783 fillInBusInfoDetails<false>(); | |
784 #endif | |
785 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
786 fillInBusInfoPairs(); | |
787 #endif | |
788 } | |
789 | |
790 // ---------------------------------------------------------------------------------------------------------------- | |
791 // core | |
792 | |
793 template <class T> | |
794 const T* getHostExtension(const char* const extensionId) const | |
795 { | |
796 return static_cast<const T*>(fHost->get_extension(fHost, extensionId)); | |
797 } | |
798 | |
799 bool init() | |
800 { | |
801 if (!clap_version_is_compatible(fHost->clap_version)) | |
802 return false; | |
803 | |
804 return fHostExtensions.init(); | |
805 } | |
806 | |
807 void activate(const double sampleRate, const uint32_t maxFramesCount) | |
808 { | |
809 fPlugin.setSampleRate(sampleRate, true); | |
810 fPlugin.setBufferSize(maxFramesCount, true); | |
811 fPlugin.activate(); | |
812 } | |
813 | |
814 void deactivate() | |
815 { | |
816 fPlugin.deactivate(); | |
817 #if DISTRHO_PLUGIN_WANT_LATENCY | |
818 checkForLatencyChanges(false, true); | |
819 reportLatencyChangeIfNeeded(); | |
820 #endif | |
821 } | |
822 | |
823 bool process(const clap_process_t* const process) | |
824 { | |
825 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
826 fMidiEventCount = 0; | |
827 #endif | |
828 | |
829 #if DISTRHO_PLUGIN_HAS_UI | |
830 if (const clap_output_events_t* const outputEvents = process->out_events) | |
831 { | |
832 const RecursiveMutexTryLocker crmtl(fEventQueue.lock); | |
833 | |
834 if (crmtl.wasLocked()) | |
835 { | |
836 // reuse the same struct for gesture and parameters, they are compatible up to where it matters | |
837 clap_event_param_value_t clapEvent = { | |
838 { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE }, | |
839 0, nullptr, 0, 0, 0, 0, 0.0 | |
840 }; | |
841 | |
842 for (uint32_t i=0; i<fEventQueue.used; ++i) | |
843 { | |
844 const Event& event(fEventQueue.events[i]); | |
845 | |
846 switch (event.type) | |
847 { | |
848 case kEventGestureBegin: | |
849 clapEvent.header.size = sizeof(clap_event_param_gesture_t); | |
850 clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_BEGIN; | |
851 clapEvent.param_id = event.index; | |
852 break; | |
853 case kEventGestureEnd: | |
854 clapEvent.header.size = sizeof(clap_event_param_gesture_t); | |
855 clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_END; | |
856 clapEvent.param_id = event.index; | |
857 break; | |
858 case kEventParamSet: | |
859 clapEvent.header.size = sizeof(clap_event_param_value_t); | |
860 clapEvent.header.type = CLAP_EVENT_PARAM_VALUE; | |
861 clapEvent.param_id = event.index; | |
862 clapEvent.value = event.value; | |
863 fPlugin.setParameterValue(event.index, event.value); | |
864 break; | |
865 default: | |
866 continue; | |
867 } | |
868 | |
869 outputEvents->try_push(outputEvents, &clapEvent.header); | |
870 } | |
871 | |
872 fEventQueue.used = 0; | |
873 } | |
874 } | |
875 #endif | |
876 | |
877 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
878 if (const clap_event_transport_t* const transport = process->transport) | |
879 { | |
880 fTimePosition.playing = (transport->flags & CLAP_TRANSPORT_IS_PLAYING) != 0 && | |
881 (transport->flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL) == 0; | |
882 | |
883 fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0; | |
884 | |
885 if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO) | |
886 fTimePosition.bbt.beatsPerMinute = transport->tempo; | |
887 else | |
888 fTimePosition.bbt.beatsPerMinute = 120.0; | |
889 | |
890 // ticksPerBeat is not possible with CLAP | |
891 fTimePosition.bbt.ticksPerBeat = 1920.0; | |
892 | |
893 if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) | |
894 { | |
895 if (transport->song_pos_beats >= 0) | |
896 { | |
897 const int64_t clapPos = std::abs(transport->song_pos_beats); | |
898 const int64_t clapBeats = clapPos >> 31; | |
899 const double clapRest = static_cast<double>(clapPos & 0x7fffffff) / CLAP_BEATTIME_FACTOR; | |
900 | |
901 fTimePosition.bbt.bar = static_cast<int32_t>(clapBeats) / transport->tsig_num + 1; | |
902 fTimePosition.bbt.beat = static_cast<int32_t>(clapBeats % transport->tsig_num) + 1; | |
903 fTimePosition.bbt.tick = clapRest * fTimePosition.bbt.ticksPerBeat; | |
904 } | |
905 else | |
906 { | |
907 fTimePosition.bbt.bar = 1; | |
908 fTimePosition.bbt.beat = 1; | |
909 fTimePosition.bbt.tick = 0.0; | |
910 } | |
911 | |
912 fTimePosition.bbt.valid = true; | |
913 fTimePosition.bbt.beatsPerBar = transport->tsig_num; | |
914 fTimePosition.bbt.beatType = transport->tsig_denom; | |
915 } | |
916 else | |
917 { | |
918 fTimePosition.bbt.valid = false; | |
919 fTimePosition.bbt.bar = 1; | |
920 fTimePosition.bbt.beat = 1; | |
921 fTimePosition.bbt.tick = 0.0; | |
922 fTimePosition.bbt.beatsPerBar = 4.0f; | |
923 fTimePosition.bbt.beatType = 4.0f; | |
924 } | |
925 | |
926 fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* | |
927 fTimePosition.bbt.beatsPerBar* | |
928 (fTimePosition.bbt.bar-1); | |
929 } | |
930 else | |
931 { | |
932 fTimePosition.playing = false; | |
933 fTimePosition.frame = 0; | |
934 fTimePosition.bbt.valid = false; | |
935 fTimePosition.bbt.beatsPerMinute = 120.0; | |
936 fTimePosition.bbt.bar = 1; | |
937 fTimePosition.bbt.beat = 1; | |
938 fTimePosition.bbt.tick = 0.0; | |
939 fTimePosition.bbt.beatsPerBar = 4.f; | |
940 fTimePosition.bbt.beatType = 4.f; | |
941 fTimePosition.bbt.barStartTick = 0; | |
942 } | |
943 | |
944 fPlugin.setTimePosition(fTimePosition); | |
945 #endif | |
946 | |
947 if (const clap_input_events_t* const inputEvents = process->in_events) | |
948 { | |
949 if (const uint32_t len = inputEvents->size(inputEvents)) | |
950 { | |
951 for (uint32_t i=0; i<len; ++i) | |
952 { | |
953 const clap_event_header_t* const event = inputEvents->get(inputEvents, i); | |
954 | |
955 switch (event->type) | |
956 { | |
957 case CLAP_EVENT_NOTE_ON: | |
958 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
959 // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway | |
960 addNoteEvent(static_cast<const clap_event_note_t*>(static_cast<const void*>(event)), true); | |
961 #endif | |
962 break; | |
963 case CLAP_EVENT_NOTE_OFF: | |
964 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
965 // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway | |
966 addNoteEvent(static_cast<const clap_event_note_t*>(static_cast<const void*>(event)), false); | |
967 #endif | |
968 break; | |
969 case CLAP_EVENT_NOTE_CHOKE: | |
970 case CLAP_EVENT_NOTE_END: | |
971 case CLAP_EVENT_NOTE_EXPRESSION: | |
972 break; | |
973 case CLAP_EVENT_PARAM_VALUE: | |
974 DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), | |
975 event->size, sizeof(clap_event_param_value)); | |
976 setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event))); | |
977 break; | |
978 case CLAP_EVENT_PARAM_MOD: | |
979 case CLAP_EVENT_PARAM_GESTURE_BEGIN: | |
980 case CLAP_EVENT_PARAM_GESTURE_END: | |
981 case CLAP_EVENT_TRANSPORT: | |
982 break; | |
983 case CLAP_EVENT_MIDI: | |
984 DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t), | |
985 event->size, sizeof(clap_event_midi_t)); | |
986 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
987 addMidiEvent(static_cast<const clap_event_midi_t*>(static_cast<const void*>(event))); | |
988 #endif | |
989 break; | |
990 case CLAP_EVENT_MIDI_SYSEX: | |
991 case CLAP_EVENT_MIDI2: | |
992 break; | |
993 } | |
994 } | |
995 } | |
996 } | |
997 | |
998 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
999 if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) | |
1000 { | |
1001 uint8_t midiData[3]; | |
1002 const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0; | |
1003 | |
1004 while (fNotesRingBuffer.isDataAvailableForReading()) | |
1005 { | |
1006 if (! fNotesRingBuffer.readCustomData(midiData, 3)) | |
1007 break; | |
1008 | |
1009 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); | |
1010 midiEvent.frame = frame; | |
1011 midiEvent.size = 3; | |
1012 std::memcpy(midiEvent.data, midiData, 3); | |
1013 | |
1014 if (fMidiEventCount == kMaxMidiEvents) | |
1015 break; | |
1016 } | |
1017 } | |
1018 #endif | |
1019 | |
1020 if (const uint32_t frames = process->frames_count) | |
1021 { | |
1022 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 | |
1023 const float** const audioInputs = fAudioInputs; | |
1024 | |
1025 uint32_t in=0; | |
1026 for (uint32_t i=0; i<process->audio_inputs_count; ++i) | |
1027 { | |
1028 const clap_audio_buffer_t& inputs(process->audio_inputs[i]); | |
1029 DISTRHO_SAFE_ASSERT_CONTINUE(inputs.channel_count != 0); | |
1030 | |
1031 for (uint32_t j=0; j<inputs.channel_count; ++j, ++in) | |
1032 audioInputs[in] = const_cast<const float*>(inputs.data32[j]); | |
1033 } | |
1034 | |
1035 if (fUsingCV) | |
1036 { | |
1037 for (; in<DISTRHO_PLUGIN_NUM_INPUTS; ++in) | |
1038 audioInputs[in] = nullptr; | |
1039 } | |
1040 else | |
1041 { | |
1042 DISTRHO_SAFE_ASSERT_UINT2_RETURN(in == DISTRHO_PLUGIN_NUM_INPUTS, | |
1043 in, process->audio_inputs_count, false); | |
1044 } | |
1045 #else | |
1046 constexpr const float** const audioInputs = nullptr; | |
1047 #endif | |
1048 | |
1049 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1050 float** const audioOutputs = fAudioOutputs; | |
1051 | |
1052 uint32_t out=0; | |
1053 for (uint32_t i=0; i<process->audio_outputs_count; ++i) | |
1054 { | |
1055 const clap_audio_buffer_t& outputs(process->audio_outputs[i]); | |
1056 DISTRHO_SAFE_ASSERT_CONTINUE(outputs.channel_count != 0); | |
1057 | |
1058 for (uint32_t j=0; j<outputs.channel_count; ++j, ++out) | |
1059 audioOutputs[out] = outputs.data32[j]; | |
1060 } | |
1061 | |
1062 if (fUsingCV) | |
1063 { | |
1064 for (; out<DISTRHO_PLUGIN_NUM_OUTPUTS; ++out) | |
1065 audioOutputs[out] = nullptr; | |
1066 } | |
1067 else | |
1068 { | |
1069 DISTRHO_SAFE_ASSERT_UINT2_RETURN(out == DISTRHO_PLUGIN_NUM_OUTPUTS, | |
1070 out, DISTRHO_PLUGIN_NUM_OUTPUTS, false); | |
1071 } | |
1072 #else | |
1073 constexpr float** const audioOutputs = nullptr; | |
1074 #endif | |
1075 | |
1076 fOutputEvents = process->out_events; | |
1077 | |
1078 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
1079 fPlugin.run(audioInputs, audioOutputs, frames, fMidiEvents, fMidiEventCount); | |
1080 #else | |
1081 fPlugin.run(audioInputs, audioOutputs, frames); | |
1082 #endif | |
1083 | |
1084 flushParameters(nullptr, process->out_events, frames - 1); | |
1085 | |
1086 fOutputEvents = nullptr; | |
1087 } | |
1088 | |
1089 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1090 checkForLatencyChanges(true, false); | |
1091 #endif | |
1092 | |
1093 return true; | |
1094 } | |
1095 | |
1096 void onMainThread() | |
1097 { | |
1098 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1099 reportLatencyChangeIfNeeded(); | |
1100 #endif | |
1101 } | |
1102 | |
1103 // ---------------------------------------------------------------------------------------------------------------- | |
1104 // parameters | |
1105 | |
1106 uint32_t getParameterCount() const | |
1107 { | |
1108 return fPlugin.getParameterCount(); | |
1109 } | |
1110 | |
1111 bool getParameterInfo(const uint32_t index, clap_param_info_t* const info) const | |
1112 { | |
1113 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | |
1114 | |
1115 if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass) | |
1116 { | |
1117 info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE; | |
1118 std::strcpy(info->name, "Bypass"); | |
1119 std::strcpy(info->module, "dpf_bypass"); | |
1120 } | |
1121 else | |
1122 { | |
1123 const uint32_t hints = fPlugin.getParameterHints(index); | |
1124 const uint32_t groupId = fPlugin.getParameterGroupId(index); | |
1125 | |
1126 info->flags = 0; | |
1127 if (hints & kParameterIsOutput) | |
1128 info->flags |= CLAP_PARAM_IS_READONLY; | |
1129 else if (hints & kParameterIsAutomatable) | |
1130 info->flags |= CLAP_PARAM_IS_AUTOMATABLE; | |
1131 | |
1132 if (hints & (kParameterIsBoolean|kParameterIsInteger)) | |
1133 info->flags |= CLAP_PARAM_IS_STEPPED; | |
1134 | |
1135 d_strncpy(info->name, fPlugin.getParameterName(index), CLAP_NAME_SIZE); | |
1136 | |
1137 uint wrtn; | |
1138 if (groupId != kPortGroupNone) | |
1139 { | |
1140 const PortGroupWithId& portGroup(fPlugin.getPortGroupById(groupId)); | |
1141 strncpy(info->module, portGroup.symbol, CLAP_PATH_SIZE / 2); | |
1142 info->module[CLAP_PATH_SIZE / 2] = '\0'; | |
1143 wrtn = std::strlen(info->module); | |
1144 info->module[wrtn++] = '/'; | |
1145 } | |
1146 else | |
1147 { | |
1148 wrtn = 0; | |
1149 } | |
1150 | |
1151 d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn); | |
1152 } | |
1153 | |
1154 info->id = index; | |
1155 info->cookie = nullptr; | |
1156 info->min_value = ranges.min; | |
1157 info->max_value = ranges.max; | |
1158 info->default_value = ranges.def; | |
1159 return true; | |
1160 } | |
1161 | |
1162 bool getParameterValue(const clap_id param_id, double* const value) const | |
1163 { | |
1164 *value = fPlugin.getParameterValue(param_id); | |
1165 return true; | |
1166 } | |
1167 | |
1168 bool getParameterStringForValue(const clap_id param_id, double value, char* const display, const uint32_t size) const | |
1169 { | |
1170 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id)); | |
1171 const ParameterRanges& ranges(fPlugin.getParameterRanges(param_id)); | |
1172 const uint32_t hints = fPlugin.getParameterHints(param_id); | |
1173 | |
1174 if (hints & kParameterIsBoolean) | |
1175 { | |
1176 const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f; | |
1177 value = value > midRange ? ranges.max : ranges.min; | |
1178 } | |
1179 else if (hints & kParameterIsInteger) | |
1180 { | |
1181 value = std::round(value); | |
1182 } | |
1183 | |
1184 for (uint32_t i=0; i < enumValues.count; ++i) | |
1185 { | |
1186 if (d_isEqual(static_cast<double>(enumValues.values[i].value), value)) | |
1187 { | |
1188 d_strncpy(display, enumValues.values[i].label, size); | |
1189 return true; | |
1190 } | |
1191 } | |
1192 | |
1193 if (hints & kParameterIsInteger) | |
1194 snprintf_i32(display, value, size); | |
1195 else | |
1196 snprintf_f32(display, value, size); | |
1197 | |
1198 return true; | |
1199 } | |
1200 | |
1201 bool getParameterValueForString(const clap_id param_id, const char* const display, double* const value) const | |
1202 { | |
1203 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id)); | |
1204 const bool isInteger = fPlugin.isParameterInteger(param_id); | |
1205 | |
1206 for (uint32_t i=0; i < enumValues.count; ++i) | |
1207 { | |
1208 if (std::strcmp(display, enumValues.values[i].label) == 0) | |
1209 { | |
1210 *value = enumValues.values[i].value; | |
1211 return true; | |
1212 } | |
1213 } | |
1214 | |
1215 if (isInteger) | |
1216 *value = std::atoi(display); | |
1217 else | |
1218 *value = std::atof(display); | |
1219 | |
1220 return true; | |
1221 } | |
1222 | |
1223 void flushParameters(const clap_input_events_t* const in, | |
1224 const clap_output_events_t* const out, | |
1225 const uint32_t frameOffset) | |
1226 { | |
1227 if (const uint32_t len = in != nullptr ? in->size(in) : 0) | |
1228 { | |
1229 for (uint32_t i=0; i<len; ++i) | |
1230 { | |
1231 const clap_event_header_t* const event = in->get(in, i); | |
1232 | |
1233 if (event->type != CLAP_EVENT_PARAM_VALUE) | |
1234 continue; | |
1235 | |
1236 DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), | |
1237 event->size, sizeof(clap_event_param_value)); | |
1238 | |
1239 setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event))); | |
1240 } | |
1241 } | |
1242 | |
1243 if (out != nullptr) | |
1244 { | |
1245 clap_event_param_value_t clapEvent = { | |
1246 { sizeof(clap_event_param_value_t), frameOffset, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE }, | |
1247 0, nullptr, 0, 0, 0, 0, 0.0 | |
1248 }; | |
1249 | |
1250 float value; | |
1251 for (uint i=0; i<fCachedParameters.numParams; ++i) | |
1252 { | |
1253 if (fPlugin.isParameterOutputOrTrigger(i)) | |
1254 { | |
1255 value = fPlugin.getParameterValue(i); | |
1256 | |
1257 if (d_isEqual(fCachedParameters.values[i], value)) | |
1258 continue; | |
1259 | |
1260 fCachedParameters.values[i] = value; | |
1261 fCachedParameters.changed[i] = true; | |
1262 | |
1263 clapEvent.param_id = i; | |
1264 clapEvent.value = value; | |
1265 out->try_push(out, &clapEvent.header); | |
1266 } | |
1267 } | |
1268 } | |
1269 | |
1270 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1271 const bool active = fPlugin.isActive(); | |
1272 checkForLatencyChanges(active, !active); | |
1273 #endif | |
1274 } | |
1275 | |
1276 // ---------------------------------------------------------------------------------------------------------------- | |
1277 | |
1278 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
1279 void addNoteEvent(const clap_event_note_t* const event, const bool isOn) noexcept | |
1280 { | |
1281 DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,); | |
1282 | |
1283 if (fMidiEventCount == kMaxMidiEvents) | |
1284 return; | |
1285 | |
1286 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); | |
1287 midiEvent.frame = event->header.time; | |
1288 midiEvent.size = 3; | |
1289 midiEvent.data[0] = (isOn ? 0x90 : 0x80) | (event->channel & 0x0F); | |
1290 midiEvent.data[1] = std::max(0, std::min(127, static_cast<int>(event->key))); | |
1291 midiEvent.data[2] = std::max(0, std::min(127, static_cast<int>(event->velocity * 127 + 0.5))); | |
1292 } | |
1293 | |
1294 void addMidiEvent(const clap_event_midi_t* const event) noexcept | |
1295 { | |
1296 DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,); | |
1297 | |
1298 if (fMidiEventCount == kMaxMidiEvents) | |
1299 return; | |
1300 | |
1301 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); | |
1302 midiEvent.frame = event->header.time; | |
1303 midiEvent.size = 3; | |
1304 std::memcpy(midiEvent.data, event->data, 3); | |
1305 } | |
1306 #endif | |
1307 | |
1308 void setParameterValueFromEvent(const clap_event_param_value* const event) | |
1309 { | |
1310 fCachedParameters.values[event->param_id] = event->value; | |
1311 fCachedParameters.changed[event->param_id] = true; | |
1312 fPlugin.setParameterValue(event->param_id, event->value); | |
1313 } | |
1314 | |
1315 // ---------------------------------------------------------------------------------------------------------------- | |
1316 // audio ports | |
1317 | |
1318 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1319 template<bool isInput> | |
1320 uint32_t getAudioPortCount() const noexcept | |
1321 { | |
1322 return (isInput ? fAudioInputBuses : fAudioOutputBuses).size(); | |
1323 } | |
1324 | |
1325 template<bool isInput> | |
1326 bool getAudioPortInfo(const uint32_t index, clap_audio_port_info_t* const info) const noexcept | |
1327 { | |
1328 const std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses); | |
1329 DISTRHO_SAFE_ASSERT_RETURN(index < busInfos.size(), false); | |
1330 | |
1331 const BusInfo& busInfo(busInfos[index]); | |
1332 | |
1333 info->id = busInfo.groupId; | |
1334 d_strncpy(info->name, busInfo.name, CLAP_NAME_SIZE); | |
1335 | |
1336 info->flags = busInfo.isMain ? CLAP_AUDIO_PORT_IS_MAIN : 0x0; | |
1337 info->channel_count = busInfo.numChannels; | |
1338 | |
1339 switch (busInfo.groupId) | |
1340 { | |
1341 case kPortGroupMono: | |
1342 info->port_type = CLAP_PORT_MONO; | |
1343 break; | |
1344 case kPortGroupStereo: | |
1345 info->port_type = CLAP_PORT_STEREO; | |
1346 break; | |
1347 default: | |
1348 info->port_type = nullptr; | |
1349 break; | |
1350 } | |
1351 | |
1352 info->in_place_pair = busInfo.hasPair ? busInfo.groupId : CLAP_INVALID_ID; | |
1353 return true; | |
1354 } | |
1355 #endif | |
1356 | |
1357 // ---------------------------------------------------------------------------------------------------------------- | |
1358 // latency | |
1359 | |
1360 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1361 uint32_t getLatency() const noexcept | |
1362 { | |
1363 return fPlugin.getLatency(); | |
1364 } | |
1365 | |
1366 void checkForLatencyChanges(const bool isActive, const bool fromMainThread) | |
1367 { | |
1368 const uint32_t latency = fPlugin.getLatency(); | |
1369 | |
1370 if (fLastKnownLatency == latency) | |
1371 return; | |
1372 | |
1373 fLastKnownLatency = latency; | |
1374 | |
1375 if (isActive) | |
1376 { | |
1377 fLatencyChanged = true; | |
1378 fHost->request_restart(fHost); | |
1379 } | |
1380 else | |
1381 { | |
1382 // if this is main-thread we can report latency change directly | |
1383 if (fromMainThread || (fHostExtensions.threadCheck != nullptr && fHostExtensions.threadCheck->is_main_thread(fHost))) | |
1384 { | |
1385 fLatencyChanged = false; | |
1386 fHostExtensions.latency->changed(fHost); | |
1387 } | |
1388 // otherwise we need to request a main-thread callback | |
1389 else | |
1390 { | |
1391 fLatencyChanged = true; | |
1392 fHost->request_callback(fHost); | |
1393 } | |
1394 } | |
1395 } | |
1396 | |
1397 // called from main thread | |
1398 void reportLatencyChangeIfNeeded() | |
1399 { | |
1400 if (fLatencyChanged) | |
1401 { | |
1402 fLatencyChanged = false; | |
1403 fHostExtensions.latency->changed(fHost); | |
1404 } | |
1405 } | |
1406 #endif | |
1407 | |
1408 // ---------------------------------------------------------------------------------------------------------------- | |
1409 // state | |
1410 | |
1411 bool stateSave(const clap_ostream_t* const stream) | |
1412 { | |
1413 const uint32_t paramCount = fPlugin.getParameterCount(); | |
1414 #if DISTRHO_PLUGIN_WANT_STATE | |
1415 const uint32_t stateCount = fPlugin.getStateCount(); | |
1416 #else | |
1417 const uint32_t stateCount = 0; | |
1418 #endif | |
1419 | |
1420 if (stateCount == 0 && paramCount == 0) | |
1421 { | |
1422 char buffer = '\0'; | |
1423 return stream->write(stream, &buffer, 1) == 1; | |
1424 } | |
1425 | |
1426 #if DISTRHO_PLUGIN_WANT_FULL_STATE | |
1427 // Update current state | |
1428 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
1429 { | |
1430 const String& key = cit->first; | |
1431 fStateMap[key] = fPlugin.getStateValue(key); | |
1432 } | |
1433 #endif | |
1434 | |
1435 String state; | |
1436 | |
1437 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
1438 { | |
1439 String tmpStr("__dpf_program__\xff"); | |
1440 tmpStr += String(fCurrentProgram); | |
1441 tmpStr += "\xff"; | |
1442 | |
1443 state += tmpStr; | |
1444 } | |
1445 #endif | |
1446 | |
1447 #if DISTRHO_PLUGIN_WANT_STATE | |
1448 if (stateCount != 0) | |
1449 { | |
1450 state += "__dpf_state_begin__\xff"; | |
1451 | |
1452 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
1453 { | |
1454 const String& key = cit->first; | |
1455 const String& value = cit->second; | |
1456 | |
1457 // join key and value | |
1458 String tmpStr; | |
1459 tmpStr = key; | |
1460 tmpStr += "\xff"; | |
1461 tmpStr += value; | |
1462 tmpStr += "\xff"; | |
1463 | |
1464 state += tmpStr; | |
1465 } | |
1466 | |
1467 state += "__dpf_state_end__\xff"; | |
1468 } | |
1469 #endif | |
1470 | |
1471 if (paramCount != 0) | |
1472 { | |
1473 state += "__dpf_parameters_begin__\xff"; | |
1474 | |
1475 for (uint32_t i=0; i<paramCount; ++i) | |
1476 { | |
1477 if (fPlugin.isParameterOutputOrTrigger(i)) | |
1478 continue; | |
1479 | |
1480 // join key and value | |
1481 String tmpStr; | |
1482 tmpStr = fPlugin.getParameterSymbol(i); | |
1483 tmpStr += "\xff"; | |
1484 if (fPlugin.getParameterHints(i) & kParameterIsInteger) | |
1485 tmpStr += String(static_cast<int>(std::round(fPlugin.getParameterValue(i)))); | |
1486 else | |
1487 tmpStr += String(fPlugin.getParameterValue(i)); | |
1488 tmpStr += "\xff"; | |
1489 | |
1490 state += tmpStr; | |
1491 } | |
1492 | |
1493 state += "__dpf_parameters_end__\xff"; | |
1494 } | |
1495 | |
1496 // terminator | |
1497 state += "\xfe"; | |
1498 | |
1499 state.replace('\xff', '\0'); | |
1500 | |
1501 // now saving state, carefully until host written bytes matches full state size | |
1502 const char* buffer = state.buffer(); | |
1503 const int32_t size = static_cast<int32_t>(state.length())+1; | |
1504 | |
1505 for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn) | |
1506 { | |
1507 wrtn = stream->write(stream, buffer, size - wrtntotal); | |
1508 DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, false); | |
1509 } | |
1510 | |
1511 return true; | |
1512 } | |
1513 | |
1514 bool stateLoad(const clap_istream_t* const stream) | |
1515 { | |
1516 #if DISTRHO_PLUGIN_HAS_UI | |
1517 ClapUI* const ui = fUI.get(); | |
1518 #endif | |
1519 String key, value; | |
1520 bool hasValue = false; | |
1521 bool fillingKey = true; // if filling key or value | |
1522 char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters) | |
1523 | |
1524 char buffer[512], orig; | |
1525 buffer[sizeof(buffer)-1] = '\xff'; | |
1526 | |
1527 for (int32_t terminated = 0, read; terminated == 0;) | |
1528 { | |
1529 read = stream->read(stream, buffer, sizeof(buffer)-1); | |
1530 DISTRHO_SAFE_ASSERT_INT_RETURN(read >= 0, read, false); | |
1531 | |
1532 if (read == 0) | |
1533 return true; | |
1534 | |
1535 for (int32_t i = 0; i < read; ++i) | |
1536 { | |
1537 // found terminator, stop here | |
1538 if (buffer[i] == '\xfe') | |
1539 { | |
1540 terminated = 1; | |
1541 break; | |
1542 } | |
1543 | |
1544 // store character at read position | |
1545 orig = buffer[read]; | |
1546 | |
1547 // place null character to create valid string | |
1548 buffer[read] = '\0'; | |
1549 | |
1550 // append to temporary vars | |
1551 if (fillingKey) | |
1552 { | |
1553 key += buffer + i; | |
1554 } | |
1555 else | |
1556 { | |
1557 value += buffer + i; | |
1558 hasValue = true; | |
1559 } | |
1560 | |
1561 // increase buffer offset by length of string | |
1562 i += std::strlen(buffer + i); | |
1563 | |
1564 // restore read character | |
1565 buffer[read] = orig; | |
1566 | |
1567 // if buffer offset points to null, we found the end of a string, lets check | |
1568 if (buffer[i] == '\0') | |
1569 { | |
1570 // special keys | |
1571 if (key == "__dpf_state_begin__") | |
1572 { | |
1573 DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n', | |
1574 queryingType, false); | |
1575 queryingType = 's'; | |
1576 key.clear(); | |
1577 value.clear(); | |
1578 hasValue = false; | |
1579 continue; | |
1580 } | |
1581 if (key == "__dpf_state_end__") | |
1582 { | |
1583 DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, false); | |
1584 queryingType = 'n'; | |
1585 key.clear(); | |
1586 value.clear(); | |
1587 hasValue = false; | |
1588 continue; | |
1589 } | |
1590 if (key == "__dpf_parameters_begin__") | |
1591 { | |
1592 DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n', | |
1593 queryingType, false); | |
1594 queryingType = 'p'; | |
1595 key.clear(); | |
1596 value.clear(); | |
1597 hasValue = false; | |
1598 continue; | |
1599 } | |
1600 if (key == "__dpf_parameters_end__") | |
1601 { | |
1602 DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, false); | |
1603 queryingType = 'x'; | |
1604 key.clear(); | |
1605 value.clear(); | |
1606 hasValue = false; | |
1607 continue; | |
1608 } | |
1609 | |
1610 // no special key, swap between reading real key and value | |
1611 fillingKey = !fillingKey; | |
1612 | |
1613 // if there is no value yet keep reading until we have one | |
1614 if (! hasValue) | |
1615 continue; | |
1616 | |
1617 if (key == "__dpf_program__") | |
1618 { | |
1619 DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, false); | |
1620 queryingType = 'n'; | |
1621 | |
1622 d_debug("found program '%s'", value.buffer()); | |
1623 | |
1624 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
1625 const int program = std::atoi(value.buffer()); | |
1626 DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0); | |
1627 | |
1628 fCurrentProgram = static_cast<uint32_t>(program); | |
1629 fPlugin.loadProgram(fCurrentProgram); | |
1630 | |
1631 #if DISTRHO_PLUGIN_HAS_UI | |
1632 if (ui != nullptr) | |
1633 ui->setProgramFromPlugin(fCurrentProgram); | |
1634 #endif | |
1635 #endif | |
1636 } | |
1637 else if (queryingType == 's') | |
1638 { | |
1639 d_debug("found state '%s' '%s'", key.buffer(), value.buffer()); | |
1640 | |
1641 #if DISTRHO_PLUGIN_WANT_STATE | |
1642 if (fPlugin.wantStateKey(key)) | |
1643 { | |
1644 fStateMap[key] = value; | |
1645 fPlugin.setState(key, value); | |
1646 | |
1647 #if DISTRHO_PLUGIN_HAS_UI | |
1648 if (ui != nullptr) | |
1649 ui->setStateFromPlugin(key, value); | |
1650 #endif | |
1651 } | |
1652 #endif | |
1653 } | |
1654 else if (queryingType == 'p') | |
1655 { | |
1656 d_debug("found parameter '%s' '%s'", key.buffer(), value.buffer()); | |
1657 float fvalue; | |
1658 | |
1659 // find parameter with this symbol, and set its value | |
1660 for (uint32_t j=0; j<fCachedParameters.numParams; ++j) | |
1661 { | |
1662 if (fPlugin.isParameterOutputOrTrigger(j)) | |
1663 continue; | |
1664 if (fPlugin.getParameterSymbol(j) != key) | |
1665 continue; | |
1666 | |
1667 if (fPlugin.getParameterHints(j) & kParameterIsInteger) | |
1668 fvalue = std::atoi(value.buffer()); | |
1669 else | |
1670 fvalue = std::atof(value.buffer()); | |
1671 | |
1672 fCachedParameters.values[j] = fvalue; | |
1673 #if DISTRHO_PLUGIN_HAS_UI | |
1674 if (ui != nullptr) | |
1675 { | |
1676 // UI parameter updates are handled outside the read loop (after host param restart) | |
1677 fCachedParameters.changed[j] = true; | |
1678 } | |
1679 #endif | |
1680 fPlugin.setParameterValue(j, fvalue); | |
1681 break; | |
1682 } | |
1683 } | |
1684 | |
1685 key.clear(); | |
1686 value.clear(); | |
1687 hasValue = false; | |
1688 } | |
1689 } | |
1690 } | |
1691 | |
1692 if (fHostExtensions.params != nullptr) | |
1693 fHostExtensions.params->rescan(fHost, CLAP_PARAM_RESCAN_VALUES|CLAP_PARAM_RESCAN_TEXT); | |
1694 | |
1695 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1696 checkForLatencyChanges(fPlugin.isActive(), true); | |
1697 reportLatencyChangeIfNeeded(); | |
1698 #endif | |
1699 | |
1700 #if DISTRHO_PLUGIN_HAS_UI | |
1701 if (ui != nullptr) | |
1702 { | |
1703 for (uint32_t i=0; i<fCachedParameters.numParams; ++i) | |
1704 { | |
1705 if (fPlugin.isParameterOutputOrTrigger(i)) | |
1706 continue; | |
1707 fCachedParameters.changed[i] = false; | |
1708 ui->setParameterValueFromPlugin(i, fCachedParameters.values[i]); | |
1709 } | |
1710 } | |
1711 #endif | |
1712 | |
1713 return true; | |
1714 } | |
1715 | |
1716 // ---------------------------------------------------------------------------------------------------------------- | |
1717 // gui | |
1718 | |
1719 #if DISTRHO_PLUGIN_HAS_UI | |
1720 bool createUI(const bool isFloating) | |
1721 { | |
1722 const clap_host_gui_t* const hostGui = getHostExtension<clap_host_gui_t>(CLAP_EXT_GUI); | |
1723 DISTRHO_SAFE_ASSERT_RETURN(hostGui != nullptr, false); | |
1724 | |
1725 #if DPF_CLAP_USING_HOST_TIMER | |
1726 const clap_host_timer_support_t* const hostTimer = getHostExtension<clap_host_timer_support_t>(CLAP_EXT_TIMER_SUPPORT); | |
1727 DISTRHO_SAFE_ASSERT_RETURN(hostTimer != nullptr, false); | |
1728 #endif | |
1729 | |
1730 fUI = new ClapUI(fPlugin, this, fHost, hostGui, | |
1731 #if DPF_CLAP_USING_HOST_TIMER | |
1732 hostTimer, | |
1733 #endif | |
1734 isFloating); | |
1735 return true; | |
1736 } | |
1737 | |
1738 void destroyUI() | |
1739 { | |
1740 fUI = nullptr; | |
1741 } | |
1742 | |
1743 ClapUI* getUI() const noexcept | |
1744 { | |
1745 return fUI.get(); | |
1746 } | |
1747 #endif | |
1748 | |
1749 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE | |
1750 void setStateFromUI(const char* const key, const char* const value) override | |
1751 { | |
1752 fPlugin.setState(key, value); | |
1753 | |
1754 // check if we want to save this key | |
1755 if (! fPlugin.wantStateKey(key)) | |
1756 return; | |
1757 | |
1758 // check if key already exists | |
1759 for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |
1760 { | |
1761 const String& dkey(it->first); | |
1762 | |
1763 if (dkey == key) | |
1764 { | |
1765 it->second = value; | |
1766 return; | |
1767 } | |
1768 } | |
1769 | |
1770 d_stderr("Failed to find plugin state with key \"%s\"", key); | |
1771 } | |
1772 #endif | |
1773 | |
1774 // ---------------------------------------------------------------------------------------------------------------- | |
1775 | |
1776 private: | |
1777 // Plugin and UI | |
1778 PluginExporter fPlugin; | |
1779 #if DISTRHO_PLUGIN_HAS_UI | |
1780 ScopedPointer<ClapUI> fUI; | |
1781 #endif | |
1782 | |
1783 // CLAP stuff | |
1784 const clap_host_t* const fHost; | |
1785 const clap_output_events_t* fOutputEvents; | |
1786 | |
1787 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 | |
1788 const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS]; | |
1789 #endif | |
1790 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1791 float* fAudioOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |
1792 #endif | |
1793 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1794 bool fUsingCV; | |
1795 #endif | |
1796 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1797 bool fLatencyChanged; | |
1798 uint32_t fLastKnownLatency; | |
1799 #endif | |
1800 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
1801 uint32_t fMidiEventCount; | |
1802 MidiEvent fMidiEvents[kMaxMidiEvents]; | |
1803 #if DISTRHO_PLUGIN_HAS_UI | |
1804 RingBufferControl<SmallStackBuffer> fNotesRingBuffer; | |
1805 #endif | |
1806 #endif | |
1807 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
1808 TimePosition fTimePosition; | |
1809 #endif | |
1810 | |
1811 struct HostExtensions { | |
1812 const clap_host_t* const host; | |
1813 const clap_host_params_t* params; | |
1814 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1815 const clap_host_latency_t* latency; | |
1816 const clap_host_thread_check_t* threadCheck; | |
1817 #endif | |
1818 | |
1819 HostExtensions(const clap_host_t* const host) | |
1820 : host(host), | |
1821 params(nullptr) | |
1822 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1823 , latency(nullptr) | |
1824 , threadCheck(nullptr) | |
1825 #endif | |
1826 {} | |
1827 | |
1828 bool init() | |
1829 { | |
1830 params = static_cast<const clap_host_params_t*>(host->get_extension(host, CLAP_EXT_PARAMS)); | |
1831 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1832 DISTRHO_SAFE_ASSERT_RETURN(host->request_restart != nullptr, false); | |
1833 DISTRHO_SAFE_ASSERT_RETURN(host->request_callback != nullptr, false); | |
1834 latency = static_cast<const clap_host_latency_t*>(host->get_extension(host, CLAP_EXT_LATENCY)); | |
1835 threadCheck = static_cast<const clap_host_thread_check_t*>(host->get_extension(host, CLAP_EXT_THREAD_CHECK)); | |
1836 #endif | |
1837 return true; | |
1838 } | |
1839 } fHostExtensions; | |
1840 | |
1841 // ---------------------------------------------------------------------------------------------------------------- | |
1842 // helper functions for dealing with buses | |
1843 | |
1844 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1845 struct BusInfo { | |
1846 char name[CLAP_NAME_SIZE]; | |
1847 uint32_t numChannels; | |
1848 bool hasPair; | |
1849 bool isCV; | |
1850 bool isMain; | |
1851 uint32_t groupId; | |
1852 }; | |
1853 std::vector<BusInfo> fAudioInputBuses, fAudioOutputBuses; | |
1854 | |
1855 template<bool isInput> | |
1856 void fillInBusInfoDetails() | |
1857 { | |
1858 constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; | |
1859 std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses); | |
1860 | |
1861 enum { | |
1862 kPortTypeNull, | |
1863 kPortTypeAudio, | |
1864 kPortTypeSidechain, | |
1865 kPortTypeCV, | |
1866 kPortTypeGroup | |
1867 } lastSeenPortType = kPortTypeNull; | |
1868 uint32_t lastSeenGroupId = kPortGroupNone; | |
1869 uint32_t nonGroupAudioId = 0; | |
1870 uint32_t nonGroupSidechainId = 0x20000000; | |
1871 | |
1872 for (uint32_t i=0; i<numPorts; ++i) | |
1873 { | |
1874 const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i)); | |
1875 | |
1876 if (port.groupId != kPortGroupNone) | |
1877 { | |
1878 if (lastSeenPortType != kPortTypeGroup || lastSeenGroupId != port.groupId) | |
1879 { | |
1880 lastSeenPortType = kPortTypeGroup; | |
1881 lastSeenGroupId = port.groupId; | |
1882 | |
1883 BusInfo busInfo = { | |
1884 {}, 1, false, false, | |
1885 // if this is the first port, set it as main | |
1886 busInfos.empty(), | |
1887 // user given group id with extra safety offset | |
1888 port.groupId + 0x80000000 | |
1889 }; | |
1890 | |
1891 const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId)); | |
1892 | |
1893 switch (port.groupId) | |
1894 { | |
1895 case kPortGroupStereo: | |
1896 case kPortGroupMono: | |
1897 if (busInfo.isMain) | |
1898 { | |
1899 d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE); | |
1900 break; | |
1901 } | |
1902 // fall-through | |
1903 default: | |
1904 if (group.name.isNotEmpty()) | |
1905 d_strncpy(busInfo.name, group.name, CLAP_NAME_SIZE); | |
1906 else | |
1907 d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); | |
1908 break; | |
1909 } | |
1910 | |
1911 busInfos.push_back(busInfo); | |
1912 } | |
1913 else | |
1914 { | |
1915 ++busInfos.back().numChannels; | |
1916 } | |
1917 } | |
1918 else if (port.hints & kAudioPortIsCV) | |
1919 { | |
1920 // TODO | |
1921 lastSeenPortType = kPortTypeCV; | |
1922 lastSeenGroupId = kPortGroupNone; | |
1923 fUsingCV = true; | |
1924 } | |
1925 else if (port.hints & kAudioPortIsSidechain) | |
1926 { | |
1927 if (lastSeenPortType != kPortTypeSidechain) | |
1928 { | |
1929 lastSeenPortType = kPortTypeSidechain; | |
1930 lastSeenGroupId = kPortGroupNone; | |
1931 | |
1932 BusInfo busInfo = { | |
1933 {}, 1, false, false, | |
1934 // not main | |
1935 false, | |
1936 // give unique id | |
1937 nonGroupSidechainId++ | |
1938 }; | |
1939 | |
1940 d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); | |
1941 | |
1942 busInfos.push_back(busInfo); | |
1943 } | |
1944 else | |
1945 { | |
1946 ++busInfos.back().numChannels; | |
1947 } | |
1948 } | |
1949 else | |
1950 { | |
1951 if (lastSeenPortType != kPortTypeAudio) | |
1952 { | |
1953 lastSeenPortType = kPortTypeAudio; | |
1954 lastSeenGroupId = kPortGroupNone; | |
1955 | |
1956 BusInfo busInfo = { | |
1957 {}, 1, false, false, | |
1958 // if this is the first port, set it as main | |
1959 busInfos.empty(), | |
1960 // give unique id | |
1961 nonGroupAudioId++ | |
1962 }; | |
1963 | |
1964 if (busInfo.isMain) | |
1965 { | |
1966 d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE); | |
1967 } | |
1968 else | |
1969 { | |
1970 d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); | |
1971 } | |
1972 | |
1973 busInfos.push_back(busInfo); | |
1974 } | |
1975 else | |
1976 { | |
1977 ++busInfos.back().numChannels; | |
1978 } | |
1979 } | |
1980 } | |
1981 } | |
1982 #endif | |
1983 | |
1984 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
1985 void fillInBusInfoPairs() | |
1986 { | |
1987 const size_t numChannels = std::min(fAudioInputBuses.size(), fAudioOutputBuses.size()); | |
1988 | |
1989 for (size_t i=0; i<numChannels; ++i) | |
1990 { | |
1991 if (fAudioInputBuses[i].groupId != fAudioOutputBuses[i].groupId) | |
1992 break; | |
1993 if (fAudioInputBuses[i].numChannels != fAudioOutputBuses[i].numChannels) | |
1994 break; | |
1995 if (fAudioInputBuses[i].isMain != fAudioOutputBuses[i].isMain) | |
1996 break; | |
1997 | |
1998 fAudioInputBuses[i].hasPair = fAudioOutputBuses[i].hasPair = true; | |
1999 } | |
2000 } | |
2001 #endif | |
2002 | |
2003 // ---------------------------------------------------------------------------------------------------------------- | |
2004 // DPF callbacks | |
2005 | |
2006 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
2007 bool writeMidi(const MidiEvent& midiEvent) | |
2008 { | |
2009 DISTRHO_SAFE_ASSERT_RETURN(fOutputEvents != nullptr, false); | |
2010 | |
2011 if (midiEvent.size > 3) | |
2012 return true; | |
2013 | |
2014 const clap_event_midi clapEvent = { | |
2015 { sizeof(clap_event_midi), midiEvent.frame, 0, CLAP_EVENT_MIDI, CLAP_EVENT_IS_LIVE }, | |
2016 0, { midiEvent.data[0], | |
2017 static_cast<uint8_t>(midiEvent.size >= 2 ? midiEvent.data[1] : 0), | |
2018 static_cast<uint8_t>(midiEvent.size >= 3 ? midiEvent.data[2] : 0) } | |
2019 }; | |
2020 return fOutputEvents->try_push(fOutputEvents, &clapEvent.header); | |
2021 } | |
2022 | |
2023 static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) | |
2024 { | |
2025 return static_cast<PluginCLAP*>(ptr)->writeMidi(midiEvent); | |
2026 } | |
2027 #endif | |
2028 | |
2029 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
2030 bool requestParameterValueChange(uint32_t, float) | |
2031 { | |
2032 return true; | |
2033 } | |
2034 | |
2035 static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) | |
2036 { | |
2037 return static_cast<PluginCLAP*>(ptr)->requestParameterValueChange(index, value); | |
2038 } | |
2039 #endif | |
2040 | |
2041 #if DISTRHO_PLUGIN_WANT_STATE | |
2042 bool updateState(const char*, const char*) | |
2043 { | |
2044 return true; | |
2045 } | |
2046 | |
2047 static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) | |
2048 { | |
2049 return static_cast<PluginCLAP*>(ptr)->updateState(key, value); | |
2050 } | |
2051 #endif | |
2052 }; | |
2053 | |
2054 // -------------------------------------------------------------------------------------------------------------------- | |
2055 | |
2056 static ScopedPointer<PluginExporter> sPlugin; | |
2057 | |
2058 // -------------------------------------------------------------------------------------------------------------------- | |
2059 // plugin gui | |
2060 | |
2061 #if DISTRHO_PLUGIN_HAS_UI | |
2062 | |
2063 static const char* const kSupportedAPIs[] = { | |
2064 #if defined(DISTRHO_OS_WINDOWS) | |
2065 CLAP_WINDOW_API_WIN32, | |
2066 #elif defined(DISTRHO_OS_MAC) | |
2067 CLAP_WINDOW_API_COCOA, | |
2068 #else | |
2069 CLAP_WINDOW_API_X11, | |
2070 #endif | |
2071 }; | |
2072 | |
2073 // TODO DPF external UI | |
2074 static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool) | |
2075 { | |
2076 for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i) | |
2077 { | |
2078 if (std::strcmp(kSupportedAPIs[i], api) == 0) | |
2079 return true; | |
2080 } | |
2081 | |
2082 return false; | |
2083 } | |
2084 | |
2085 // TODO DPF external UI | |
2086 static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating) | |
2087 { | |
2088 *api = kSupportedAPIs[0]; | |
2089 *is_floating = false; | |
2090 return true; | |
2091 } | |
2092 | |
2093 static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating) | |
2094 { | |
2095 for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i) | |
2096 { | |
2097 if (std::strcmp(kSupportedAPIs[i], api) == 0) | |
2098 { | |
2099 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2100 return instance->createUI(is_floating); | |
2101 } | |
2102 } | |
2103 | |
2104 return false; | |
2105 } | |
2106 | |
2107 static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin) | |
2108 { | |
2109 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2110 instance->destroyUI(); | |
2111 } | |
2112 | |
2113 static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale) | |
2114 { | |
2115 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2116 ClapUI* const gui = instance->getUI(); | |
2117 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2118 #ifndef DISTRHO_OS_MAC | |
2119 return gui->setScaleFactor(scale); | |
2120 #else | |
2121 return true; | |
2122 // unused | |
2123 (void)scale; | |
2124 #endif | |
2125 } | |
2126 | |
2127 static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) | |
2128 { | |
2129 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2130 ClapUI* const gui = instance->getUI(); | |
2131 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2132 return gui->getSize(width, height); | |
2133 } | |
2134 | |
2135 static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin) | |
2136 { | |
2137 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2138 ClapUI* const gui = instance->getUI(); | |
2139 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2140 return gui->canResize(); | |
2141 } | |
2142 | |
2143 static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints) | |
2144 { | |
2145 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2146 ClapUI* const gui = instance->getUI(); | |
2147 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2148 return gui->getResizeHints(hints); | |
2149 } | |
2150 | |
2151 static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) | |
2152 { | |
2153 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2154 ClapUI* const gui = instance->getUI(); | |
2155 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2156 return gui->adjustSize(width, height); | |
2157 } | |
2158 | |
2159 static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height) | |
2160 { | |
2161 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2162 ClapUI* const gui = instance->getUI(); | |
2163 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2164 return gui->setSizeFromHost(width, height); | |
2165 } | |
2166 | |
2167 static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window) | |
2168 { | |
2169 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2170 ClapUI* const gui = instance->getUI(); | |
2171 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2172 return gui->setParent(window); | |
2173 } | |
2174 | |
2175 static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window) | |
2176 { | |
2177 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2178 ClapUI* const gui = instance->getUI(); | |
2179 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2180 return gui->setTransient(window); | |
2181 } | |
2182 | |
2183 static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title) | |
2184 { | |
2185 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2186 ClapUI* const gui = instance->getUI(); | |
2187 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,); | |
2188 return gui->suggestTitle(title); | |
2189 } | |
2190 | |
2191 static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin) | |
2192 { | |
2193 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2194 ClapUI* const gui = instance->getUI(); | |
2195 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2196 return gui->show(); | |
2197 } | |
2198 | |
2199 static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin) | |
2200 { | |
2201 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2202 ClapUI* const gui = instance->getUI(); | |
2203 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false); | |
2204 return gui->hide(); | |
2205 } | |
2206 | |
2207 static const clap_plugin_gui_t clap_plugin_gui = { | |
2208 clap_gui_is_api_supported, | |
2209 clap_gui_get_preferred_api, | |
2210 clap_gui_create, | |
2211 clap_gui_destroy, | |
2212 clap_gui_set_scale, | |
2213 clap_gui_get_size, | |
2214 clap_gui_can_resize, | |
2215 clap_gui_get_resize_hints, | |
2216 clap_gui_adjust_size, | |
2217 clap_gui_set_size, | |
2218 clap_gui_set_parent, | |
2219 clap_gui_set_transient, | |
2220 clap_gui_suggest_title, | |
2221 clap_gui_show, | |
2222 clap_gui_hide | |
2223 }; | |
2224 | |
2225 // -------------------------------------------------------------------------------------------------------------------- | |
2226 // plugin timer | |
2227 | |
2228 #if DPF_CLAP_USING_HOST_TIMER | |
2229 static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id) | |
2230 { | |
2231 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2232 ClapUI* const gui = instance->getUI(); | |
2233 DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,); | |
2234 return gui->idleCallback(); | |
2235 } | |
2236 | |
2237 static const clap_plugin_timer_support_t clap_timer = { | |
2238 clap_plugin_on_timer | |
2239 }; | |
2240 #endif | |
2241 | |
2242 #endif // DISTRHO_PLUGIN_HAS_UI | |
2243 | |
2244 // -------------------------------------------------------------------------------------------------------------------- | |
2245 // plugin audio ports | |
2246 | |
2247 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
2248 static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input) | |
2249 { | |
2250 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2251 return is_input ? instance->getAudioPortCount<true>() | |
2252 : instance->getAudioPortCount<false>(); | |
2253 } | |
2254 | |
2255 static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin, | |
2256 const uint32_t index, | |
2257 const bool is_input, | |
2258 clap_audio_port_info_t* const info) | |
2259 { | |
2260 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2261 return is_input ? instance->getAudioPortInfo<true>(index, info) | |
2262 : instance->getAudioPortInfo<false>(index, info); | |
2263 } | |
2264 | |
2265 static const clap_plugin_audio_ports_t clap_plugin_audio_ports = { | |
2266 clap_plugin_audio_ports_count, | |
2267 clap_plugin_audio_ports_get | |
2268 }; | |
2269 #endif | |
2270 | |
2271 // -------------------------------------------------------------------------------------------------------------------- | |
2272 // plugin note ports | |
2273 | |
2274 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 | |
2275 static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input) | |
2276 { | |
2277 return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0; | |
2278 } | |
2279 | |
2280 static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, | |
2281 const bool is_input, clap_note_port_info_t* const info) | |
2282 { | |
2283 if (is_input) | |
2284 { | |
2285 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
2286 info->id = 0; | |
2287 info->supported_dialects = CLAP_NOTE_DIALECT_MIDI; | |
2288 info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI; | |
2289 std::strcpy(info->name, "Event/MIDI Input"); | |
2290 return true; | |
2291 #endif | |
2292 } | |
2293 else | |
2294 { | |
2295 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
2296 info->id = 0; | |
2297 info->supported_dialects = CLAP_NOTE_DIALECT_MIDI; | |
2298 info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI; | |
2299 std::strcpy(info->name, "Event/MIDI Output"); | |
2300 return true; | |
2301 #endif | |
2302 } | |
2303 | |
2304 return false; | |
2305 } | |
2306 | |
2307 static const clap_plugin_note_ports_t clap_plugin_note_ports = { | |
2308 clap_plugin_note_ports_count, | |
2309 clap_plugin_note_ports_get | |
2310 }; | |
2311 #endif | |
2312 | |
2313 // -------------------------------------------------------------------------------------------------------------------- | |
2314 // plugin parameters | |
2315 | |
2316 static uint32_t CLAP_ABI clap_plugin_params_count(const clap_plugin_t* const plugin) | |
2317 { | |
2318 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2319 return instance->getParameterCount(); | |
2320 } | |
2321 | |
2322 static bool CLAP_ABI clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info) | |
2323 { | |
2324 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2325 return instance->getParameterInfo(index, info); | |
2326 } | |
2327 | |
2328 static bool CLAP_ABI clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value) | |
2329 { | |
2330 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2331 return instance->getParameterValue(param_id, value); | |
2332 } | |
2333 | |
2334 static bool CLAP_ABI clap_plugin_params_value_to_text(const clap_plugin_t* plugin, const clap_id param_id, const double value, char* const display, const uint32_t size) | |
2335 { | |
2336 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2337 return instance->getParameterStringForValue(param_id, value, display, size); | |
2338 } | |
2339 | |
2340 static bool CLAP_ABI clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value) | |
2341 { | |
2342 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2343 return instance->getParameterValueForString(param_id, display, value); | |
2344 } | |
2345 | |
2346 static void CLAP_ABI clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out) | |
2347 { | |
2348 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2349 return instance->flushParameters(in, out, 0); | |
2350 } | |
2351 | |
2352 static const clap_plugin_params_t clap_plugin_params = { | |
2353 clap_plugin_params_count, | |
2354 clap_plugin_params_get_info, | |
2355 clap_plugin_params_get_value, | |
2356 clap_plugin_params_value_to_text, | |
2357 clap_plugin_params_text_to_value, | |
2358 clap_plugin_params_flush | |
2359 }; | |
2360 | |
2361 #if DISTRHO_PLUGIN_WANT_LATENCY | |
2362 // -------------------------------------------------------------------------------------------------------------------- | |
2363 // plugin latency | |
2364 | |
2365 static uint32_t CLAP_ABI clap_plugin_latency_get(const clap_plugin_t* const plugin) | |
2366 { | |
2367 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2368 return instance->getLatency(); | |
2369 } | |
2370 | |
2371 static const clap_plugin_latency_t clap_plugin_latency = { | |
2372 clap_plugin_latency_get | |
2373 }; | |
2374 #endif | |
2375 | |
2376 // -------------------------------------------------------------------------------------------------------------------- | |
2377 // plugin state | |
2378 | |
2379 static bool CLAP_ABI clap_plugin_state_save(const clap_plugin_t* const plugin, const clap_ostream_t* const stream) | |
2380 { | |
2381 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2382 return instance->stateSave(stream); | |
2383 } | |
2384 | |
2385 static bool CLAP_ABI clap_plugin_state_load(const clap_plugin_t* const plugin, const clap_istream_t* const stream) | |
2386 { | |
2387 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2388 return instance->stateLoad(stream); | |
2389 } | |
2390 | |
2391 static const clap_plugin_state_t clap_plugin_state = { | |
2392 clap_plugin_state_save, | |
2393 clap_plugin_state_load | |
2394 }; | |
2395 | |
2396 // -------------------------------------------------------------------------------------------------------------------- | |
2397 // plugin | |
2398 | |
2399 static bool CLAP_ABI clap_plugin_init(const clap_plugin_t* const plugin) | |
2400 { | |
2401 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2402 return instance->init(); | |
2403 } | |
2404 | |
2405 static void CLAP_ABI clap_plugin_destroy(const clap_plugin_t* const plugin) | |
2406 { | |
2407 delete static_cast<PluginCLAP*>(plugin->plugin_data); | |
2408 std::free(const_cast<clap_plugin_t*>(plugin)); | |
2409 } | |
2410 | |
2411 static bool CLAP_ABI clap_plugin_activate(const clap_plugin_t* const plugin, | |
2412 const double sample_rate, | |
2413 uint32_t, | |
2414 const uint32_t max_frames_count) | |
2415 { | |
2416 d_nextBufferSize = max_frames_count; | |
2417 d_nextSampleRate = sample_rate; | |
2418 | |
2419 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2420 instance->activate(sample_rate, max_frames_count); | |
2421 return true; | |
2422 } | |
2423 | |
2424 static void CLAP_ABI clap_plugin_deactivate(const clap_plugin_t* const plugin) | |
2425 { | |
2426 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2427 instance->deactivate(); | |
2428 } | |
2429 | |
2430 static bool CLAP_ABI clap_plugin_start_processing(const clap_plugin_t*) | |
2431 { | |
2432 // nothing to do | |
2433 return true; | |
2434 } | |
2435 | |
2436 static void CLAP_ABI clap_plugin_stop_processing(const clap_plugin_t*) | |
2437 { | |
2438 // nothing to do | |
2439 } | |
2440 | |
2441 static void CLAP_ABI clap_plugin_reset(const clap_plugin_t*) | |
2442 { | |
2443 // nothing to do | |
2444 } | |
2445 | |
2446 static clap_process_status CLAP_ABI clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process) | |
2447 { | |
2448 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2449 return instance->process(process) ? CLAP_PROCESS_CONTINUE : CLAP_PROCESS_ERROR; | |
2450 } | |
2451 | |
2452 static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, const char* const id) | |
2453 { | |
2454 if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) | |
2455 return &clap_plugin_params; | |
2456 if (std::strcmp(id, CLAP_EXT_STATE) == 0) | |
2457 return &clap_plugin_state; | |
2458 #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | |
2459 if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) | |
2460 return &clap_plugin_audio_ports; | |
2461 #endif | |
2462 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 | |
2463 if (std::strcmp(id, CLAP_EXT_NOTE_PORTS) == 0) | |
2464 return &clap_plugin_note_ports; | |
2465 #endif | |
2466 #if DISTRHO_PLUGIN_WANT_LATENCY | |
2467 if (std::strcmp(id, CLAP_EXT_LATENCY) == 0) | |
2468 return &clap_plugin_latency; | |
2469 #endif | |
2470 #if DISTRHO_PLUGIN_HAS_UI | |
2471 if (std::strcmp(id, CLAP_EXT_GUI) == 0) | |
2472 return &clap_plugin_gui; | |
2473 #if DPF_CLAP_USING_HOST_TIMER | |
2474 if (std::strcmp(id, CLAP_EXT_TIMER_SUPPORT) == 0) | |
2475 return &clap_timer; | |
2476 #endif | |
2477 #endif | |
2478 return nullptr; | |
2479 } | |
2480 | |
2481 static void CLAP_ABI clap_plugin_on_main_thread(const clap_plugin_t* const plugin) | |
2482 { | |
2483 PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | |
2484 instance->onMainThread(); | |
2485 } | |
2486 | |
2487 // -------------------------------------------------------------------------------------------------------------------- | |
2488 // plugin factory | |
2489 | |
2490 static uint32_t CLAP_ABI clap_get_plugin_count(const clap_plugin_factory_t*) | |
2491 { | |
2492 return 1; | |
2493 } | |
2494 | |
2495 static const clap_plugin_descriptor_t* CLAP_ABI clap_get_plugin_descriptor(const clap_plugin_factory_t*, | |
2496 const uint32_t index) | |
2497 { | |
2498 DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr); | |
2499 | |
2500 static const char* features[] = { | |
2501 #ifdef DISTRHO_PLUGIN_CLAP_FEATURES | |
2502 DISTRHO_PLUGIN_CLAP_FEATURES, | |
2503 #elif DISTRHO_PLUGIN_IS_SYNTH | |
2504 "instrument", | |
2505 #endif | |
2506 nullptr | |
2507 }; | |
2508 | |
2509 static const clap_plugin_descriptor_t descriptor = { | |
2510 CLAP_VERSION, | |
2511 DISTRHO_PLUGIN_CLAP_ID, | |
2512 sPlugin->getName(), | |
2513 sPlugin->getMaker(), | |
2514 // TODO url | |
2515 "", | |
2516 // TODO manual url | |
2517 "", | |
2518 // TODO support url | |
2519 "", | |
2520 // TODO version string | |
2521 "", | |
2522 sPlugin->getDescription(), | |
2523 features | |
2524 }; | |
2525 | |
2526 return &descriptor; | |
2527 } | |
2528 | |
2529 static const clap_plugin_t* CLAP_ABI clap_create_plugin(const clap_plugin_factory_t* const factory, | |
2530 const clap_host_t* const host, | |
2531 const char*) | |
2532 { | |
2533 clap_plugin_t* const pluginptr = static_cast<clap_plugin_t*>(std::malloc(sizeof(clap_plugin_t))); | |
2534 DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr); | |
2535 | |
2536 // default early values | |
2537 if (d_nextBufferSize == 0) | |
2538 d_nextBufferSize = 1024; | |
2539 if (d_nextSampleRate <= 0.0) | |
2540 d_nextSampleRate = 44100.0; | |
2541 | |
2542 d_nextCanRequestParameterValueChanges = true; | |
2543 | |
2544 const clap_plugin_t plugin = { | |
2545 clap_get_plugin_descriptor(factory, 0), | |
2546 new PluginCLAP(host), | |
2547 clap_plugin_init, | |
2548 clap_plugin_destroy, | |
2549 clap_plugin_activate, | |
2550 clap_plugin_deactivate, | |
2551 clap_plugin_start_processing, | |
2552 clap_plugin_stop_processing, | |
2553 clap_plugin_reset, | |
2554 clap_plugin_process, | |
2555 clap_plugin_get_extension, | |
2556 clap_plugin_on_main_thread | |
2557 }; | |
2558 | |
2559 std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t)); | |
2560 return pluginptr; | |
2561 } | |
2562 | |
2563 static const clap_plugin_factory_t clap_plugin_factory = { | |
2564 clap_get_plugin_count, | |
2565 clap_get_plugin_descriptor, | |
2566 clap_create_plugin | |
2567 }; | |
2568 | |
2569 // -------------------------------------------------------------------------------------------------------------------- | |
2570 // plugin entry | |
2571 | |
2572 static bool CLAP_ABI clap_plugin_entry_init(const char* const plugin_path) | |
2573 { | |
2574 static String bundlePath; | |
2575 bundlePath = plugin_path; | |
2576 d_nextBundlePath = bundlePath.buffer(); | |
2577 | |
2578 // init dummy plugin | |
2579 if (sPlugin == nullptr) | |
2580 { | |
2581 // set valid but dummy values | |
2582 d_nextBufferSize = 512; | |
2583 d_nextSampleRate = 44100.0; | |
2584 d_nextPluginIsDummy = true; | |
2585 d_nextCanRequestParameterValueChanges = true; | |
2586 | |
2587 // Create dummy plugin to get data from | |
2588 sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr); | |
2589 | |
2590 // unset | |
2591 d_nextBufferSize = 0; | |
2592 d_nextSampleRate = 0.0; | |
2593 d_nextPluginIsDummy = false; | |
2594 d_nextCanRequestParameterValueChanges = false; | |
2595 } | |
2596 | |
2597 return true; | |
2598 } | |
2599 | |
2600 static void CLAP_ABI clap_plugin_entry_deinit(void) | |
2601 { | |
2602 sPlugin = nullptr; | |
2603 } | |
2604 | |
2605 static const void* CLAP_ABI clap_plugin_entry_get_factory(const char* const factory_id) | |
2606 { | |
2607 if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0) | |
2608 return &clap_plugin_factory; | |
2609 return nullptr; | |
2610 } | |
2611 | |
2612 static const clap_plugin_entry_t clap_plugin_entry = { | |
2613 CLAP_VERSION, | |
2614 clap_plugin_entry_init, | |
2615 clap_plugin_entry_deinit, | |
2616 clap_plugin_entry_get_factory | |
2617 }; | |
2618 | |
2619 // -------------------------------------------------------------------------------------------------------------------- | |
2620 | |
2621 END_NAMESPACE_DISTRHO | |
2622 | |
2623 // -------------------------------------------------------------------------------------------------------------------- | |
2624 | |
2625 DISTRHO_PLUGIN_EXPORT | |
2626 const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry; | |
2627 | |
2628 // -------------------------------------------------------------------------------------------------------------------- |