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.

612 lines
18KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoDetails.hpp"
  17. #include "src/DistrhoPluginChecks.h"
  18. #include "src/DistrhoDefines.h"
  19. #include <cstddef>
  20. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  21. # include <cstdint>
  22. #else
  23. # include <stdint.h>
  24. #endif
  25. #if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC)
  26. # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION
  27. # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION)
  28. # define x_fib_add_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_add_recent)
  29. # define x_fib_cfg_buttons DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_buttons)
  30. # define x_fib_cfg_filter_callback DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_cfg_filter_callback)
  31. # define x_fib_close DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_close)
  32. # define x_fib_configure DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_configure)
  33. # define x_fib_filename DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_filename)
  34. # define x_fib_free_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_free_recent)
  35. # define x_fib_handle_events DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_handle_events)
  36. # define x_fib_load_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_load_recent)
  37. # define x_fib_recent_at DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_at)
  38. # define x_fib_recent_count DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_count)
  39. # define x_fib_recent_file DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_recent_file)
  40. # define x_fib_save_recent DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_save_recent)
  41. # define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show)
  42. # define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status)
  43. # define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED
  44. # define FILE_BROWSER_DIALOG_NAMESPACE DISTRHO_NAMESPACE
  45. # define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE
  46. START_NAMESPACE_DISTRHO
  47. # include "../extra/FileBrowserDialogImpl.hpp"
  48. END_NAMESPACE_DISTRHO
  49. # include "../extra/FileBrowserDialogImpl.cpp"
  50. #endif
  51. #if DISTRHO_UI_USE_WEBVIEW && !defined(DISTRHO_OS_MAC)
  52. # define DISTRHO_WEB_VIEW_HPP_INCLUDED
  53. # define WEB_VIEW_NAMESPACE DISTRHO_NAMESPACE
  54. # define WEB_VIEW_DISTRHO_NAMESPACE
  55. START_NAMESPACE_DISTRHO
  56. # include "../extra/WebViewImpl.hpp"
  57. END_NAMESPACE_DISTRHO
  58. # include "../extra/WebViewImpl.cpp"
  59. #endif
  60. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  61. # if defined(DISTRHO_OS_WINDOWS)
  62. # include <winsock2.h>
  63. # include <windows.h>
  64. # elif defined(HAVE_X11)
  65. # include <X11/Xresource.h>
  66. # endif
  67. #else
  68. # include "src/TopLevelWidgetPrivateData.hpp"
  69. # include "src/WindowPrivateData.hpp"
  70. #endif
  71. #include "DistrhoUIPrivateData.hpp"
  72. START_NAMESPACE_DISTRHO
  73. /* ------------------------------------------------------------------------------------------------------------
  74. * Static data, see DistrhoUIInternal.hpp */
  75. const char* g_nextBundlePath = nullptr;
  76. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  77. uintptr_t g_nextWindowId = 0;
  78. double g_nextScaleFactor = 1.0;
  79. #endif
  80. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  81. /* ------------------------------------------------------------------------------------------------------------
  82. * get global scale factor */
  83. #ifdef DISTRHO_OS_MAC
  84. double getDesktopScaleFactor(uintptr_t parentWindowHandle);
  85. #else
  86. static double getDesktopScaleFactor(const uintptr_t parentWindowHandle)
  87. {
  88. // allow custom scale for testing
  89. if (const char* const scale = getenv("DPF_SCALE_FACTOR"))
  90. return std::max(1.0, std::atof(scale));
  91. #if defined(DISTRHO_OS_WINDOWS)
  92. if (const HMODULE Shcore = LoadLibraryA("Shcore.dll"))
  93. {
  94. typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*);
  95. typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*);
  96. # if defined(__GNUC__) && (__GNUC__ >= 9)
  97. # pragma GCC diagnostic push
  98. # pragma GCC diagnostic ignored "-Wcast-function-type"
  99. # endif
  100. const PFN_GetProcessDpiAwareness GetProcessDpiAwareness
  101. = (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness");
  102. const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor
  103. = (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor");
  104. # if defined(__GNUC__) && (__GNUC__ >= 9)
  105. # pragma GCC diagnostic pop
  106. # endif
  107. DWORD dpiAware = 0;
  108. DWORD scaleFactor = 100;
  109. if (GetProcessDpiAwareness && GetScaleFactorForMonitor
  110. && GetProcessDpiAwareness(nullptr, &dpiAware) == 0 && dpiAware != 0)
  111. {
  112. const HMONITOR hMon = parentWindowHandle != 0
  113. ? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY)
  114. : MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY);
  115. GetScaleFactorForMonitor(hMon, &scaleFactor);
  116. }
  117. FreeLibrary(Shcore);
  118. return static_cast<double>(scaleFactor) / 100.0;
  119. }
  120. #elif defined(HAVE_X11)
  121. ::Display* const display = XOpenDisplay(nullptr);
  122. DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0);
  123. XrmInitialize();
  124. double dpi = 96.0;
  125. if (char* const rms = XResourceManagerString(display))
  126. {
  127. if (const XrmDatabase db = XrmGetStringDatabase(rms))
  128. {
  129. char* type = nullptr;
  130. XrmValue value = {};
  131. if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)
  132. && type != nullptr
  133. && std::strcmp(type, "String") == 0
  134. && value.addr != nullptr)
  135. {
  136. char* end = nullptr;
  137. const double xftDpi = std::strtod(value.addr, &end);
  138. if (xftDpi > 0.0 && xftDpi < HUGE_VAL)
  139. dpi = xftDpi;
  140. }
  141. XrmDestroyDatabase(db);
  142. }
  143. }
  144. XCloseDisplay(display);
  145. return dpi / 96;
  146. #endif
  147. return 1.0;
  148. // might be unused
  149. (void)parentWindowHandle;
  150. }
  151. #endif // !DISTRHO_OS_MAC
  152. #endif
  153. /* ------------------------------------------------------------------------------------------------------------
  154. * UI::PrivateData special handling */
  155. UI::PrivateData* UI::PrivateData::s_nextPrivateData = nullptr;
  156. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  157. ExternalWindow::PrivateData
  158. #else
  159. PluginWindow&
  160. #endif
  161. UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const bool adjustForScaleFactor)
  162. {
  163. UI::PrivateData* const pData = s_nextPrivateData;
  164. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  165. const double scaleFactor = d_isNotZero(pData->scaleFactor) ? pData->scaleFactor : getDesktopScaleFactor(pData->winId);
  166. if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0))
  167. {
  168. width *= scaleFactor;
  169. height *= scaleFactor;
  170. }
  171. pData->window = new PluginWindow(ui, pData->app);
  172. ExternalWindow::PrivateData ewData;
  173. ewData.parentWindowHandle = pData->winId;
  174. ewData.width = width;
  175. ewData.height = height;
  176. ewData.scaleFactor = scaleFactor;
  177. ewData.title = DISTRHO_PLUGIN_NAME;
  178. ewData.isStandalone = DISTRHO_UI_IS_STANDALONE;
  179. return ewData;
  180. #else
  181. const double scaleFactor = pData->scaleFactor;
  182. if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0))
  183. {
  184. width *= scaleFactor;
  185. height *= scaleFactor;
  186. }
  187. pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, scaleFactor);
  188. // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks
  189. if (pData->callbacksPtr == nullptr)
  190. pData->window->setIgnoreIdleCallbacks();
  191. return pData->window.getObject();
  192. #endif
  193. }
  194. /* ------------------------------------------------------------------------------------------------------------
  195. * UI */
  196. UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize)
  197. : UIWidget(UI::PrivateData::createNextWindow(this,
  198. #ifdef DISTRHO_UI_DEFAULT_WIDTH
  199. width == 0 ? DISTRHO_UI_DEFAULT_WIDTH :
  200. #endif
  201. width,
  202. #ifdef DISTRHO_UI_DEFAULT_HEIGHT
  203. height == 0 ? DISTRHO_UI_DEFAULT_HEIGHT :
  204. #endif
  205. height,
  206. #ifdef DISTRHO_UI_DEFAULT_WIDTH
  207. width == 0
  208. #else
  209. false
  210. #endif
  211. )),
  212. uiData(UI::PrivateData::s_nextPrivateData)
  213. {
  214. #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  215. if (width != 0 && height != 0)
  216. {
  217. Widget::setSize(width, height);
  218. if (automaticallyScaleAndSetAsMinimumSize)
  219. setGeometryConstraints(width, height, true, true, true);
  220. }
  221. #ifdef DISTRHO_UI_DEFAULT_WIDTH
  222. else
  223. {
  224. Widget::setSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT);
  225. }
  226. #endif
  227. #else
  228. // unused
  229. (void)automaticallyScaleAndSetAsMinimumSize;
  230. #endif
  231. #if DISTRHO_UI_WEB_VIEW
  232. evaluateJS("\
  233. function editParameter(index, started){ window.webkit.messageHandlers.external.postMessage('editparam ' + index + ' ' + (started ? 1 : 0)) }\
  234. function setParameterValue(index, value){ window.webkit.messageHandlers.external.postMessage('setparam ' + index + ' ' + value) }\
  235. ");
  236. #if DISTRHO_PLUGIN_WANT_STATE
  237. evaluateJS("\
  238. function setState(key, value){ window.webkit.messageHandlers.external.postMessage('setstate ' + key + ' ' + value) }\
  239. ");
  240. #endif
  241. #endif
  242. }
  243. UI::~UI()
  244. {
  245. }
  246. /* ------------------------------------------------------------------------------------------------------------
  247. * Host state */
  248. bool UI::isResizable() const noexcept
  249. {
  250. #if DISTRHO_UI_USER_RESIZABLE
  251. # if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  252. return true;
  253. # else
  254. return uiData->window->isResizable();
  255. # endif
  256. #else
  257. return false;
  258. #endif
  259. }
  260. uint UI::getBackgroundColor() const noexcept
  261. {
  262. return uiData->bgColor;
  263. }
  264. uint UI::getForegroundColor() const noexcept
  265. {
  266. return uiData->fgColor;
  267. }
  268. double UI::getSampleRate() const noexcept
  269. {
  270. return uiData->sampleRate;
  271. }
  272. const char* UI::getBundlePath() const noexcept
  273. {
  274. return uiData->bundlePath;
  275. }
  276. void UI::editParameter(uint32_t index, bool started)
  277. {
  278. uiData->editParamCallback(index + uiData->parameterOffset, started);
  279. }
  280. void UI::setParameterValue(uint32_t index, float value)
  281. {
  282. uiData->setParamCallback(index + uiData->parameterOffset, value);
  283. }
  284. #if DISTRHO_PLUGIN_WANT_STATE
  285. void UI::setState(const char* key, const char* value)
  286. {
  287. uiData->setStateCallback(key, value);
  288. }
  289. #endif
  290. #if DISTRHO_PLUGIN_WANT_STATE
  291. bool UI::requestStateFile(const char* key)
  292. {
  293. return uiData->fileRequestCallback(key);
  294. }
  295. #endif
  296. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  297. void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
  298. {
  299. uiData->sendNoteCallback(channel, note, velocity);
  300. }
  301. #endif
  302. #if DISTRHO_UI_FILE_BROWSER
  303. bool UI::openFileBrowser(const FileBrowserOptions& options)
  304. {
  305. return getWindow().openFileBrowser((DGL_NAMESPACE::FileBrowserOptions&)options);
  306. }
  307. #endif
  308. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  309. /* ------------------------------------------------------------------------------------------------------------
  310. * Direct DSP access */
  311. void* UI::getPluginInstancePointer() const noexcept
  312. {
  313. return uiData->dspPtr;
  314. }
  315. #endif
  316. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  317. /* ------------------------------------------------------------------------------------------------------------
  318. * External UI helpers (static calls) */
  319. const char* UI::getNextBundlePath() noexcept
  320. {
  321. return g_nextBundlePath;
  322. }
  323. double UI::getNextScaleFactor() noexcept
  324. {
  325. return g_nextScaleFactor;
  326. }
  327. # if DISTRHO_PLUGIN_HAS_EMBED_UI
  328. uintptr_t UI::getNextWindowId() noexcept
  329. {
  330. return g_nextWindowId;
  331. }
  332. # endif
  333. #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  334. /* ------------------------------------------------------------------------------------------------------------
  335. * DSP/Plugin Callbacks */
  336. void UI::parameterChanged(const uint32_t index, const float value)
  337. {
  338. #if DISTRHO_UI_WEB_VIEW
  339. char msg[128];
  340. {
  341. const ScopedSafeLocale ssl;
  342. std::snprintf(msg, sizeof(msg) - 1,
  343. "typeof(parameterChanged) === 'function' && parameterChanged(%u,%f)", index, value);
  344. }
  345. evaluateJS(msg);
  346. #else
  347. // unused
  348. (void)index;
  349. (void)value;
  350. #endif
  351. }
  352. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  353. void UI::programLoaded(const uint32_t index)
  354. {
  355. #if DISTRHO_UI_WEB_VIEW
  356. char msg[128];
  357. std::snprintf(msg, sizeof(msg) - 1,
  358. "typeof(programLoaded) === 'function' && programLoaded(%u)", index);
  359. evaluateJS(msg);
  360. #else
  361. // unused
  362. (void)index;
  363. #endif
  364. }
  365. #endif
  366. #if DISTRHO_PLUGIN_WANT_STATE
  367. void UI::stateChanged(const char* const key, const char* const value)
  368. {
  369. #if DISTRHO_UI_WEB_VIEW
  370. const size_t keylen = std::strlen(key);
  371. const size_t valuelen = std::strlen(value);
  372. const size_t msglen = keylen + valuelen + 60;
  373. if (char* const msg = static_cast<char*>(std::malloc(msglen)))
  374. {
  375. // TODO escape \\'
  376. std::snprintf(msg, sizeof(msglen) - 1,
  377. "typeof(stateChanged) === 'function' && stateChanged('%s','%s')", key, value);
  378. msg[msglen - 1] = '\0';
  379. evaluateJS(msg);
  380. std::free(msg);
  381. }
  382. #else
  383. // unused
  384. (void)key;
  385. (void)value;
  386. #endif
  387. }
  388. #endif
  389. /* ------------------------------------------------------------------------------------------------------------
  390. * DSP/Plugin Callbacks (optional) */
  391. void UI::sampleRateChanged(const double sampleRate)
  392. {
  393. #if DISTRHO_UI_WEB_VIEW
  394. char msg[128];
  395. {
  396. const ScopedSafeLocale ssl;
  397. std::snprintf(msg, sizeof(msg) - 1,
  398. "typeof(sampleRateChanged) === 'function' && sampleRateChanged(%f)", sampleRate);
  399. }
  400. evaluateJS(msg);
  401. #else
  402. // unused
  403. (void)sampleRate;
  404. #endif
  405. }
  406. /* ------------------------------------------------------------------------------------------------------------
  407. * UI Callbacks (optional) */
  408. void UI::uiScaleFactorChanged(double)
  409. {
  410. }
  411. #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  412. std::vector<DGL_NAMESPACE::ClipboardDataOffer> UI::getClipboardDataOfferTypes()
  413. {
  414. return uiData->window->getClipboardDataOfferTypes();
  415. }
  416. uint32_t UI::uiClipboardDataOffer()
  417. {
  418. std::vector<DGL_NAMESPACE::ClipboardDataOffer> offers(uiData->window->getClipboardDataOfferTypes());
  419. for (std::vector<DGL_NAMESPACE::ClipboardDataOffer>::iterator it=offers.begin(), end=offers.end(); it != end;++it)
  420. {
  421. const DGL_NAMESPACE::ClipboardDataOffer offer = *it;
  422. if (std::strcmp(offer.type, "text/plain") == 0)
  423. return offer.id;
  424. }
  425. return 0;
  426. }
  427. void UI::uiFocus(bool, DGL_NAMESPACE::CrossingMode)
  428. {
  429. }
  430. void UI::uiReshape(const uint width, const uint height)
  431. {
  432. // NOTE this must be the same as Window::onReshape
  433. pData->fallbackOnResize(width, height);
  434. }
  435. #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  436. #if DISTRHO_UI_FILE_BROWSER
  437. void UI::uiFileBrowserSelected(const char*)
  438. {
  439. }
  440. #endif
  441. /* ------------------------------------------------------------------------------------------------------------
  442. * UI Resize Handling, internal */
  443. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  444. void UI::sizeChanged(const uint width, const uint height)
  445. {
  446. UIWidget::sizeChanged(width, height);
  447. uiData->setSizeCallback(width, height);
  448. }
  449. #else
  450. void UI::onResize(const ResizeEvent& ev)
  451. {
  452. UIWidget::onResize(ev);
  453. #if ! DISTRHO_UI_USES_SIZE_REQUEST
  454. if (uiData->initializing)
  455. return;
  456. const uint width = ev.size.getWidth();
  457. const uint height = ev.size.getHeight();
  458. uiData->setSizeCallback(width, height);
  459. #endif
  460. }
  461. // NOTE: only used for CLAP and VST3
  462. void UI::requestSizeChange(const uint width, const uint height)
  463. {
  464. #if DISTRHO_UI_USES_SIZE_REQUEST
  465. if (uiData->initializing)
  466. uiData->window->setSizeFromHost(width, height);
  467. else
  468. uiData->setSizeCallback(width, height);
  469. #else
  470. // unused
  471. (void)width;
  472. (void)height;
  473. #endif
  474. }
  475. #endif
  476. // -----------------------------------------------------------------------------------------------------------
  477. #if DISTRHO_UI_WEB_VIEW
  478. void UI::onMessage(char* const message)
  479. {
  480. if (std::strncmp(message, "setparam ", 9) == 0)
  481. {
  482. char* const strindex = message + 9;
  483. char* const sep = std::strchr(strindex, ' ');
  484. DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,);
  485. *sep = 0;
  486. char* const strvalue = sep + 1;
  487. const uint32_t index = std::atoi(strindex);
  488. float value;
  489. {
  490. const ScopedSafeLocale ssl;
  491. value = std::atof(strvalue);
  492. }
  493. setParameterValue(index, value);
  494. return;
  495. }
  496. if (std::strncmp(message, "editparam ", 10) == 0)
  497. {
  498. char* const strindex = message + 10;
  499. char* const sep = std::strchr(strindex, ' ');
  500. DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,);
  501. *sep = 0;
  502. char* const strstarted = sep + 1;
  503. const uint32_t index = std::atoi(strindex);
  504. const bool started = std::atoi(strstarted) != 0;
  505. editParameter(index, started);
  506. return;
  507. }
  508. #if DISTRHO_PLUGIN_WANT_STATE
  509. if (std::strncmp(message, "setstate ", 9) == 0)
  510. {
  511. char* const key = message + 9;
  512. char* const sep = std::strchr(key, ' ');
  513. DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,);
  514. *sep = 0;
  515. char* const value = sep + 1;
  516. setState(key, value);
  517. return;
  518. }
  519. #endif
  520. d_stderr("UI received unknown message %s", message);
  521. }
  522. #endif
  523. // -----------------------------------------------------------------------------------------------------------
  524. END_NAMESPACE_DISTRHO