Collection of DPF-based plugins for packaging
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2629 lines
85KB

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