DISTRHO Plugin Framework
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.

2631 lines
85KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2022 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. fPluinEventQueue(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 fPluinEventQueue;
  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. 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. fPluinEventQueue->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. bool fileRequest(const char*)
  598. {
  599. return true;
  600. }
  601. static bool fileRequestCallback(void* const ptr, const char* const key)
  602. {
  603. return static_cast<ClapUI*>(ptr)->fileRequest(key);
  604. }
  605. };
  606. // --------------------------------------------------------------------------------------------------------------------
  607. #endif // DISTRHO_PLUGIN_HAS_UI
  608. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  609. static constexpr const writeMidiFunc writeMidiCallback = nullptr;
  610. #endif
  611. #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  612. static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
  613. #endif
  614. #if ! DISTRHO_PLUGIN_WANT_STATE
  615. static constexpr const updateStateValueFunc updateStateValueCallback = nullptr;
  616. #endif
  617. // --------------------------------------------------------------------------------------------------------------------
  618. /**
  619. * CLAP plugin class.
  620. */
  621. class PluginCLAP : ClapEventQueue
  622. {
  623. public:
  624. PluginCLAP(const clap_host_t* const host)
  625. : fPlugin(this,
  626. writeMidiCallback,
  627. requestParameterValueChangeCallback,
  628. updateStateValueCallback),
  629. fHost(host),
  630. fOutputEvents(nullptr),
  631. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  632. fUsingCV(false),
  633. #endif
  634. #if DISTRHO_PLUGIN_WANT_LATENCY
  635. fLatencyChanged(false),
  636. fLastKnownLatency(0),
  637. #endif
  638. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  639. fMidiEventCount(0),
  640. #endif
  641. fHostExtensions(host)
  642. {
  643. fCachedParameters.setup(fPlugin.getParameterCount());
  644. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
  645. fNotesRingBuffer.setRingBuffer(&fNotesBuffer, true);
  646. #endif
  647. #if DISTRHO_PLUGIN_WANT_STATE
  648. for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
  649. {
  650. const String& dkey(fPlugin.getStateKey(i));
  651. fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
  652. }
  653. #endif
  654. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  655. fillInBusInfoDetails<true>();
  656. #endif
  657. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  658. fillInBusInfoDetails<false>();
  659. #endif
  660. #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  661. fillInBusInfoPairs();
  662. #endif
  663. }
  664. // ----------------------------------------------------------------------------------------------------------------
  665. // core
  666. template <class T>
  667. const T* getHostExtension(const char* const extensionId) const
  668. {
  669. return static_cast<const T*>(fHost->get_extension(fHost, extensionId));
  670. }
  671. bool init()
  672. {
  673. if (!clap_version_is_compatible(fHost->clap_version))
  674. return false;
  675. return fHostExtensions.init();
  676. }
  677. void activate(const double sampleRate, const uint32_t maxFramesCount)
  678. {
  679. fPlugin.setSampleRate(sampleRate, true);
  680. fPlugin.setBufferSize(maxFramesCount, true);
  681. fPlugin.activate();
  682. }
  683. void deactivate()
  684. {
  685. fPlugin.deactivate();
  686. #if DISTRHO_PLUGIN_WANT_LATENCY
  687. checkForLatencyChanges(false, true);
  688. reportLatencyChangeIfNeeded();
  689. #endif
  690. }
  691. bool process(const clap_process_t* const process)
  692. {
  693. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  694. fMidiEventCount = 0;
  695. #endif
  696. #if DISTRHO_PLUGIN_HAS_UI
  697. if (const clap_output_events_t* const outputEvents = process->out_events)
  698. {
  699. const RecursiveMutexTryLocker crmtl(fEventQueue.lock);
  700. if (crmtl.wasLocked())
  701. {
  702. // reuse the same struct for gesture and parameters, they are compatible up to where it matters
  703. clap_event_param_value_t clapEvent = {
  704. { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE },
  705. 0, nullptr, 0, 0, 0, 0, 0.0
  706. };
  707. for (uint32_t i=0; i<fEventQueue.used; ++i)
  708. {
  709. const Event& event(fEventQueue.events[i]);
  710. switch (event.type)
  711. {
  712. case kEventGestureBegin:
  713. clapEvent.header.size = sizeof(clap_event_param_gesture_t);
  714. clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_BEGIN;
  715. clapEvent.param_id = event.index;
  716. break;
  717. case kEventGestureEnd:
  718. clapEvent.header.size = sizeof(clap_event_param_gesture_t);
  719. clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_END;
  720. clapEvent.param_id = event.index;
  721. break;
  722. case kEventParamSet:
  723. clapEvent.header.size = sizeof(clap_event_param_value_t);
  724. clapEvent.header.type = CLAP_EVENT_PARAM_VALUE;
  725. clapEvent.param_id = event.index;
  726. clapEvent.value = event.value;
  727. fPlugin.setParameterValue(event.index, event.value);
  728. break;
  729. default:
  730. continue;
  731. }
  732. outputEvents->try_push(outputEvents, &clapEvent.header);
  733. }
  734. fEventQueue.used = 0;
  735. }
  736. }
  737. #endif
  738. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  739. if (const clap_event_transport_t* const transport = process->transport)
  740. {
  741. fTimePosition.playing = (transport->flags & CLAP_TRANSPORT_IS_PLAYING) != 0 &&
  742. (transport->flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL) == 0;
  743. fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0;
  744. if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO)
  745. fTimePosition.bbt.beatsPerMinute = transport->tempo;
  746. else
  747. fTimePosition.bbt.beatsPerMinute = 120.0;
  748. // ticksPerBeat is not possible with CLAP
  749. fTimePosition.bbt.ticksPerBeat = 1920.0;
  750. if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE))
  751. {
  752. if (transport->song_pos_beats >= 0)
  753. {
  754. const int64_t clapPos = std::abs(transport->song_pos_beats);
  755. const int64_t clapBeats = clapPos >> 31;
  756. const double clapRest = static_cast<double>(clapPos & 0x7fffffff) / CLAP_BEATTIME_FACTOR;
  757. fTimePosition.bbt.bar = static_cast<int32_t>(clapBeats) / transport->tsig_num + 1;
  758. fTimePosition.bbt.beat = static_cast<int32_t>(clapBeats % transport->tsig_num) + 1;
  759. fTimePosition.bbt.tick = clapRest * fTimePosition.bbt.ticksPerBeat;
  760. }
  761. else
  762. {
  763. fTimePosition.bbt.bar = 1;
  764. fTimePosition.bbt.beat = 1;
  765. fTimePosition.bbt.tick = 0.0;
  766. }
  767. fTimePosition.bbt.valid = true;
  768. fTimePosition.bbt.beatsPerBar = transport->tsig_num;
  769. fTimePosition.bbt.beatType = transport->tsig_denom;
  770. }
  771. else
  772. {
  773. fTimePosition.bbt.valid = false;
  774. fTimePosition.bbt.bar = 1;
  775. fTimePosition.bbt.beat = 1;
  776. fTimePosition.bbt.tick = 0.0;
  777. fTimePosition.bbt.beatsPerBar = 4.0f;
  778. fTimePosition.bbt.beatType = 4.0f;
  779. }
  780. fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
  781. fTimePosition.bbt.beatsPerBar*
  782. (fTimePosition.bbt.bar-1);
  783. }
  784. else
  785. {
  786. fTimePosition.playing = false;
  787. fTimePosition.frame = 0;
  788. fTimePosition.bbt.valid = false;
  789. fTimePosition.bbt.beatsPerMinute = 120.0;
  790. fTimePosition.bbt.bar = 1;
  791. fTimePosition.bbt.beat = 1;
  792. fTimePosition.bbt.tick = 0.0;
  793. fTimePosition.bbt.beatsPerBar = 4.f;
  794. fTimePosition.bbt.beatType = 4.f;
  795. fTimePosition.bbt.barStartTick = 0;
  796. }
  797. fPlugin.setTimePosition(fTimePosition);
  798. #endif
  799. if (const clap_input_events_t* const inputEvents = process->in_events)
  800. {
  801. if (const uint32_t len = inputEvents->size(inputEvents))
  802. {
  803. for (uint32_t i=0; i<len; ++i)
  804. {
  805. const clap_event_header_t* const event = inputEvents->get(inputEvents, i);
  806. switch (event->type)
  807. {
  808. case CLAP_EVENT_NOTE_ON:
  809. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  810. // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway
  811. addNoteEvent(static_cast<const clap_event_note_t*>(static_cast<const void*>(event)), true);
  812. #endif
  813. break;
  814. case CLAP_EVENT_NOTE_OFF:
  815. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  816. // BUG: even though we only report CLAP_NOTE_DIALECT_MIDI as supported, Bitwig sends us this anyway
  817. addNoteEvent(static_cast<const clap_event_note_t*>(static_cast<const void*>(event)), false);
  818. #endif
  819. break;
  820. case CLAP_EVENT_NOTE_CHOKE:
  821. case CLAP_EVENT_NOTE_END:
  822. case CLAP_EVENT_NOTE_EXPRESSION:
  823. break;
  824. case CLAP_EVENT_PARAM_VALUE:
  825. DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
  826. event->size, sizeof(clap_event_param_value));
  827. setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
  828. break;
  829. case CLAP_EVENT_PARAM_MOD:
  830. case CLAP_EVENT_PARAM_GESTURE_BEGIN:
  831. case CLAP_EVENT_PARAM_GESTURE_END:
  832. case CLAP_EVENT_TRANSPORT:
  833. break;
  834. case CLAP_EVENT_MIDI:
  835. DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t),
  836. event->size, sizeof(clap_event_midi_t));
  837. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  838. addMidiEvent(static_cast<const clap_event_midi_t*>(static_cast<const void*>(event)));
  839. #endif
  840. break;
  841. case CLAP_EVENT_MIDI_SYSEX:
  842. case CLAP_EVENT_MIDI2:
  843. break;
  844. }
  845. }
  846. }
  847. }
  848. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
  849. if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
  850. {
  851. uint8_t midiData[3];
  852. const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0;
  853. while (fNotesRingBuffer.isDataAvailableForReading())
  854. {
  855. if (! fNotesRingBuffer.readCustomData(midiData, 3))
  856. break;
  857. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  858. midiEvent.frame = frame;
  859. midiEvent.size = 3;
  860. std::memcpy(midiEvent.data, midiData, 3);
  861. if (fMidiEventCount == kMaxMidiEvents)
  862. break;
  863. }
  864. }
  865. #endif
  866. if (const uint32_t frames = process->frames_count)
  867. {
  868. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  869. const float** const audioInputs = fAudioInputs;
  870. uint32_t in=0;
  871. for (uint32_t i=0; i<process->audio_inputs_count; ++i)
  872. {
  873. const clap_audio_buffer_t& inputs(process->audio_inputs[i]);
  874. DISTRHO_SAFE_ASSERT_CONTINUE(inputs.channel_count != 0);
  875. for (uint32_t j=0; j<inputs.channel_count; ++j, ++in)
  876. audioInputs[in] = const_cast<const float*>(inputs.data32[j]);
  877. }
  878. if (fUsingCV)
  879. {
  880. for (; in<DISTRHO_PLUGIN_NUM_INPUTS; ++in)
  881. audioInputs[in] = nullptr;
  882. }
  883. else
  884. {
  885. DISTRHO_SAFE_ASSERT_UINT2_RETURN(in == DISTRHO_PLUGIN_NUM_INPUTS,
  886. in, process->audio_inputs_count, false);
  887. }
  888. #else
  889. constexpr const float** const audioInputs = nullptr;
  890. #endif
  891. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  892. float** const audioOutputs = fAudioOutputs;
  893. uint32_t out=0;
  894. for (uint32_t i=0; i<process->audio_outputs_count; ++i)
  895. {
  896. const clap_audio_buffer_t& outputs(process->audio_outputs[i]);
  897. DISTRHO_SAFE_ASSERT_CONTINUE(outputs.channel_count != 0);
  898. for (uint32_t j=0; j<outputs.channel_count; ++j, ++out)
  899. audioOutputs[out] = outputs.data32[j];
  900. }
  901. if (fUsingCV)
  902. {
  903. for (; out<DISTRHO_PLUGIN_NUM_OUTPUTS; ++out)
  904. audioOutputs[out] = nullptr;
  905. }
  906. else
  907. {
  908. DISTRHO_SAFE_ASSERT_UINT2_RETURN(out == DISTRHO_PLUGIN_NUM_OUTPUTS,
  909. out, DISTRHO_PLUGIN_NUM_OUTPUTS, false);
  910. }
  911. #else
  912. constexpr float** const audioOutputs = nullptr;
  913. #endif
  914. fOutputEvents = process->out_events;
  915. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  916. fPlugin.run(audioInputs, audioOutputs, frames, fMidiEvents, fMidiEventCount);
  917. #else
  918. fPlugin.run(audioInputs, audioOutputs, frames);
  919. #endif
  920. flushParameters(nullptr, process->out_events, frames - 1);
  921. fOutputEvents = nullptr;
  922. }
  923. #if DISTRHO_PLUGIN_WANT_LATENCY
  924. checkForLatencyChanges(true, false);
  925. #endif
  926. return true;
  927. }
  928. void onMainThread()
  929. {
  930. #if DISTRHO_PLUGIN_WANT_LATENCY
  931. reportLatencyChangeIfNeeded();
  932. #endif
  933. }
  934. // ----------------------------------------------------------------------------------------------------------------
  935. // parameters
  936. uint32_t getParameterCount() const
  937. {
  938. return fPlugin.getParameterCount();
  939. }
  940. bool getParameterInfo(const uint32_t index, clap_param_info_t* const info) const
  941. {
  942. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  943. if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass)
  944. {
  945. info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE;
  946. std::strcpy(info->name, "Bypass");
  947. std::strcpy(info->module, "dpf_bypass");
  948. }
  949. else
  950. {
  951. const uint32_t hints = fPlugin.getParameterHints(index);
  952. const uint32_t groupId = fPlugin.getParameterGroupId(index);
  953. info->flags = 0;
  954. if (hints & kParameterIsOutput)
  955. info->flags |= CLAP_PARAM_IS_READONLY;
  956. else if (hints & kParameterIsAutomatable)
  957. info->flags |= CLAP_PARAM_IS_AUTOMATABLE;
  958. if (hints & (kParameterIsBoolean|kParameterIsInteger))
  959. info->flags |= CLAP_PARAM_IS_STEPPED;
  960. d_strncpy(info->name, fPlugin.getParameterName(index), CLAP_NAME_SIZE);
  961. uint wrtn;
  962. if (groupId != kPortGroupNone)
  963. {
  964. const PortGroupWithId& portGroup(fPlugin.getPortGroupById(groupId));
  965. strncpy(info->module, portGroup.symbol, CLAP_PATH_SIZE / 2);
  966. info->module[CLAP_PATH_SIZE / 2] = '\0';
  967. wrtn = std::strlen(info->module);
  968. info->module[wrtn++] = '/';
  969. }
  970. else
  971. {
  972. wrtn = 0;
  973. }
  974. d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn);
  975. }
  976. info->id = index;
  977. info->cookie = nullptr;
  978. info->min_value = ranges.min;
  979. info->max_value = ranges.max;
  980. info->default_value = ranges.def;
  981. return true;
  982. }
  983. bool getParameterValue(const clap_id param_id, double* const value) const
  984. {
  985. *value = fPlugin.getParameterValue(param_id);
  986. return true;
  987. }
  988. bool getParameterStringForValue(const clap_id param_id, double value, char* const display, const uint32_t size) const
  989. {
  990. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
  991. const ParameterRanges& ranges(fPlugin.getParameterRanges(param_id));
  992. const uint32_t hints = fPlugin.getParameterHints(param_id);
  993. if (hints & kParameterIsBoolean)
  994. {
  995. const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
  996. value = value > midRange ? ranges.max : ranges.min;
  997. }
  998. else if (hints & kParameterIsInteger)
  999. {
  1000. value = std::round(value);
  1001. }
  1002. for (uint32_t i=0; i < enumValues.count; ++i)
  1003. {
  1004. if (d_isEqual(static_cast<double>(enumValues.values[i].value), value))
  1005. {
  1006. d_strncpy(display, enumValues.values[i].label, size);
  1007. return true;
  1008. }
  1009. }
  1010. if (hints & kParameterIsInteger)
  1011. snprintf_i32(display, value, size);
  1012. else
  1013. snprintf_f32(display, value, size);
  1014. return true;
  1015. }
  1016. bool getParameterValueForString(const clap_id param_id, const char* const display, double* const value) const
  1017. {
  1018. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
  1019. const bool isInteger = fPlugin.isParameterInteger(param_id);
  1020. for (uint32_t i=0; i < enumValues.count; ++i)
  1021. {
  1022. if (std::strcmp(display, enumValues.values[i].label) == 0)
  1023. {
  1024. *value = enumValues.values[i].value;
  1025. return true;
  1026. }
  1027. }
  1028. if (isInteger)
  1029. *value = std::atoi(display);
  1030. else
  1031. *value = std::atof(display);
  1032. return true;
  1033. }
  1034. void flushParameters(const clap_input_events_t* const in,
  1035. const clap_output_events_t* const out,
  1036. const uint32_t frameOffset)
  1037. {
  1038. if (const uint32_t len = in != nullptr ? in->size(in) : 0)
  1039. {
  1040. for (uint32_t i=0; i<len; ++i)
  1041. {
  1042. const clap_event_header_t* const event = in->get(in, i);
  1043. if (event->type != CLAP_EVENT_PARAM_VALUE)
  1044. continue;
  1045. DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
  1046. event->size, sizeof(clap_event_param_value));
  1047. setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
  1048. }
  1049. }
  1050. if (out != nullptr)
  1051. {
  1052. clap_event_param_value_t clapEvent = {
  1053. { sizeof(clap_event_param_value_t), frameOffset, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE },
  1054. 0, nullptr, 0, 0, 0, 0, 0.0
  1055. };
  1056. float value;
  1057. for (uint i=0; i<fCachedParameters.numParams; ++i)
  1058. {
  1059. if (fPlugin.isParameterOutputOrTrigger(i))
  1060. {
  1061. value = fPlugin.getParameterValue(i);
  1062. if (d_isEqual(fCachedParameters.values[i], value))
  1063. continue;
  1064. fCachedParameters.values[i] = value;
  1065. fCachedParameters.changed[i] = true;
  1066. clapEvent.param_id = i;
  1067. clapEvent.value = value;
  1068. out->try_push(out, &clapEvent.header);
  1069. }
  1070. }
  1071. }
  1072. #if DISTRHO_PLUGIN_WANT_LATENCY
  1073. const bool active = fPlugin.isActive();
  1074. checkForLatencyChanges(active, !active);
  1075. #endif
  1076. }
  1077. // ----------------------------------------------------------------------------------------------------------------
  1078. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1079. void addNoteEvent(const clap_event_note_t* const event, const bool isOn) noexcept
  1080. {
  1081. DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,);
  1082. if (fMidiEventCount == kMaxMidiEvents)
  1083. return;
  1084. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  1085. midiEvent.frame = event->header.time;
  1086. midiEvent.size = 3;
  1087. midiEvent.data[0] = (isOn ? 0x90 : 0x80) | (event->channel & 0x0F);
  1088. midiEvent.data[1] = std::max(0, std::min(127, static_cast<int>(event->key)));
  1089. midiEvent.data[2] = std::max(0, std::min(127, static_cast<int>(event->velocity * 127 + 0.5)));
  1090. }
  1091. void addMidiEvent(const clap_event_midi_t* const event) noexcept
  1092. {
  1093. DISTRHO_SAFE_ASSERT_UINT_RETURN(event->port_index == 0, event->port_index,);
  1094. if (fMidiEventCount == kMaxMidiEvents)
  1095. return;
  1096. MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
  1097. midiEvent.frame = event->header.time;
  1098. midiEvent.size = 3;
  1099. std::memcpy(midiEvent.data, event->data, 3);
  1100. }
  1101. #endif
  1102. void setParameterValueFromEvent(const clap_event_param_value* const event)
  1103. {
  1104. fCachedParameters.values[event->param_id] = event->value;
  1105. fCachedParameters.changed[event->param_id] = true;
  1106. fPlugin.setParameterValue(event->param_id, event->value);
  1107. }
  1108. // ----------------------------------------------------------------------------------------------------------------
  1109. // audio ports
  1110. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1111. template<bool isInput>
  1112. uint32_t getAudioPortCount() const noexcept
  1113. {
  1114. return (isInput ? fAudioInputBuses : fAudioOutputBuses).size();
  1115. }
  1116. template<bool isInput>
  1117. bool getAudioPortInfo(const uint32_t index, clap_audio_port_info_t* const info) const noexcept
  1118. {
  1119. const std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses);
  1120. DISTRHO_SAFE_ASSERT_RETURN(index < busInfos.size(), false);
  1121. const BusInfo& busInfo(busInfos[index]);
  1122. info->id = busInfo.groupId;
  1123. d_strncpy(info->name, busInfo.name, CLAP_NAME_SIZE);
  1124. info->flags = busInfo.isMain ? CLAP_AUDIO_PORT_IS_MAIN : 0x0;
  1125. info->channel_count = busInfo.numChannels;
  1126. switch (busInfo.groupId)
  1127. {
  1128. case kPortGroupMono:
  1129. info->port_type = CLAP_PORT_MONO;
  1130. break;
  1131. case kPortGroupStereo:
  1132. info->port_type = CLAP_PORT_STEREO;
  1133. break;
  1134. default:
  1135. info->port_type = nullptr;
  1136. break;
  1137. }
  1138. info->in_place_pair = busInfo.hasPair ? busInfo.groupId : CLAP_INVALID_ID;
  1139. return true;
  1140. }
  1141. #endif
  1142. // ----------------------------------------------------------------------------------------------------------------
  1143. // latency
  1144. #if DISTRHO_PLUGIN_WANT_LATENCY
  1145. uint32_t getLatency() const noexcept
  1146. {
  1147. return fPlugin.getLatency();
  1148. }
  1149. void checkForLatencyChanges(const bool isActive, const bool fromMainThread)
  1150. {
  1151. const uint32_t latency = fPlugin.getLatency();
  1152. if (fLastKnownLatency == latency)
  1153. return;
  1154. fLastKnownLatency = latency;
  1155. if (isActive)
  1156. {
  1157. fLatencyChanged = true;
  1158. fHost->request_restart(fHost);
  1159. }
  1160. else
  1161. {
  1162. // if this is main-thread we can report latency change directly
  1163. if (fromMainThread || (fHostExtensions.threadCheck != nullptr && fHostExtensions.threadCheck->is_main_thread(fHost)))
  1164. {
  1165. fLatencyChanged = false;
  1166. fHostExtensions.latency->changed(fHost);
  1167. }
  1168. // otherwise we need to request a main-thread callback
  1169. else
  1170. {
  1171. fLatencyChanged = true;
  1172. fHost->request_callback(fHost);
  1173. }
  1174. }
  1175. }
  1176. // called from main thread
  1177. void reportLatencyChangeIfNeeded()
  1178. {
  1179. if (fLatencyChanged)
  1180. {
  1181. fLatencyChanged = false;
  1182. fHostExtensions.latency->changed(fHost);
  1183. }
  1184. }
  1185. #endif
  1186. // ----------------------------------------------------------------------------------------------------------------
  1187. // state
  1188. bool stateSave(const clap_ostream_t* const stream)
  1189. {
  1190. const uint32_t paramCount = fPlugin.getParameterCount();
  1191. #if DISTRHO_PLUGIN_WANT_STATE
  1192. const uint32_t stateCount = fPlugin.getStateCount();
  1193. #else
  1194. const uint32_t stateCount = 0;
  1195. #endif
  1196. if (stateCount == 0 && paramCount == 0)
  1197. {
  1198. char buffer = '\0';
  1199. return stream->write(stream, &buffer, 1) == 1;
  1200. }
  1201. #if DISTRHO_PLUGIN_WANT_FULL_STATE
  1202. // Update current state
  1203. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1204. {
  1205. const String& key = cit->first;
  1206. fStateMap[key] = fPlugin.getStateValue(key);
  1207. }
  1208. #endif
  1209. String state;
  1210. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1211. {
  1212. String tmpStr("__dpf_program__\xff");
  1213. tmpStr += String(fCurrentProgram);
  1214. tmpStr += "\xff";
  1215. state += tmpStr;
  1216. }
  1217. #endif
  1218. #if DISTRHO_PLUGIN_WANT_STATE
  1219. if (stateCount != 0)
  1220. {
  1221. state += "__dpf_state_begin__\xff";
  1222. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1223. {
  1224. const String& key = cit->first;
  1225. const String& value = cit->second;
  1226. // join key and value
  1227. String tmpStr;
  1228. tmpStr = key;
  1229. tmpStr += "\xff";
  1230. tmpStr += value;
  1231. tmpStr += "\xff";
  1232. state += tmpStr;
  1233. }
  1234. state += "__dpf_state_end__\xff";
  1235. }
  1236. #endif
  1237. if (paramCount != 0)
  1238. {
  1239. state += "__dpf_parameters_begin__\xff";
  1240. for (uint32_t i=0; i<paramCount; ++i)
  1241. {
  1242. if (fPlugin.isParameterOutputOrTrigger(i))
  1243. continue;
  1244. // join key and value
  1245. String tmpStr;
  1246. tmpStr = fPlugin.getParameterSymbol(i);
  1247. tmpStr += "\xff";
  1248. if (fPlugin.getParameterHints(i) & kParameterIsInteger)
  1249. tmpStr += String(static_cast<int>(std::round(fPlugin.getParameterValue(i))));
  1250. else
  1251. tmpStr += String(fPlugin.getParameterValue(i));
  1252. tmpStr += "\xff";
  1253. state += tmpStr;
  1254. }
  1255. state += "__dpf_parameters_end__\xff";
  1256. }
  1257. // terminator
  1258. state += "\xfe";
  1259. state.replace('\xff', '\0');
  1260. // now saving state, carefully until host written bytes matches full state size
  1261. const char* buffer = state.buffer();
  1262. const int32_t size = static_cast<int32_t>(state.length())+1;
  1263. for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn)
  1264. {
  1265. wrtn = stream->write(stream, buffer, size - wrtntotal);
  1266. DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, false);
  1267. }
  1268. return true;
  1269. }
  1270. bool stateLoad(const clap_istream_t* const stream)
  1271. {
  1272. #if DISTRHO_PLUGIN_HAS_UI
  1273. ClapUI* const ui = fUI.get();
  1274. #endif
  1275. String key, value;
  1276. bool hasValue = false;
  1277. bool fillingKey = true; // if filling key or value
  1278. char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters)
  1279. char buffer[512], orig;
  1280. buffer[sizeof(buffer)-1] = '\xff';
  1281. for (int32_t terminated = 0, read; terminated == 0;)
  1282. {
  1283. read = stream->read(stream, buffer, sizeof(buffer)-1);
  1284. DISTRHO_SAFE_ASSERT_INT_RETURN(read >= 0, read, false);
  1285. if (read == 0)
  1286. return true;
  1287. for (int32_t i = 0; i < read; ++i)
  1288. {
  1289. // found terminator, stop here
  1290. if (buffer[i] == '\xfe')
  1291. {
  1292. terminated = 1;
  1293. break;
  1294. }
  1295. // store character at read position
  1296. orig = buffer[read];
  1297. // place null character to create valid string
  1298. buffer[read] = '\0';
  1299. // append to temporary vars
  1300. if (fillingKey)
  1301. {
  1302. key += buffer + i;
  1303. }
  1304. else
  1305. {
  1306. value += buffer + i;
  1307. hasValue = true;
  1308. }
  1309. // increase buffer offset by length of string
  1310. i += std::strlen(buffer + i);
  1311. // restore read character
  1312. buffer[read] = orig;
  1313. // if buffer offset points to null, we found the end of a string, lets check
  1314. if (buffer[i] == '\0')
  1315. {
  1316. // special keys
  1317. if (key == "__dpf_state_begin__")
  1318. {
  1319. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
  1320. queryingType, false);
  1321. queryingType = 's';
  1322. key.clear();
  1323. value.clear();
  1324. hasValue = false;
  1325. continue;
  1326. }
  1327. if (key == "__dpf_state_end__")
  1328. {
  1329. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, false);
  1330. queryingType = 'n';
  1331. key.clear();
  1332. value.clear();
  1333. hasValue = false;
  1334. continue;
  1335. }
  1336. if (key == "__dpf_parameters_begin__")
  1337. {
  1338. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
  1339. queryingType, false);
  1340. queryingType = 'p';
  1341. key.clear();
  1342. value.clear();
  1343. hasValue = false;
  1344. continue;
  1345. }
  1346. if (key == "__dpf_parameters_end__")
  1347. {
  1348. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, false);
  1349. queryingType = 'x';
  1350. key.clear();
  1351. value.clear();
  1352. hasValue = false;
  1353. continue;
  1354. }
  1355. // no special key, swap between reading real key and value
  1356. fillingKey = !fillingKey;
  1357. // if there is no value yet keep reading until we have one
  1358. if (! hasValue)
  1359. continue;
  1360. if (key == "__dpf_program__")
  1361. {
  1362. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, false);
  1363. queryingType = 'n';
  1364. d_debug("found program '%s'", value.buffer());
  1365. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1366. const int program = std::atoi(value.buffer());
  1367. DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0);
  1368. fCurrentProgram = static_cast<uint32_t>(program);
  1369. fPlugin.loadProgram(fCurrentProgram);
  1370. #if DISTRHO_PLUGIN_HAS_UI
  1371. if (ui != nullptr)
  1372. ui->setProgramFromPlugin(fCurrentProgram);
  1373. #endif
  1374. #endif
  1375. }
  1376. else if (queryingType == 's')
  1377. {
  1378. d_debug("found state '%s' '%s'", key.buffer(), value.buffer());
  1379. #if DISTRHO_PLUGIN_WANT_STATE
  1380. if (fPlugin.wantStateKey(key))
  1381. {
  1382. fStateMap[key] = value;
  1383. fPlugin.setState(key, value);
  1384. #if DISTRHO_PLUGIN_HAS_UI
  1385. if (ui != nullptr)
  1386. ui->setStateFromPlugin(key, value);
  1387. #endif
  1388. }
  1389. #endif
  1390. }
  1391. else if (queryingType == 'p')
  1392. {
  1393. d_debug("found parameter '%s' '%s'", key.buffer(), value.buffer());
  1394. float fvalue;
  1395. // find parameter with this symbol, and set its value
  1396. for (uint32_t j=0; j<fCachedParameters.numParams; ++j)
  1397. {
  1398. if (fPlugin.isParameterOutputOrTrigger(j))
  1399. continue;
  1400. if (fPlugin.getParameterSymbol(j) != key)
  1401. continue;
  1402. if (fPlugin.getParameterHints(j) & kParameterIsInteger)
  1403. fvalue = std::atoi(value.buffer());
  1404. else
  1405. fvalue = std::atof(value.buffer());
  1406. fCachedParameters.values[j] = fvalue;
  1407. #if DISTRHO_PLUGIN_HAS_UI
  1408. if (ui != nullptr)
  1409. {
  1410. // UI parameter updates are handled outside the read loop (after host param restart)
  1411. fCachedParameters.changed[j] = true;
  1412. }
  1413. #endif
  1414. fPlugin.setParameterValue(j, fvalue);
  1415. break;
  1416. }
  1417. }
  1418. key.clear();
  1419. value.clear();
  1420. hasValue = false;
  1421. }
  1422. }
  1423. }
  1424. if (fHostExtensions.params != nullptr)
  1425. fHostExtensions.params->rescan(fHost, CLAP_PARAM_RESCAN_VALUES|CLAP_PARAM_RESCAN_TEXT);
  1426. #if DISTRHO_PLUGIN_WANT_LATENCY
  1427. checkForLatencyChanges(fPlugin.isActive(), true);
  1428. reportLatencyChangeIfNeeded();
  1429. #endif
  1430. #if DISTRHO_PLUGIN_HAS_UI
  1431. if (ui != nullptr)
  1432. {
  1433. for (uint32_t i=0; i<fCachedParameters.numParams; ++i)
  1434. {
  1435. if (fPlugin.isParameterOutputOrTrigger(i))
  1436. continue;
  1437. fCachedParameters.changed[i] = false;
  1438. ui->setParameterValueFromPlugin(i, fCachedParameters.values[i]);
  1439. }
  1440. }
  1441. #endif
  1442. return true;
  1443. }
  1444. // ----------------------------------------------------------------------------------------------------------------
  1445. // gui
  1446. #if DISTRHO_PLUGIN_HAS_UI
  1447. bool createUI(const bool isFloating)
  1448. {
  1449. const clap_host_gui_t* const hostGui = getHostExtension<clap_host_gui_t>(CLAP_EXT_GUI);
  1450. DISTRHO_SAFE_ASSERT_RETURN(hostGui != nullptr, false);
  1451. #if DPF_CLAP_USING_HOST_TIMER
  1452. const clap_host_timer_support_t* const hostTimer = getHostExtension<clap_host_timer_support_t>(CLAP_EXT_TIMER_SUPPORT);
  1453. DISTRHO_SAFE_ASSERT_RETURN(hostTimer != nullptr, false);
  1454. #endif
  1455. fUI = new ClapUI(fPlugin, this, fHost, hostGui,
  1456. #if DPF_CLAP_USING_HOST_TIMER
  1457. hostTimer,
  1458. #endif
  1459. isFloating);
  1460. return true;
  1461. }
  1462. void destroyUI()
  1463. {
  1464. fUI = nullptr;
  1465. }
  1466. ClapUI* getUI() const noexcept
  1467. {
  1468. return fUI.get();
  1469. }
  1470. #endif
  1471. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE
  1472. void setStateFromUI(const char* const key, const char* const value) override
  1473. {
  1474. fPlugin.setState(key, value);
  1475. // check if we want to save this key
  1476. if (! fPlugin.wantStateKey(key))
  1477. return;
  1478. // check if key already exists
  1479. for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it)
  1480. {
  1481. const String& dkey(it->first);
  1482. if (dkey == key)
  1483. {
  1484. it->second = value;
  1485. return;
  1486. }
  1487. }
  1488. d_stderr("Failed to find plugin state with key \"%s\"", key);
  1489. }
  1490. #endif
  1491. // ----------------------------------------------------------------------------------------------------------------
  1492. private:
  1493. // Plugin and UI
  1494. PluginExporter fPlugin;
  1495. #if DISTRHO_PLUGIN_HAS_UI
  1496. ScopedPointer<ClapUI> fUI;
  1497. #endif
  1498. // CLAP stuff
  1499. const clap_host_t* const fHost;
  1500. const clap_output_events_t* fOutputEvents;
  1501. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1502. const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS];
  1503. #endif
  1504. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1505. float* fAudioOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS];
  1506. #endif
  1507. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1508. bool fUsingCV;
  1509. #endif
  1510. #if DISTRHO_PLUGIN_WANT_LATENCY
  1511. bool fLatencyChanged;
  1512. uint32_t fLastKnownLatency;
  1513. #endif
  1514. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1515. uint32_t fMidiEventCount;
  1516. MidiEvent fMidiEvents[kMaxMidiEvents];
  1517. #if DISTRHO_PLUGIN_HAS_UI
  1518. RingBufferControl<SmallStackBuffer> fNotesRingBuffer;
  1519. #endif
  1520. #endif
  1521. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  1522. TimePosition fTimePosition;
  1523. #endif
  1524. struct HostExtensions {
  1525. const clap_host_t* const host;
  1526. const clap_host_params_t* params;
  1527. #if DISTRHO_PLUGIN_WANT_LATENCY
  1528. const clap_host_latency_t* latency;
  1529. const clap_host_thread_check_t* threadCheck;
  1530. #endif
  1531. HostExtensions(const clap_host_t* const host)
  1532. : host(host),
  1533. params(nullptr)
  1534. #if DISTRHO_PLUGIN_WANT_LATENCY
  1535. , latency(nullptr)
  1536. , threadCheck(nullptr)
  1537. #endif
  1538. {}
  1539. bool init()
  1540. {
  1541. params = static_cast<const clap_host_params_t*>(host->get_extension(host, CLAP_EXT_PARAMS));
  1542. #if DISTRHO_PLUGIN_WANT_LATENCY
  1543. DISTRHO_SAFE_ASSERT_RETURN(host->request_restart != nullptr, false);
  1544. DISTRHO_SAFE_ASSERT_RETURN(host->request_callback != nullptr, false);
  1545. latency = static_cast<const clap_host_latency_t*>(host->get_extension(host, CLAP_EXT_LATENCY));
  1546. threadCheck = static_cast<const clap_host_thread_check_t*>(host->get_extension(host, CLAP_EXT_THREAD_CHECK));
  1547. #endif
  1548. return true;
  1549. }
  1550. } fHostExtensions;
  1551. // ----------------------------------------------------------------------------------------------------------------
  1552. // helper functions for dealing with buses
  1553. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1554. struct BusInfo {
  1555. char name[CLAP_NAME_SIZE];
  1556. uint32_t numChannels;
  1557. bool hasPair;
  1558. bool isCV;
  1559. bool isMain;
  1560. uint32_t groupId;
  1561. };
  1562. std::vector<BusInfo> fAudioInputBuses, fAudioOutputBuses;
  1563. template<bool isInput>
  1564. void fillInBusInfoDetails()
  1565. {
  1566. constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
  1567. std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses);
  1568. enum {
  1569. kPortTypeNull,
  1570. kPortTypeAudio,
  1571. kPortTypeSidechain,
  1572. kPortTypeCV,
  1573. kPortTypeGroup
  1574. } lastSeenPortType = kPortTypeNull;
  1575. uint32_t lastSeenGroupId = kPortGroupNone;
  1576. uint32_t nonGroupAudioId = 0;
  1577. uint32_t nonGroupSidechainId = 0x20000000;
  1578. for (uint32_t i=0; i<numPorts; ++i)
  1579. {
  1580. const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i));
  1581. if (port.groupId != kPortGroupNone)
  1582. {
  1583. if (lastSeenPortType != kPortTypeGroup || lastSeenGroupId != port.groupId)
  1584. {
  1585. lastSeenPortType = kPortTypeGroup;
  1586. lastSeenGroupId = port.groupId;
  1587. BusInfo busInfo = {
  1588. {}, 1, false, false,
  1589. // if this is the first port, set it as main
  1590. busInfos.empty(),
  1591. // user given group id with extra safety offset
  1592. port.groupId + 0x80000000
  1593. };
  1594. const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId));
  1595. switch (port.groupId)
  1596. {
  1597. case kPortGroupStereo:
  1598. case kPortGroupMono:
  1599. if (busInfo.isMain)
  1600. {
  1601. d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE);
  1602. break;
  1603. }
  1604. // fall-through
  1605. default:
  1606. if (group.name.isNotEmpty())
  1607. d_strncpy(busInfo.name, group.name, CLAP_NAME_SIZE);
  1608. else
  1609. d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
  1610. break;
  1611. }
  1612. busInfos.push_back(busInfo);
  1613. }
  1614. else
  1615. {
  1616. ++busInfos.back().numChannels;
  1617. }
  1618. }
  1619. else if (port.hints & kAudioPortIsCV)
  1620. {
  1621. // TODO
  1622. lastSeenPortType = kPortTypeCV;
  1623. lastSeenGroupId = kPortGroupNone;
  1624. fUsingCV = true;
  1625. }
  1626. else if (port.hints & kAudioPortIsSidechain)
  1627. {
  1628. if (lastSeenPortType != kPortTypeSidechain)
  1629. {
  1630. lastSeenPortType = kPortTypeSidechain;
  1631. lastSeenGroupId = kPortGroupNone;
  1632. BusInfo busInfo = {
  1633. {}, 1, false, false,
  1634. // not main
  1635. false,
  1636. // give unique id
  1637. nonGroupSidechainId++
  1638. };
  1639. d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
  1640. busInfos.push_back(busInfo);
  1641. }
  1642. else
  1643. {
  1644. ++busInfos.back().numChannels;
  1645. }
  1646. }
  1647. else
  1648. {
  1649. if (lastSeenPortType != kPortTypeAudio)
  1650. {
  1651. lastSeenPortType = kPortTypeAudio;
  1652. lastSeenGroupId = kPortGroupNone;
  1653. BusInfo busInfo = {
  1654. {}, 1, false, false,
  1655. // if this is the first port, set it as main
  1656. busInfos.empty(),
  1657. // give unique id
  1658. nonGroupAudioId++
  1659. };
  1660. if (busInfo.isMain)
  1661. {
  1662. d_strncpy(busInfo.name, isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE);
  1663. }
  1664. else
  1665. {
  1666. d_strncpy(busInfo.name, port.name, CLAP_NAME_SIZE);
  1667. }
  1668. busInfos.push_back(busInfo);
  1669. }
  1670. else
  1671. {
  1672. ++busInfos.back().numChannels;
  1673. }
  1674. }
  1675. }
  1676. }
  1677. #endif
  1678. #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1679. void fillInBusInfoPairs()
  1680. {
  1681. const size_t numChannels = std::min(fAudioInputBuses.size(), fAudioOutputBuses.size());
  1682. for (size_t i=0; i<numChannels; ++i)
  1683. {
  1684. if (fAudioInputBuses[i].groupId != fAudioOutputBuses[i].groupId)
  1685. break;
  1686. if (fAudioInputBuses[i].numChannels != fAudioOutputBuses[i].numChannels)
  1687. break;
  1688. if (fAudioInputBuses[i].isMain != fAudioOutputBuses[i].isMain)
  1689. break;
  1690. fAudioInputBuses[i].hasPair = fAudioOutputBuses[i].hasPair = true;
  1691. }
  1692. }
  1693. #endif
  1694. // ----------------------------------------------------------------------------------------------------------------
  1695. // DPF callbacks
  1696. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1697. bool writeMidi(const MidiEvent& midiEvent)
  1698. {
  1699. DISTRHO_SAFE_ASSERT_RETURN(fOutputEvents != nullptr, false);
  1700. if (midiEvent.size > 3)
  1701. return true;
  1702. const clap_event_midi clapEvent = {
  1703. { sizeof(clap_event_midi), midiEvent.frame, 0, CLAP_EVENT_MIDI, CLAP_EVENT_IS_LIVE },
  1704. 0, { midiEvent.data[0],
  1705. static_cast<uint8_t>(midiEvent.size >= 2 ? midiEvent.data[1] : 0),
  1706. static_cast<uint8_t>(midiEvent.size >= 3 ? midiEvent.data[2] : 0) }
  1707. };
  1708. return fOutputEvents->try_push(fOutputEvents, &clapEvent.header);
  1709. }
  1710. static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
  1711. {
  1712. return static_cast<PluginCLAP*>(ptr)->writeMidi(midiEvent);
  1713. }
  1714. #endif
  1715. #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  1716. bool requestParameterValueChange(uint32_t, float)
  1717. {
  1718. return true;
  1719. }
  1720. static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
  1721. {
  1722. return static_cast<PluginCLAP*>(ptr)->requestParameterValueChange(index, value);
  1723. }
  1724. #endif
  1725. #if DISTRHO_PLUGIN_WANT_STATE
  1726. bool updateState(const char*, const char*)
  1727. {
  1728. return true;
  1729. }
  1730. static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
  1731. {
  1732. return static_cast<PluginCLAP*>(ptr)->updateState(key, value);
  1733. }
  1734. #endif
  1735. };
  1736. // --------------------------------------------------------------------------------------------------------------------
  1737. static ScopedPointer<PluginExporter> sPlugin;
  1738. // --------------------------------------------------------------------------------------------------------------------
  1739. // plugin gui
  1740. #if DISTRHO_PLUGIN_HAS_UI
  1741. static const char* const kSupportedAPIs[] = {
  1742. #if defined(DISTRHO_OS_WINDOWS)
  1743. CLAP_WINDOW_API_WIN32,
  1744. #elif defined(DISTRHO_OS_MAC)
  1745. CLAP_WINDOW_API_COCOA,
  1746. #else
  1747. CLAP_WINDOW_API_X11,
  1748. #endif
  1749. };
  1750. // TODO DPF external UI
  1751. static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
  1752. {
  1753. for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
  1754. {
  1755. if (std::strcmp(kSupportedAPIs[i], api) == 0)
  1756. return true;
  1757. }
  1758. return false;
  1759. }
  1760. // TODO DPF external UI
  1761. static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
  1762. {
  1763. *api = kSupportedAPIs[0];
  1764. *is_floating = false;
  1765. return true;
  1766. }
  1767. static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
  1768. {
  1769. for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
  1770. {
  1771. if (std::strcmp(kSupportedAPIs[i], api) == 0)
  1772. {
  1773. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1774. return instance->createUI(is_floating);
  1775. }
  1776. }
  1777. return false;
  1778. }
  1779. static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin)
  1780. {
  1781. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1782. instance->destroyUI();
  1783. }
  1784. static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
  1785. {
  1786. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1787. ClapUI* const gui = instance->getUI();
  1788. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1789. #ifndef DISTRHO_OS_MAC
  1790. return gui->setScaleFactor(scale);
  1791. #else
  1792. return true;
  1793. // unused
  1794. (void)scale;
  1795. #endif
  1796. }
  1797. static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
  1798. {
  1799. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1800. ClapUI* const gui = instance->getUI();
  1801. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1802. return gui->getSize(width, height);
  1803. }
  1804. static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin)
  1805. {
  1806. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1807. ClapUI* const gui = instance->getUI();
  1808. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1809. return gui->canResize();
  1810. }
  1811. static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
  1812. {
  1813. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1814. ClapUI* const gui = instance->getUI();
  1815. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1816. return gui->getResizeHints(hints);
  1817. }
  1818. static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
  1819. {
  1820. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1821. ClapUI* const gui = instance->getUI();
  1822. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1823. return gui->adjustSize(width, height);
  1824. }
  1825. static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
  1826. {
  1827. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1828. ClapUI* const gui = instance->getUI();
  1829. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1830. return gui->setSizeFromHost(width, height);
  1831. }
  1832. static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
  1833. {
  1834. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1835. ClapUI* const gui = instance->getUI();
  1836. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1837. return gui->setParent(window);
  1838. }
  1839. static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
  1840. {
  1841. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1842. ClapUI* const gui = instance->getUI();
  1843. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1844. return gui->setTransient(window);
  1845. }
  1846. static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
  1847. {
  1848. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1849. ClapUI* const gui = instance->getUI();
  1850. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
  1851. return gui->suggestTitle(title);
  1852. }
  1853. static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin)
  1854. {
  1855. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1856. ClapUI* const gui = instance->getUI();
  1857. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1858. return gui->show();
  1859. }
  1860. static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin)
  1861. {
  1862. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1863. ClapUI* const gui = instance->getUI();
  1864. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  1865. return gui->hide();
  1866. }
  1867. static const clap_plugin_gui_t clap_plugin_gui = {
  1868. clap_gui_is_api_supported,
  1869. clap_gui_get_preferred_api,
  1870. clap_gui_create,
  1871. clap_gui_destroy,
  1872. clap_gui_set_scale,
  1873. clap_gui_get_size,
  1874. clap_gui_can_resize,
  1875. clap_gui_get_resize_hints,
  1876. clap_gui_adjust_size,
  1877. clap_gui_set_size,
  1878. clap_gui_set_parent,
  1879. clap_gui_set_transient,
  1880. clap_gui_suggest_title,
  1881. clap_gui_show,
  1882. clap_gui_hide
  1883. };
  1884. // --------------------------------------------------------------------------------------------------------------------
  1885. // plugin timer
  1886. #if DPF_CLAP_USING_HOST_TIMER
  1887. static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
  1888. {
  1889. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1890. ClapUI* const gui = instance->getUI();
  1891. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
  1892. return gui->idleCallback();
  1893. }
  1894. static const clap_plugin_timer_support_t clap_timer = {
  1895. clap_plugin_on_timer
  1896. };
  1897. #endif
  1898. #endif // DISTRHO_PLUGIN_HAS_UI
  1899. // --------------------------------------------------------------------------------------------------------------------
  1900. // plugin audio ports
  1901. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  1902. static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
  1903. {
  1904. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1905. return is_input ? instance->getAudioPortCount<true>()
  1906. : instance->getAudioPortCount<false>();
  1907. }
  1908. static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
  1909. const uint32_t index,
  1910. const bool is_input,
  1911. clap_audio_port_info_t* const info)
  1912. {
  1913. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1914. return is_input ? instance->getAudioPortInfo<true>(index, info)
  1915. : instance->getAudioPortInfo<false>(index, info);
  1916. }
  1917. static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
  1918. clap_plugin_audio_ports_count,
  1919. clap_plugin_audio_ports_get
  1920. };
  1921. #endif
  1922. // --------------------------------------------------------------------------------------------------------------------
  1923. // plugin note ports
  1924. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
  1925. static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
  1926. {
  1927. return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0;
  1928. }
  1929. static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
  1930. const bool is_input, clap_note_port_info_t* const info)
  1931. {
  1932. if (is_input)
  1933. {
  1934. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1935. info->id = 0;
  1936. info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
  1937. info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
  1938. std::strcpy(info->name, "Event/MIDI Input");
  1939. return true;
  1940. #endif
  1941. }
  1942. else
  1943. {
  1944. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1945. info->id = 0;
  1946. info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
  1947. info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
  1948. std::strcpy(info->name, "Event/MIDI Output");
  1949. return true;
  1950. #endif
  1951. }
  1952. return false;
  1953. }
  1954. static const clap_plugin_note_ports_t clap_plugin_note_ports = {
  1955. clap_plugin_note_ports_count,
  1956. clap_plugin_note_ports_get
  1957. };
  1958. #endif
  1959. // --------------------------------------------------------------------------------------------------------------------
  1960. // plugin parameters
  1961. static uint32_t CLAP_ABI clap_plugin_params_count(const clap_plugin_t* const plugin)
  1962. {
  1963. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1964. return instance->getParameterCount();
  1965. }
  1966. 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)
  1967. {
  1968. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1969. return instance->getParameterInfo(index, info);
  1970. }
  1971. static bool CLAP_ABI clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value)
  1972. {
  1973. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1974. return instance->getParameterValue(param_id, value);
  1975. }
  1976. 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)
  1977. {
  1978. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1979. return instance->getParameterStringForValue(param_id, value, display, size);
  1980. }
  1981. 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)
  1982. {
  1983. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1984. return instance->getParameterValueForString(param_id, display, value);
  1985. }
  1986. 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)
  1987. {
  1988. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1989. return instance->flushParameters(in, out, 0);
  1990. }
  1991. static const clap_plugin_params_t clap_plugin_params = {
  1992. clap_plugin_params_count,
  1993. clap_plugin_params_get_info,
  1994. clap_plugin_params_get_value,
  1995. clap_plugin_params_value_to_text,
  1996. clap_plugin_params_text_to_value,
  1997. clap_plugin_params_flush
  1998. };
  1999. #if DISTRHO_PLUGIN_WANT_LATENCY
  2000. // --------------------------------------------------------------------------------------------------------------------
  2001. // plugin latency
  2002. static uint32_t CLAP_ABI clap_plugin_latency_get(const clap_plugin_t* const plugin)
  2003. {
  2004. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  2005. return instance->getLatency();
  2006. }
  2007. static const clap_plugin_latency_t clap_plugin_latency = {
  2008. clap_plugin_latency_get
  2009. };
  2010. #endif
  2011. #if DISTRHO_PLUGIN_WANT_STATE
  2012. // --------------------------------------------------------------------------------------------------------------------
  2013. // plugin state
  2014. static bool CLAP_ABI clap_plugin_state_save(const clap_plugin_t* const plugin, const clap_ostream_t* const stream)
  2015. {
  2016. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  2017. return instance->stateSave(stream);
  2018. }
  2019. static bool CLAP_ABI clap_plugin_state_load(const clap_plugin_t* const plugin, const clap_istream_t* const stream)
  2020. {
  2021. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  2022. return instance->stateLoad(stream);
  2023. }
  2024. static const clap_plugin_state_t clap_plugin_state = {
  2025. clap_plugin_state_save,
  2026. clap_plugin_state_load
  2027. };
  2028. #endif
  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 DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
  2080. if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
  2081. return &clap_plugin_audio_ports;
  2082. #endif
  2083. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
  2084. if (std::strcmp(id, CLAP_EXT_NOTE_PORTS) == 0)
  2085. return &clap_plugin_note_ports;
  2086. #endif
  2087. #if DISTRHO_PLUGIN_WANT_LATENCY
  2088. if (std::strcmp(id, CLAP_EXT_LATENCY) == 0)
  2089. return &clap_plugin_latency;
  2090. #endif
  2091. #if DISTRHO_PLUGIN_WANT_STATE
  2092. if (std::strcmp(id, CLAP_EXT_STATE) == 0)
  2093. return &clap_plugin_state;
  2094. #endif
  2095. #if DISTRHO_PLUGIN_HAS_UI
  2096. if (std::strcmp(id, CLAP_EXT_GUI) == 0)
  2097. return &clap_plugin_gui;
  2098. #if DPF_CLAP_USING_HOST_TIMER
  2099. if (std::strcmp(id, CLAP_EXT_TIMER_SUPPORT) == 0)
  2100. return &clap_timer;
  2101. #endif
  2102. #endif
  2103. return nullptr;
  2104. }
  2105. static void CLAP_ABI clap_plugin_on_main_thread(const clap_plugin_t* const plugin)
  2106. {
  2107. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  2108. instance->onMainThread();
  2109. }
  2110. // --------------------------------------------------------------------------------------------------------------------
  2111. // plugin factory
  2112. static uint32_t CLAP_ABI clap_get_plugin_count(const clap_plugin_factory_t*)
  2113. {
  2114. return 1;
  2115. }
  2116. static const clap_plugin_descriptor_t* CLAP_ABI clap_get_plugin_descriptor(const clap_plugin_factory_t*,
  2117. const uint32_t index)
  2118. {
  2119. DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr);
  2120. static const char* features[] = {
  2121. #ifdef DISTRHO_PLUGIN_CLAP_FEATURES
  2122. DISTRHO_PLUGIN_CLAP_FEATURES,
  2123. #elif DISTRHO_PLUGIN_IS_SYNTH
  2124. "instrument",
  2125. #endif
  2126. nullptr
  2127. };
  2128. static const clap_plugin_descriptor_t descriptor = {
  2129. CLAP_VERSION,
  2130. DISTRHO_PLUGIN_CLAP_ID,
  2131. sPlugin->getName(),
  2132. sPlugin->getMaker(),
  2133. // TODO url
  2134. "",
  2135. // TODO manual url
  2136. "",
  2137. // TODO support url
  2138. "",
  2139. // TODO version string
  2140. "",
  2141. sPlugin->getDescription(),
  2142. features
  2143. };
  2144. return &descriptor;
  2145. }
  2146. static const clap_plugin_t* CLAP_ABI clap_create_plugin(const clap_plugin_factory_t* const factory,
  2147. const clap_host_t* const host,
  2148. const char*)
  2149. {
  2150. clap_plugin_t* const pluginptr = static_cast<clap_plugin_t*>(std::malloc(sizeof(clap_plugin_t)));
  2151. DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr);
  2152. // default early values
  2153. if (d_nextBufferSize == 0)
  2154. d_nextBufferSize = 1024;
  2155. if (d_nextSampleRate <= 0.0)
  2156. d_nextSampleRate = 44100.0;
  2157. d_nextCanRequestParameterValueChanges = true;
  2158. const clap_plugin_t plugin = {
  2159. clap_get_plugin_descriptor(factory, 0),
  2160. new PluginCLAP(host),
  2161. clap_plugin_init,
  2162. clap_plugin_destroy,
  2163. clap_plugin_activate,
  2164. clap_plugin_deactivate,
  2165. clap_plugin_start_processing,
  2166. clap_plugin_stop_processing,
  2167. clap_plugin_reset,
  2168. clap_plugin_process,
  2169. clap_plugin_get_extension,
  2170. clap_plugin_on_main_thread
  2171. };
  2172. std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t));
  2173. return pluginptr;
  2174. }
  2175. static const clap_plugin_factory_t clap_plugin_factory = {
  2176. clap_get_plugin_count,
  2177. clap_get_plugin_descriptor,
  2178. clap_create_plugin
  2179. };
  2180. // --------------------------------------------------------------------------------------------------------------------
  2181. // plugin entry
  2182. static bool CLAP_ABI clap_plugin_entry_init(const char* const plugin_path)
  2183. {
  2184. static String bundlePath;
  2185. bundlePath = plugin_path;
  2186. d_nextBundlePath = bundlePath.buffer();
  2187. // init dummy plugin
  2188. if (sPlugin == nullptr)
  2189. {
  2190. // set valid but dummy values
  2191. d_nextBufferSize = 512;
  2192. d_nextSampleRate = 44100.0;
  2193. d_nextPluginIsDummy = true;
  2194. d_nextCanRequestParameterValueChanges = true;
  2195. // Create dummy plugin to get data from
  2196. sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
  2197. // unset
  2198. d_nextBufferSize = 0;
  2199. d_nextSampleRate = 0.0;
  2200. d_nextPluginIsDummy = false;
  2201. d_nextCanRequestParameterValueChanges = false;
  2202. }
  2203. return true;
  2204. }
  2205. static void CLAP_ABI clap_plugin_entry_deinit(void)
  2206. {
  2207. sPlugin = nullptr;
  2208. }
  2209. static const void* CLAP_ABI clap_plugin_entry_get_factory(const char* const factory_id)
  2210. {
  2211. if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0)
  2212. return &clap_plugin_factory;
  2213. return nullptr;
  2214. }
  2215. static const clap_plugin_entry_t clap_plugin_entry = {
  2216. CLAP_VERSION,
  2217. clap_plugin_entry_init,
  2218. clap_plugin_entry_deinit,
  2219. clap_plugin_entry_get_factory
  2220. };
  2221. // --------------------------------------------------------------------------------------------------------------------
  2222. END_NAMESPACE_DISTRHO
  2223. // --------------------------------------------------------------------------------------------------------------------
  2224. DISTRHO_PLUGIN_EXPORT
  2225. const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry;
  2226. // --------------------------------------------------------------------------------------------------------------------