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.

1472 lines
46KB

  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. #include "DistrhoPluginInternal.hpp"
  17. #include "extra/ScopedPointer.hpp"
  18. #if DISTRHO_PLUGIN_HAS_UI
  19. # include "DistrhoUIInternal.hpp"
  20. # include "extra/Mutex.hpp"
  21. #endif
  22. #include "clap/entry.h"
  23. #include "clap/plugin-factory.h"
  24. #include "clap/ext/audio-ports.h"
  25. #include "clap/ext/gui.h"
  26. #include "clap/ext/params.h"
  27. START_NAMESPACE_DISTRHO
  28. // --------------------------------------------------------------------------------------------------------------------
  29. struct ClapEventQueue
  30. {
  31. #if DISTRHO_PLUGIN_HAS_UI
  32. enum EventType {
  33. kEventGestureBegin,
  34. kEventGestureEnd,
  35. kEventParamSet
  36. };
  37. struct Event {
  38. EventType type;
  39. uint32_t index;
  40. float value;
  41. };
  42. struct Queue {
  43. Mutex lock;
  44. uint allocated;
  45. uint used;
  46. Event* events;
  47. Queue()
  48. : allocated(0),
  49. used(0),
  50. events(nullptr) {}
  51. ~Queue()
  52. {
  53. delete[] events;
  54. }
  55. void addEventFromUI(const Event& event)
  56. {
  57. const MutexLocker cml(lock);
  58. if (events == nullptr)
  59. {
  60. events = static_cast<Event*>(std::malloc(sizeof(Event) * 8));
  61. allocated = 8;
  62. }
  63. else if (used + 1 > allocated)
  64. {
  65. allocated = used * 2;
  66. events = static_cast<Event*>(std::realloc(events, sizeof(Event) * allocated));
  67. }
  68. std::memcpy(&events[used++], &event, sizeof(Event));
  69. }
  70. } fEventQueue;
  71. #endif
  72. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE
  73. virtual void setStateFromUI(const char* key, const char* value) = 0;
  74. #endif
  75. struct CachedParameters {
  76. uint numParams;
  77. bool* changed;
  78. float* values;
  79. CachedParameters()
  80. : changed(nullptr),
  81. values(nullptr) {}
  82. ~CachedParameters()
  83. {
  84. delete[] changed;
  85. delete[] values;
  86. }
  87. void setup(const uint numParameters)
  88. {
  89. if (numParameters == 0)
  90. return;
  91. numParams = numParameters;
  92. changed = new bool[numParameters];
  93. values = new float[numParameters];
  94. std::memset(changed, 0, sizeof(bool)*numParameters);
  95. std::memset(values, 0, sizeof(float)*numParameters);
  96. }
  97. } fCachedParameters;
  98. virtual ~ClapEventQueue() {}
  99. };
  100. // --------------------------------------------------------------------------------------------------------------------
  101. #if DISTRHO_PLUGIN_HAS_UI
  102. #if ! DISTRHO_PLUGIN_WANT_STATE
  103. static constexpr const setStateFunc setStateCallback = nullptr;
  104. #endif
  105. #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
  106. static constexpr const sendNoteFunc sendNoteCallback = nullptr;
  107. #endif
  108. /**
  109. * CLAP UI class.
  110. */
  111. class ClapUI : public DGL_NAMESPACE::IdleCallback
  112. {
  113. public:
  114. ClapUI(PluginExporter& plugin, ClapEventQueue* const eventQueue, const bool isFloating)
  115. : fPlugin(plugin),
  116. fPluinEventQueue(eventQueue),
  117. fEventQueue(eventQueue->fEventQueue),
  118. fCachedParameters(eventQueue->fCachedParameters),
  119. fUI(),
  120. fIsFloating(isFloating),
  121. fCallbackRegistered(false),
  122. fScaleFactor(0.0),
  123. fParentWindow(0),
  124. fTransientWindow(0)
  125. {
  126. }
  127. ~ClapUI() override
  128. {
  129. if (fCallbackRegistered)
  130. {
  131. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  132. if (UIExporter* const ui = fUI.get())
  133. ui->removeIdleCallbackForVST3(this);
  134. #endif
  135. }
  136. }
  137. bool setScaleFactor(const double scaleFactor)
  138. {
  139. if (d_isEqual(fScaleFactor, scaleFactor))
  140. return true;
  141. fScaleFactor = scaleFactor;
  142. if (UIExporter* const ui = fUI.get())
  143. ui->notifyScaleFactorChanged(scaleFactor);
  144. return true;
  145. }
  146. bool getSize(uint32_t* const width, uint32_t* const height) const
  147. {
  148. if (UIExporter* const ui = fUI.get())
  149. {
  150. *width = ui->getWidth();
  151. *height = ui->getHeight();
  152. return true;
  153. }
  154. #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT)
  155. *width = DISTRHO_UI_DEFAULT_WIDTH;
  156. *height = DISTRHO_UI_DEFAULT_HEIGHT;
  157. #else
  158. UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
  159. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
  160. fPlugin.getInstancePointer(), fScaleFactor);
  161. *width = tmpUI.getWidth();
  162. *height = tmpUI.getHeight();
  163. tmpUI.quit();
  164. #endif
  165. return true;
  166. }
  167. bool canResize() const noexcept
  168. {
  169. #if DISTRHO_UI_USER_RESIZABLE
  170. if (UIExporter* const ui = fUI.get())
  171. return ui->isResizable();
  172. #endif
  173. return false;
  174. }
  175. bool getResizeHints(clap_gui_resize_hints_t* const hints) const
  176. {
  177. if (canResize())
  178. {
  179. uint minimumWidth, minimumHeight;
  180. bool keepAspectRatio;
  181. fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
  182. hints->can_resize_horizontally = true;
  183. hints->can_resize_vertically = true;
  184. hints->preserve_aspect_ratio = keepAspectRatio;
  185. hints->aspect_ratio_width = minimumWidth;
  186. hints->aspect_ratio_height = minimumHeight;
  187. return true;
  188. }
  189. hints->can_resize_horizontally = false;
  190. hints->can_resize_vertically = false;
  191. hints->preserve_aspect_ratio = false;
  192. hints->aspect_ratio_width = 0;
  193. hints->aspect_ratio_height = 0;
  194. return false;
  195. }
  196. bool adjustSize(uint32_t* const width, uint32_t* const height) const
  197. {
  198. if (canResize())
  199. {
  200. uint minimumWidth, minimumHeight;
  201. bool keepAspectRatio;
  202. fUI->getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
  203. if (keepAspectRatio)
  204. {
  205. if (*width < 1)
  206. *width = 1;
  207. if (*height < 1)
  208. *height = 1;
  209. const double ratio = static_cast<double>(minimumWidth) / static_cast<double>(minimumHeight);
  210. const double reqRatio = static_cast<double>(*width) / static_cast<double>(*height);
  211. if (d_isNotEqual(ratio, reqRatio))
  212. {
  213. // fix width
  214. if (reqRatio > ratio)
  215. *width = static_cast<int32_t>(*height * ratio + 0.5);
  216. // fix height
  217. else
  218. *height = static_cast<int32_t>(static_cast<double>(*width) / ratio + 0.5);
  219. }
  220. }
  221. if (minimumWidth > *width)
  222. *width = minimumWidth;
  223. if (minimumHeight > *height)
  224. *height = minimumHeight;
  225. return true;
  226. }
  227. return false;
  228. }
  229. bool setSizeFromHost(const uint32_t width, const uint32_t height)
  230. {
  231. if (UIExporter* const ui = fUI.get())
  232. {
  233. ui->setWindowSizeFromHost(width, height);
  234. return true;
  235. }
  236. return false;
  237. }
  238. bool setParent(const clap_window_t* const window)
  239. {
  240. if (fIsFloating)
  241. return false;
  242. fParentWindow = window->uptr;
  243. /*
  244. if (fUI != nullptr)
  245. createUI();
  246. */
  247. return true;
  248. }
  249. bool setTransient(const clap_window_t* const window)
  250. {
  251. if (! fIsFloating)
  252. return false;
  253. fTransientWindow = window->uptr;
  254. if (UIExporter* const ui = fUI.get())
  255. ui->setWindowTransientWinId(window->uptr);
  256. return true;
  257. }
  258. void suggestTitle(const char* const title)
  259. {
  260. if (! fIsFloating)
  261. return;
  262. fWindowTitle = title;
  263. if (UIExporter* const ui = fUI.get())
  264. ui->setWindowTitle(title);
  265. }
  266. bool show()
  267. {
  268. if (fUI == nullptr)
  269. createUI();
  270. if (fIsFloating)
  271. fUI->setWindowVisible(true);
  272. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  273. fUI->addIdleCallbackForVST3(this, 16);
  274. #endif
  275. fCallbackRegistered = true;
  276. return true;
  277. }
  278. bool hide()
  279. {
  280. if (UIExporter* const ui = fUI.get())
  281. {
  282. ui->setWindowVisible(false);
  283. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  284. ui->removeIdleCallbackForVST3(this);
  285. #endif
  286. fCallbackRegistered = false;
  287. }
  288. return true;
  289. }
  290. // ----------------------------------------------------------------------------------------------------------------
  291. private:
  292. // Plugin and UI
  293. PluginExporter& fPlugin;
  294. ClapEventQueue* const fPluinEventQueue;
  295. ClapEventQueue::Queue& fEventQueue;
  296. ClapEventQueue::CachedParameters& fCachedParameters;
  297. ScopedPointer<UIExporter> fUI;
  298. const bool fIsFloating;
  299. // Temporary data
  300. bool fCallbackRegistered;
  301. double fScaleFactor;
  302. uintptr_t fParentWindow;
  303. uintptr_t fTransientWindow;
  304. String fWindowTitle;
  305. // ----------------------------------------------------------------------------------------------------------------
  306. void createUI()
  307. {
  308. DISTRHO_SAFE_ASSERT_RETURN(fUI == nullptr,);
  309. fUI = new UIExporter(this,
  310. fParentWindow,
  311. fPlugin.getSampleRate(),
  312. editParameterCallback,
  313. setParameterCallback,
  314. setStateCallback,
  315. sendNoteCallback,
  316. setSizeCallback,
  317. fileRequestCallback,
  318. d_nextBundlePath,
  319. fPlugin.getInstancePointer(),
  320. fScaleFactor);
  321. if (fIsFloating)
  322. {
  323. if (fWindowTitle.isNotEmpty())
  324. fUI->setWindowTitle(fWindowTitle);
  325. if (fTransientWindow != 0)
  326. fUI->setWindowTransientWinId(fTransientWindow);
  327. }
  328. }
  329. void idleCallback() override
  330. {
  331. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  332. if (UIExporter* const ui = fUI.get())
  333. {
  334. ui->idleForVST3();
  335. for (uint i=0; i<fCachedParameters.numParams; ++i)
  336. {
  337. if (fCachedParameters.changed[i])
  338. {
  339. fCachedParameters.changed[i] = false;
  340. ui->parameterChanged(i, fCachedParameters.values[i]);
  341. }
  342. }
  343. }
  344. #endif
  345. }
  346. // ----------------------------------------------------------------------------------------------------------------
  347. // DPF callbacks
  348. void editParameter(const uint32_t rindex, const bool started) const
  349. {
  350. const ClapEventQueue::Event ev = {
  351. started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureBegin,
  352. rindex, 0.f
  353. };
  354. fEventQueue.addEventFromUI(ev);
  355. }
  356. static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
  357. {
  358. static_cast<ClapUI*>(ptr)->editParameter(rindex, started);
  359. }
  360. void setParameterValue(const uint32_t rindex, const float value)
  361. {
  362. const ClapEventQueue::Event ev = {
  363. ClapEventQueue::kEventParamSet,
  364. rindex, value
  365. };
  366. fEventQueue.addEventFromUI(ev);
  367. }
  368. static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
  369. {
  370. static_cast<ClapUI*>(ptr)->setParameterValue(rindex, value);
  371. }
  372. void setSizeFromPlugin(uint, uint)
  373. {
  374. }
  375. static void setSizeCallback(void* const ptr, const uint width, const uint height)
  376. {
  377. static_cast<ClapUI*>(ptr)->setSizeFromPlugin(width, height);
  378. }
  379. #if DISTRHO_PLUGIN_WANT_STATE
  380. void setState(const char* const key, const char* const value)
  381. {
  382. fPluinEventQueue->setStateFromUI(key, value);
  383. }
  384. static void setStateCallback(void* const ptr, const char* key, const char* value)
  385. {
  386. static_cast<ClapUI*>(ptr)->setState(key, value);
  387. }
  388. #endif
  389. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  390. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  391. {
  392. }
  393. static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
  394. {
  395. static_cast<ClapUI*>(ptr)->sendNote(channel, note, velocity);
  396. }
  397. #endif
  398. bool fileRequest(const char*)
  399. {
  400. return true;
  401. }
  402. static bool fileRequestCallback(void* const ptr, const char* const key)
  403. {
  404. return static_cast<ClapUI*>(ptr)->fileRequest(key);
  405. }
  406. };
  407. // --------------------------------------------------------------------------------------------------------------------
  408. #endif // DISTRHO_PLUGIN_HAS_UI
  409. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  410. static constexpr const writeMidiFunc writeMidiCallback = nullptr;
  411. #endif
  412. #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  413. static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
  414. #endif
  415. #if ! DISTRHO_PLUGIN_WANT_STATE
  416. static constexpr const updateStateValueFunc updateStateValueCallback = nullptr;
  417. #endif
  418. // --------------------------------------------------------------------------------------------------------------------
  419. /**
  420. * CLAP plugin class.
  421. */
  422. class PluginCLAP : ClapEventQueue
  423. {
  424. public:
  425. PluginCLAP(const clap_host_t* const host)
  426. : fPlugin(this,
  427. writeMidiCallback,
  428. requestParameterValueChangeCallback,
  429. updateStateValueCallback),
  430. fHost(host),
  431. fOutputEvents(nullptr)
  432. {
  433. fCachedParameters.setup(fPlugin.getParameterCount());
  434. }
  435. // ----------------------------------------------------------------------------------------------------------------
  436. // core
  437. bool init()
  438. {
  439. if (!clap_version_is_compatible(fHost->clap_version))
  440. return false;
  441. // TODO check host features
  442. return true;
  443. }
  444. void activate(const double sampleRate, const uint32_t maxFramesCount)
  445. {
  446. fPlugin.setSampleRate(sampleRate, true);
  447. fPlugin.setBufferSize(maxFramesCount, true);
  448. fPlugin.activate();
  449. }
  450. void deactivate()
  451. {
  452. fPlugin.deactivate();
  453. }
  454. bool process(const clap_process_t* const process)
  455. {
  456. #if DISTRHO_PLUGIN_HAS_UI
  457. if (const clap_output_events_t* const outputEvents = process->out_events)
  458. {
  459. const MutexTryLocker cmtl(fEventQueue.lock);
  460. if (cmtl.wasLocked())
  461. {
  462. // reuse the same struct for gesture and parameters, they are compatible up to where it matters
  463. clap_event_param_value_t clapEvent = {
  464. { 0, 0, 0, 0, CLAP_EVENT_IS_LIVE },
  465. 0, nullptr, 0, 0, 0, 0, 0.0
  466. };
  467. for (uint32_t i=0; i<fEventQueue.used; ++i)
  468. {
  469. const Event& event(fEventQueue.events[i]);
  470. switch (event.type)
  471. {
  472. case kEventGestureBegin:
  473. clapEvent.header.size = sizeof(clap_event_param_gesture_t);
  474. clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_BEGIN;
  475. clapEvent.param_id = event.index;
  476. break;
  477. case kEventGestureEnd:
  478. clapEvent.header.size = sizeof(clap_event_param_gesture_t);
  479. clapEvent.header.type = CLAP_EVENT_PARAM_GESTURE_END;
  480. clapEvent.param_id = event.index;
  481. break;
  482. case kEventParamSet:
  483. clapEvent.header.size = sizeof(clap_event_param_value_t);
  484. clapEvent.header.type = CLAP_EVENT_PARAM_VALUE;
  485. clapEvent.param_id = event.index;
  486. clapEvent.value = event.value;
  487. fPlugin.setParameterValue(event.index, event.value);
  488. break;
  489. default:
  490. continue;
  491. }
  492. outputEvents->try_push(outputEvents, &clapEvent.header);
  493. }
  494. fEventQueue.used = 0;
  495. }
  496. }
  497. #endif
  498. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  499. if (const clap_event_transport_t* const transport = process->transport)
  500. {
  501. fTimePosition.playing = transport->flags & CLAP_TRANSPORT_IS_PLAYING;
  502. fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0;
  503. if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO)
  504. fTimePosition.bbt.beatsPerMinute = transport->tempo;
  505. else
  506. fTimePosition.bbt.beatsPerMinute = 120.0;
  507. // ticksPerBeat is not possible with CLAP
  508. fTimePosition.bbt.ticksPerBeat = 1920.0;
  509. // TODO verify if this works or makes any sense
  510. if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE))
  511. {
  512. const double ppqPos = std::abs(transport->song_pos_beats);
  513. const int ppqPerBar = transport->tsig_num * 4 / transport->tsig_denom;
  514. const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * transport->tsig_num;
  515. const double rest = std::fmod(barBeats, 1.0);
  516. fTimePosition.bbt.valid = true;
  517. fTimePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
  518. fTimePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
  519. fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat;
  520. fTimePosition.bbt.beatsPerBar = transport->tsig_num;
  521. fTimePosition.bbt.beatType = transport->tsig_denom;
  522. if (transport->song_pos_beats < 0.0)
  523. {
  524. --fTimePosition.bbt.bar;
  525. fTimePosition.bbt.beat = transport->tsig_num - fTimePosition.bbt.beat + 1;
  526. fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1;
  527. }
  528. }
  529. else
  530. {
  531. fTimePosition.bbt.valid = false;
  532. fTimePosition.bbt.bar = 1;
  533. fTimePosition.bbt.beat = 1;
  534. fTimePosition.bbt.tick = 0.0;
  535. fTimePosition.bbt.beatsPerBar = 4.0f;
  536. fTimePosition.bbt.beatType = 4.0f;
  537. }
  538. fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
  539. fTimePosition.bbt.beatsPerBar*
  540. (fTimePosition.bbt.bar-1);
  541. }
  542. else
  543. {
  544. fTimePosition.playing = false;
  545. fTimePosition.frame = 0;
  546. fTimePosition.bbt.valid = false;
  547. fTimePosition.bbt.beatsPerMinute = 120.0;
  548. fTimePosition.bbt.bar = 1;
  549. fTimePosition.bbt.beat = 1;
  550. fTimePosition.bbt.tick = 0.0;
  551. fTimePosition.bbt.beatsPerBar = 4.f;
  552. fTimePosition.bbt.beatType = 4.f;
  553. fTimePosition.bbt.barStartTick = 0;
  554. }
  555. fPlugin.setTimePosition(fTimePosition);
  556. #endif
  557. if (const clap_input_events_t* const inputEvents = process->in_events)
  558. {
  559. if (const uint32_t len = inputEvents->size(inputEvents))
  560. {
  561. for (uint32_t i=0; i<len; ++i)
  562. {
  563. const clap_event_header_t* const event = inputEvents->get(inputEvents, i);
  564. // event->time
  565. switch (event->type)
  566. {
  567. case CLAP_EVENT_NOTE_ON:
  568. case CLAP_EVENT_NOTE_OFF:
  569. case CLAP_EVENT_NOTE_CHOKE:
  570. case CLAP_EVENT_NOTE_END:
  571. case CLAP_EVENT_NOTE_EXPRESSION:
  572. break;
  573. case CLAP_EVENT_PARAM_VALUE:
  574. DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
  575. event->size, sizeof(clap_event_param_value));
  576. setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
  577. break;
  578. case CLAP_EVENT_PARAM_MOD:
  579. case CLAP_EVENT_PARAM_GESTURE_BEGIN:
  580. case CLAP_EVENT_PARAM_GESTURE_END:
  581. case CLAP_EVENT_TRANSPORT:
  582. case CLAP_EVENT_MIDI:
  583. case CLAP_EVENT_MIDI_SYSEX:
  584. case CLAP_EVENT_MIDI2:
  585. break;
  586. }
  587. }
  588. }
  589. }
  590. if (const uint32_t frames = process->frames_count)
  591. {
  592. // TODO multi-port bus stuff
  593. DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_inputs_count == 0 || process->audio_inputs_count == 1,
  594. process->audio_inputs_count, false);
  595. DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_outputs_count == 0 || process->audio_outputs_count == 1,
  596. process->audio_outputs_count, false);
  597. const float** inputs = process->audio_inputs != nullptr
  598. ? const_cast<const float**>(process->audio_inputs[0].data32)
  599. : nullptr;
  600. /**/ float** outputs = process->audio_outputs != nullptr
  601. ? process->audio_outputs[0].data32
  602. : nullptr;
  603. fOutputEvents = process->out_events;
  604. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  605. fPlugin.run(inputs, outputs, frames, nullptr, 0);
  606. #else
  607. fPlugin.run(inputs, outputs, frames);
  608. #endif
  609. // TODO set last frame
  610. flushParameters(nullptr, process->out_events);
  611. fOutputEvents = nullptr;
  612. }
  613. return true;
  614. }
  615. // ----------------------------------------------------------------------------------------------------------------
  616. // parameters
  617. uint32_t getParameterCount() const
  618. {
  619. return fPlugin.getParameterCount();
  620. }
  621. bool getParameterInfo(const uint32_t index, clap_param_info_t* const info) const
  622. {
  623. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  624. if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass)
  625. {
  626. info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE;
  627. std::strcpy(info->name, "Bypass");
  628. std::strcpy(info->module, "dpf_bypass");
  629. }
  630. else
  631. {
  632. const uint32_t hints = fPlugin.getParameterHints(index);
  633. const uint32_t groupId = fPlugin.getParameterGroupId(index);
  634. info->flags = 0;
  635. if (hints & kParameterIsOutput)
  636. info->flags |= CLAP_PARAM_IS_READONLY;
  637. else if (hints & kParameterIsAutomatable)
  638. info->flags |= CLAP_PARAM_IS_AUTOMATABLE;
  639. if (hints & (kParameterIsBoolean|kParameterIsInteger))
  640. info->flags |= CLAP_PARAM_IS_STEPPED;
  641. DISTRHO_NAMESPACE::strncpy(info->name, fPlugin.getParameterName(index), CLAP_NAME_SIZE);
  642. uint wrtn;
  643. if (groupId != kPortGroupNone)
  644. {
  645. const PortGroupWithId& portGroup(fPlugin.getPortGroupById(groupId));
  646. strncpy(info->module, portGroup.symbol, CLAP_PATH_SIZE / 2);
  647. info->module[CLAP_PATH_SIZE / 2] = '\0';
  648. wrtn = std::strlen(info->module);
  649. info->module[wrtn++] = '/';
  650. }
  651. else
  652. {
  653. wrtn = 0;
  654. }
  655. DISTRHO_NAMESPACE::strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn);
  656. }
  657. info->id = index;
  658. info->cookie = nullptr;
  659. info->min_value = ranges.min;
  660. info->max_value = ranges.max;
  661. info->default_value = ranges.def;
  662. return true;
  663. }
  664. bool getParameterValue(const clap_id param_id, double* const value) const
  665. {
  666. *value = fPlugin.getParameterValue(param_id);
  667. return true;
  668. }
  669. bool getParameterStringForValue(const clap_id param_id, double value, char* const display, const uint32_t size) const
  670. {
  671. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
  672. const ParameterRanges& ranges(fPlugin.getParameterRanges(param_id));
  673. const uint32_t hints = fPlugin.getParameterHints(param_id);
  674. if (hints & kParameterIsBoolean)
  675. {
  676. const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
  677. value = value > midRange ? ranges.max : ranges.min;
  678. }
  679. else if (hints & kParameterIsInteger)
  680. {
  681. value = std::round(value);
  682. }
  683. for (uint32_t i=0; i < enumValues.count; ++i)
  684. {
  685. if (d_isEqual(static_cast<double>(enumValues.values[i].value), value))
  686. {
  687. DISTRHO_NAMESPACE::strncpy(display, enumValues.values[i].label, size);
  688. return true;
  689. }
  690. }
  691. if (hints & kParameterIsInteger)
  692. snprintf_i32(display, value, size);
  693. else
  694. snprintf_f32(display, value, size);
  695. return true;
  696. }
  697. bool getParameterValueForString(const clap_id param_id, const char* const display, double* const value) const
  698. {
  699. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(param_id));
  700. const bool isInteger = fPlugin.isParameterInteger(param_id);
  701. for (uint32_t i=0; i < enumValues.count; ++i)
  702. {
  703. if (std::strcmp(display, enumValues.values[i].label) == 0)
  704. {
  705. *value = enumValues.values[i].value;
  706. return true;
  707. }
  708. }
  709. if (isInteger)
  710. *value = std::atoi(display);
  711. else
  712. *value = std::atof(display);
  713. return true;
  714. }
  715. void setParameterValueFromEvent(const clap_event_param_value* const param)
  716. {
  717. fCachedParameters.values[param->param_id] = param->value;
  718. fCachedParameters.changed[param->param_id] = true;
  719. fPlugin.setParameterValue(param->param_id, param->value);
  720. }
  721. void flushParameters(const clap_input_events_t* const in, const clap_output_events_t* const out)
  722. {
  723. if (const uint32_t len = in != nullptr ? in->size(in) : 0)
  724. {
  725. for (uint32_t i=0; i<len; ++i)
  726. {
  727. const clap_event_header_t* const event = in->get(in, i);
  728. if (event->type != CLAP_EVENT_PARAM_VALUE)
  729. continue;
  730. DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
  731. event->size, sizeof(clap_event_param_value));
  732. setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
  733. }
  734. }
  735. if (out != nullptr)
  736. {
  737. clap_event_param_value_t clapEvent = {
  738. { sizeof(clap_event_param_value_t), 0, 0, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_IS_LIVE },
  739. 0, nullptr, 0, 0, 0, 0, 0.0
  740. };
  741. float value;
  742. for (uint i=0; i<fCachedParameters.numParams; ++i)
  743. {
  744. if (fPlugin.isParameterOutputOrTrigger(i))
  745. {
  746. value = fPlugin.getParameterValue(i);
  747. if (d_isEqual(fCachedParameters.values[i], value))
  748. continue;
  749. fCachedParameters.values[i] = value;
  750. fCachedParameters.changed[i] = true;
  751. clapEvent.param_id = i;
  752. clapEvent.value = value;
  753. out->try_push(out, &clapEvent.header);
  754. }
  755. }
  756. }
  757. }
  758. // ----------------------------------------------------------------------------------------------------------------
  759. // gui
  760. #if DISTRHO_PLUGIN_HAS_UI
  761. bool createUI(const bool isFloating)
  762. {
  763. fUI = new ClapUI(fPlugin, this, isFloating);
  764. return true;
  765. }
  766. void destroyUI()
  767. {
  768. fUI = nullptr;
  769. }
  770. ClapUI* getUI() const noexcept
  771. {
  772. return fUI.get();
  773. }
  774. #endif
  775. #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_STATE
  776. void setStateFromUI(const char* const key, const char* const value) override
  777. {
  778. fPlugin.setState(key, value);
  779. // TODO check if we want to save this key, and save it
  780. }
  781. #endif
  782. // ----------------------------------------------------------------------------------------------------------------
  783. private:
  784. // Plugin and UI
  785. PluginExporter fPlugin;
  786. #if DISTRHO_PLUGIN_HAS_UI
  787. ScopedPointer<ClapUI> fUI;
  788. #endif
  789. // CLAP stuff
  790. const clap_host_t* const fHost;
  791. const clap_output_events_t* fOutputEvents;
  792. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  793. TimePosition fTimePosition;
  794. #endif
  795. // ----------------------------------------------------------------------------------------------------------------
  796. // DPF callbacks
  797. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  798. bool writeMidi(const MidiEvent&)
  799. {
  800. return true;
  801. }
  802. static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
  803. {
  804. return static_cast<PluginCLAP*>(ptr)->writeMidi(midiEvent);
  805. }
  806. #endif
  807. #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  808. bool requestParameterValueChange(uint32_t, float)
  809. {
  810. return true;
  811. }
  812. static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
  813. {
  814. return static_cast<PluginCLAP*>(ptr)->requestParameterValueChange(index, value);
  815. }
  816. #endif
  817. #if DISTRHO_PLUGIN_WANT_STATE
  818. bool updateState(const char*, const char*)
  819. {
  820. return true;
  821. }
  822. static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
  823. {
  824. return static_cast<PluginCLAP*>(ptr)->updateState(key, value);
  825. }
  826. #endif
  827. };
  828. // --------------------------------------------------------------------------------------------------------------------
  829. static ScopedPointer<PluginExporter> sPlugin;
  830. // --------------------------------------------------------------------------------------------------------------------
  831. // plugin gui
  832. #if DISTRHO_PLUGIN_HAS_UI
  833. static const char* const kSupportedAPIs[] = {
  834. #if defined(DISTRHO_OS_WINDOWS)
  835. CLAP_WINDOW_API_WIN32,
  836. #elif defined(DISTRHO_OS_MAC)
  837. CLAP_WINDOW_API_COCOA,
  838. #else
  839. CLAP_WINDOW_API_X11,
  840. #endif
  841. };
  842. // TODO DPF external UI
  843. static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
  844. {
  845. for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
  846. {
  847. if (std::strcmp(kSupportedAPIs[i], api) == 0)
  848. return true;
  849. }
  850. return false;
  851. }
  852. // TODO DPF external UI
  853. static bool clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
  854. {
  855. *api = kSupportedAPIs[0];
  856. *is_floating = false;
  857. return true;
  858. }
  859. static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
  860. {
  861. for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
  862. {
  863. if (std::strcmp(kSupportedAPIs[i], api) == 0)
  864. {
  865. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  866. return instance->createUI(is_floating);
  867. }
  868. }
  869. return false;
  870. }
  871. static void clap_gui_destroy(const clap_plugin_t* const plugin)
  872. {
  873. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  874. instance->destroyUI();
  875. }
  876. static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
  877. {
  878. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  879. ClapUI* const gui = instance->getUI();
  880. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  881. return gui->setScaleFactor(scale);
  882. }
  883. static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
  884. {
  885. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  886. ClapUI* const gui = instance->getUI();
  887. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  888. return gui->getSize(width, height);
  889. }
  890. static bool clap_gui_can_resize(const clap_plugin_t* const plugin)
  891. {
  892. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  893. ClapUI* const gui = instance->getUI();
  894. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  895. return gui->canResize();
  896. }
  897. static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
  898. {
  899. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  900. ClapUI* const gui = instance->getUI();
  901. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  902. return gui->getResizeHints(hints);
  903. }
  904. static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
  905. {
  906. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  907. ClapUI* const gui = instance->getUI();
  908. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  909. return gui->adjustSize(width, height);
  910. }
  911. static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
  912. {
  913. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  914. ClapUI* const gui = instance->getUI();
  915. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  916. return gui->setSizeFromHost(width, height);
  917. }
  918. static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
  919. {
  920. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  921. ClapUI* const gui = instance->getUI();
  922. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  923. return gui->setParent(window);
  924. }
  925. static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
  926. {
  927. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  928. ClapUI* const gui = instance->getUI();
  929. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  930. return gui->setTransient(window);
  931. }
  932. static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
  933. {
  934. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  935. ClapUI* const gui = instance->getUI();
  936. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr,);
  937. return gui->suggestTitle(title);
  938. }
  939. static bool clap_gui_show(const clap_plugin_t* const plugin)
  940. {
  941. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  942. ClapUI* const gui = instance->getUI();
  943. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  944. return gui->show();
  945. }
  946. static bool clap_gui_hide(const clap_plugin_t* const plugin)
  947. {
  948. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  949. ClapUI* const gui = instance->getUI();
  950. DISTRHO_SAFE_ASSERT_RETURN(gui != nullptr, false);
  951. return gui->hide();
  952. }
  953. static const clap_plugin_gui_t clap_plugin_gui = {
  954. clap_gui_is_api_supported,
  955. clap_gui_get_preferred_api,
  956. clap_gui_create,
  957. clap_gui_destroy,
  958. clap_gui_set_scale,
  959. clap_gui_get_size,
  960. clap_gui_can_resize,
  961. clap_gui_get_resize_hints,
  962. clap_gui_adjust_size,
  963. clap_gui_set_size,
  964. clap_gui_set_parent,
  965. clap_gui_set_transient,
  966. clap_gui_suggest_title,
  967. clap_gui_show,
  968. clap_gui_hide
  969. };
  970. #endif // DISTRHO_PLUGIN_HAS_UI
  971. // --------------------------------------------------------------------------------------------------------------------
  972. // plugin audio ports
  973. static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t*, const bool is_input)
  974. {
  975. return (is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS) != 0 ? 1 : 0;
  976. }
  977. static bool clap_plugin_audio_ports_get(const clap_plugin_t* /* const plugin */,
  978. const uint32_t index,
  979. const bool is_input,
  980. clap_audio_port_info_t* const info)
  981. {
  982. const uint32_t maxPortCount = is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
  983. DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < maxPortCount, index, maxPortCount, false);
  984. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  985. // PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  986. // TODO use groups
  987. AudioPortWithBusId& audioPort(sPlugin->getAudioPort(is_input, index));
  988. info->id = index;
  989. DISTRHO_NAMESPACE::strncpy(info->name, audioPort.name, CLAP_NAME_SIZE);
  990. // TODO bus stuff
  991. info->flags = CLAP_AUDIO_PORT_IS_MAIN;
  992. info->channel_count = maxPortCount;
  993. // TODO CV
  994. // info->port_type = audioPort.hints & kAudioPortIsCV ? CLAP_PORT_CV : nullptr;
  995. info->port_type = nullptr;
  996. info->in_place_pair = DISTRHO_PLUGIN_NUM_INPUTS == DISTRHO_PLUGIN_NUM_OUTPUTS ? index : CLAP_INVALID_ID;
  997. return true;
  998. #else
  999. return false;
  1000. #endif
  1001. }
  1002. static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
  1003. clap_plugin_audio_ports_count,
  1004. clap_plugin_audio_ports_get
  1005. };
  1006. // --------------------------------------------------------------------------------------------------------------------
  1007. // plugin parameters
  1008. static uint32_t clap_plugin_params_count(const clap_plugin_t* const plugin)
  1009. {
  1010. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1011. return instance->getParameterCount();
  1012. }
  1013. static bool clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info)
  1014. {
  1015. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1016. return instance->getParameterInfo(index, info);
  1017. }
  1018. static bool clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value)
  1019. {
  1020. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1021. return instance->getParameterValue(param_id, value);
  1022. }
  1023. static bool 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)
  1024. {
  1025. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1026. return instance->getParameterStringForValue(param_id, value, display, size);
  1027. }
  1028. static bool clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value)
  1029. {
  1030. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1031. return instance->getParameterValueForString(param_id, display, value);
  1032. }
  1033. static void clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out)
  1034. {
  1035. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1036. return instance->flushParameters(in, out);
  1037. }
  1038. static const clap_plugin_params_t clap_plugin_params = {
  1039. clap_plugin_params_count,
  1040. clap_plugin_params_get_info,
  1041. clap_plugin_params_get_value,
  1042. clap_plugin_params_value_to_text,
  1043. clap_plugin_params_text_to_value,
  1044. clap_plugin_params_flush
  1045. };
  1046. // --------------------------------------------------------------------------------------------------------------------
  1047. // plugin
  1048. static bool clap_plugin_init(const clap_plugin_t* const plugin)
  1049. {
  1050. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1051. return instance->init();
  1052. }
  1053. static void clap_plugin_destroy(const clap_plugin_t* const plugin)
  1054. {
  1055. delete static_cast<PluginCLAP*>(plugin->plugin_data);
  1056. std::free(const_cast<clap_plugin_t*>(plugin));
  1057. }
  1058. static bool clap_plugin_activate(const clap_plugin_t* const plugin,
  1059. const double sample_rate,
  1060. uint32_t,
  1061. const uint32_t max_frames_count)
  1062. {
  1063. d_nextBufferSize = max_frames_count;
  1064. d_nextSampleRate = sample_rate;
  1065. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1066. instance->activate(sample_rate, max_frames_count);
  1067. return true;
  1068. }
  1069. static void clap_plugin_deactivate(const clap_plugin_t* const plugin)
  1070. {
  1071. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1072. instance->deactivate();
  1073. }
  1074. static bool clap_plugin_start_processing(const clap_plugin_t*)
  1075. {
  1076. // nothing to do
  1077. return true;
  1078. }
  1079. static void clap_plugin_stop_processing(const clap_plugin_t*)
  1080. {
  1081. // nothing to do
  1082. }
  1083. static void clap_plugin_reset(const clap_plugin_t*)
  1084. {
  1085. // nothing to do
  1086. }
  1087. static clap_process_status clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process)
  1088. {
  1089. PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
  1090. return instance->process(process) ? CLAP_PROCESS_CONTINUE : CLAP_PROCESS_ERROR;
  1091. }
  1092. static const void* clap_plugin_get_extension(const clap_plugin_t*, const char* const id)
  1093. {
  1094. if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
  1095. return &clap_plugin_audio_ports;
  1096. if (std::strcmp(id, CLAP_EXT_PARAMS) == 0)
  1097. return &clap_plugin_params;
  1098. #if DISTRHO_PLUGIN_HAS_UI
  1099. if (std::strcmp(id, CLAP_EXT_GUI) == 0)
  1100. return &clap_plugin_gui;
  1101. #endif
  1102. return nullptr;
  1103. }
  1104. static void clap_plugin_on_main_thread(const clap_plugin_t*)
  1105. {
  1106. // nothing to do
  1107. }
  1108. // --------------------------------------------------------------------------------------------------------------------
  1109. // plugin factory
  1110. static uint32_t clap_get_plugin_count(const clap_plugin_factory_t*)
  1111. {
  1112. return 1;
  1113. }
  1114. static const clap_plugin_descriptor_t* clap_get_plugin_descriptor(const clap_plugin_factory_t*, const uint32_t index)
  1115. {
  1116. DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr);
  1117. static const char* features[] = {
  1118. #ifdef DISTRHO_PLUGIN_CLAP_FEATURES
  1119. DISTRHO_PLUGIN_CLAP_FEATURES,
  1120. #elif DISTRHO_PLUGIN_IS_SYNTH
  1121. "instrument",
  1122. #endif
  1123. nullptr
  1124. };
  1125. static const clap_plugin_descriptor_t descriptor = {
  1126. CLAP_VERSION,
  1127. sPlugin->getLabel(),
  1128. sPlugin->getName(),
  1129. sPlugin->getMaker(),
  1130. // TODO url
  1131. "",
  1132. // TODO manual url
  1133. "",
  1134. // TODO support url
  1135. "",
  1136. // TODO version string
  1137. "",
  1138. sPlugin->getDescription(),
  1139. features
  1140. };
  1141. return &descriptor;
  1142. }
  1143. static const clap_plugin_t* clap_create_plugin(const clap_plugin_factory_t* const factory,
  1144. const clap_host_t* const host,
  1145. const char*)
  1146. {
  1147. clap_plugin_t* const pluginptr = static_cast<clap_plugin_t*>(std::malloc(sizeof(clap_plugin_t)));
  1148. DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr);
  1149. // default early values
  1150. if (d_nextBufferSize == 0)
  1151. d_nextBufferSize = 1024;
  1152. if (d_nextSampleRate <= 0.0)
  1153. d_nextSampleRate = 44100.0;
  1154. d_nextCanRequestParameterValueChanges = true;
  1155. const clap_plugin_t plugin = {
  1156. clap_get_plugin_descriptor(factory, 0),
  1157. new PluginCLAP(host),
  1158. clap_plugin_init,
  1159. clap_plugin_destroy,
  1160. clap_plugin_activate,
  1161. clap_plugin_deactivate,
  1162. clap_plugin_start_processing,
  1163. clap_plugin_stop_processing,
  1164. clap_plugin_reset,
  1165. clap_plugin_process,
  1166. clap_plugin_get_extension,
  1167. clap_plugin_on_main_thread
  1168. };
  1169. std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t));
  1170. return pluginptr;
  1171. }
  1172. static const clap_plugin_factory_t clap_plugin_factory = {
  1173. clap_get_plugin_count,
  1174. clap_get_plugin_descriptor,
  1175. clap_create_plugin
  1176. };
  1177. // --------------------------------------------------------------------------------------------------------------------
  1178. // plugin entry
  1179. static bool clap_plugin_entry_init(const char* const plugin_path)
  1180. {
  1181. static String bundlePath;
  1182. bundlePath = plugin_path;
  1183. d_nextBundlePath = bundlePath.buffer();
  1184. // init dummy plugin
  1185. if (sPlugin == nullptr)
  1186. {
  1187. // set valid but dummy values
  1188. d_nextBufferSize = 512;
  1189. d_nextSampleRate = 44100.0;
  1190. d_nextPluginIsDummy = true;
  1191. d_nextCanRequestParameterValueChanges = true;
  1192. // Create dummy plugin to get data from
  1193. sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
  1194. // unset
  1195. d_nextBufferSize = 0;
  1196. d_nextSampleRate = 0.0;
  1197. d_nextPluginIsDummy = false;
  1198. d_nextCanRequestParameterValueChanges = false;
  1199. }
  1200. return true;
  1201. }
  1202. static void clap_plugin_entry_deinit(void)
  1203. {
  1204. sPlugin = nullptr;
  1205. }
  1206. static const void* clap_plugin_entry_get_factory(const char* const factory_id)
  1207. {
  1208. if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0)
  1209. return &clap_plugin_factory;
  1210. return nullptr;
  1211. }
  1212. static const clap_plugin_entry_t clap_plugin_entry = {
  1213. CLAP_VERSION,
  1214. clap_plugin_entry_init,
  1215. clap_plugin_entry_deinit,
  1216. clap_plugin_entry_get_factory
  1217. };
  1218. // --------------------------------------------------------------------------------------------------------------------
  1219. END_NAMESPACE_DISTRHO
  1220. // --------------------------------------------------------------------------------------------------------------------
  1221. DISTRHO_PLUGIN_EXPORT
  1222. const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry;
  1223. // --------------------------------------------------------------------------------------------------------------------