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.

1352 lines
52KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include <library.hpp>
  18. #include <midi.hpp>
  19. #include <patch.hpp>
  20. #include <plugin.hpp>
  21. #include <random.hpp>
  22. #include <settings.hpp>
  23. #include <system.hpp>
  24. #include <app/Scene.hpp>
  25. #include <engine/Engine.hpp>
  26. #include <ui/common.hpp>
  27. #include <widget/Widget.hpp>
  28. #include <window/Window.hpp>
  29. #ifdef NDEBUG
  30. # undef DEBUG
  31. #endif
  32. #if defined(HAVE_LIBLO) && defined(HEADLESS)
  33. # include <lo/lo.h>
  34. # include "extra/Thread.hpp"
  35. #endif
  36. #include <cfloat>
  37. #include <list>
  38. #include "CardinalCommon.hpp"
  39. #include "DistrhoPluginUtils.hpp"
  40. #include "PluginContext.hpp"
  41. #include "extra/Base64.hpp"
  42. #include "extra/ScopedDenormalDisable.hpp"
  43. #include "extra/ScopedSafeLocale.hpp"
  44. #ifdef DISTRHO_OS_WASM
  45. # include <emscripten/emscripten.h>
  46. #else
  47. # include "extra/SharedResourcePointer.hpp"
  48. #endif
  49. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  50. # include "extra/ScopedValueSetter.hpp"
  51. #endif
  52. extern const std::string CARDINAL_VERSION;
  53. namespace rack {
  54. #if (CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS) || defined(HEADLESS)
  55. namespace app {
  56. rack::widget::Widget* createMenuBar() { return new rack::widget::Widget; }
  57. }
  58. #endif
  59. namespace engine {
  60. void Engine_setAboutToClose(Engine*);
  61. }
  62. }
  63. START_NAMESPACE_DISTRHO
  64. template<typename T>
  65. static inline
  66. bool d_isDiffHigherThanLimit(const T& v1, const T& v2, const T& limit)
  67. {
  68. return v1 != v2 ? (v1 > v2 ? v1 - v2 : v2 - v1) > limit : false;
  69. }
  70. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  71. const char* UI::getBundlePath() const noexcept { return nullptr; }
  72. void UI::setState(const char*, const char*) {}
  73. #endif
  74. // -----------------------------------------------------------------------------------------------------------
  75. #ifdef DISTRHO_OS_WASM
  76. static char* getPatchFileEncodedInURL() {
  77. return static_cast<char*>(EM_ASM_PTR({
  78. var searchParams = new URLSearchParams(window.location.search);
  79. var patch = searchParams.get('patch');
  80. if (!patch)
  81. return null;
  82. var length = lengthBytesUTF8(patch) + 1;
  83. var str = _malloc(length);
  84. stringToUTF8(patch, str, length);
  85. return str;
  86. }));
  87. };
  88. static char* getPatchRemoteURL() {
  89. return static_cast<char*>(EM_ASM_PTR({
  90. var searchParams = new URLSearchParams(window.location.search);
  91. var patch = searchParams.get('patchurl');
  92. if (!patch)
  93. return null;
  94. var length = lengthBytesUTF8(patch) + 1;
  95. var str = _malloc(length);
  96. stringToUTF8(patch, str, length);
  97. return str;
  98. }));
  99. };
  100. static char* getPatchStorageSlug() {
  101. return static_cast<char*>(EM_ASM_PTR({
  102. var searchParams = new URLSearchParams(window.location.search);
  103. var patch = searchParams.get('patchstorage');
  104. if (!patch)
  105. return null;
  106. var length = lengthBytesUTF8(patch) + 1;
  107. var str = _malloc(length);
  108. stringToUTF8(patch, str, length);
  109. return str;
  110. }));
  111. };
  112. #endif
  113. // -----------------------------------------------------------------------------------------------------------
  114. struct ScopedContext {
  115. ScopedContext(const CardinalBasePlugin* const plugin)
  116. {
  117. rack::contextSet(plugin->context);
  118. }
  119. ~ScopedContext()
  120. {
  121. rack::contextSet(nullptr);
  122. }
  123. };
  124. // -----------------------------------------------------------------------------------------------------------
  125. class CardinalPlugin : public CardinalBasePlugin
  126. {
  127. #ifdef DISTRHO_OS_WASM
  128. ScopedPointer<Initializer> fInitializer;
  129. #else
  130. SharedResourcePointer<Initializer> fInitializer;
  131. #endif
  132. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  133. /* If host audio ins == outs we can get issues for inplace processing.
  134. * So allocate a float array that will serve as safe copy for those cases.
  135. */
  136. float** fAudioBufferCopy;
  137. #endif
  138. std::string fAutosavePath;
  139. uint64_t fNextExpectedFrame;
  140. struct {
  141. String comment;
  142. String screenshot;
  143. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  144. String windowSize;
  145. #endif
  146. } fState;
  147. // bypass handling
  148. bool fWasBypassed;
  149. MidiEvent bypassMidiEvents[16];
  150. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  151. // real values, not VCV interpreted ones
  152. float fWindowParameters[kWindowParameterCount];
  153. #endif
  154. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  155. float fMiniReportValues[kCardinalParameterCountAtMini - kCardinalParameterStartMini];
  156. #endif
  157. public:
  158. CardinalPlugin()
  159. : CardinalBasePlugin(kCardinalParameterCount, 0, kCardinalStateCount),
  160. #ifdef DISTRHO_OS_WASM
  161. fInitializer(new Initializer(this, static_cast<const CardinalBaseUI*>(nullptr))),
  162. #else
  163. fInitializer(this, static_cast<const CardinalBaseUI*>(nullptr)),
  164. #endif
  165. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  166. fAudioBufferCopy(nullptr),
  167. #endif
  168. fNextExpectedFrame(0),
  169. fWasBypassed(false)
  170. {
  171. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  172. fWindowParameters[kWindowParameterShowTooltips] = 1.0f;
  173. fWindowParameters[kWindowParameterCableOpacity] = 50.0f;
  174. fWindowParameters[kWindowParameterCableTension] = 75.0f;
  175. fWindowParameters[kWindowParameterRackBrightness] = 100.0f;
  176. fWindowParameters[kWindowParameterHaloBrightness] = 25.0f;
  177. fWindowParameters[kWindowParameterKnobMode] = 0.0f;
  178. fWindowParameters[kWindowParameterWheelKnobControl] = 0.0f;
  179. fWindowParameters[kWindowParameterWheelSensitivity] = 1.0f;
  180. fWindowParameters[kWindowParameterLockModulePositions] = 0.0f;
  181. fWindowParameters[kWindowParameterUpdateRateLimit] = 0.0f;
  182. fWindowParameters[kWindowParameterBrowserSort] = 3.0f;
  183. fWindowParameters[kWindowParameterBrowserZoom] = 50.0f;
  184. fWindowParameters[kWindowParameterInvertZoom] = 0.0f;
  185. fWindowParameters[kWindowParameterSqueezeModulePositions] = 1.0f;
  186. #endif
  187. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  188. std::memset(fMiniReportValues, 0, sizeof(fMiniReportValues));
  189. fMiniReportValues[kCardinalParameterMiniTimeBar - kCardinalParameterStartMini] = 1;
  190. fMiniReportValues[kCardinalParameterMiniTimeBeat - kCardinalParameterStartMini] = 1;
  191. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerBar - kCardinalParameterStartMini] = 4;
  192. fMiniReportValues[kCardinalParameterMiniTimeBeatType - kCardinalParameterStartMini] = 4;
  193. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerMinute - kCardinalParameterStartMini] = 120;
  194. #endif
  195. // create unique temporary path for this instance
  196. try {
  197. char uidBuf[24];
  198. const std::string tmp = rack::system::getTempDirectory();
  199. for (int i=1;; ++i)
  200. {
  201. std::snprintf(uidBuf, sizeof(uidBuf), "Cardinal.%04d", i);
  202. const std::string trypath = rack::system::join(tmp, uidBuf);
  203. if (! rack::system::exists(trypath))
  204. {
  205. if (rack::system::createDirectories(trypath))
  206. fAutosavePath = trypath;
  207. break;
  208. }
  209. }
  210. } DISTRHO_SAFE_EXCEPTION("create unique temporary path");
  211. // initialize midi events used when entering bypassed state
  212. std::memset(bypassMidiEvents, 0, sizeof(bypassMidiEvents));
  213. for (uint8_t i=0; i<16; ++i)
  214. {
  215. bypassMidiEvents[i].size = 3;
  216. bypassMidiEvents[i].data[0] = 0xB0 + i;
  217. bypassMidiEvents[i].data[1] = 0x7B;
  218. }
  219. const float sampleRate = getSampleRate();
  220. rack::settings::sampleRate = sampleRate;
  221. context->bufferSize = getBufferSize();
  222. context->sampleRate = sampleRate;
  223. const ScopedContext sc(this);
  224. context->engine = new rack::engine::Engine;
  225. context->engine->setSampleRate(sampleRate);
  226. context->history = new rack::history::State;
  227. context->patch = new rack::patch::Manager;
  228. context->patch->autosavePath = fAutosavePath;
  229. context->patch->templatePath = fInitializer->templatePath;
  230. context->patch->factoryTemplatePath = fInitializer->factoryTemplatePath;
  231. context->event = new rack::widget::EventState;
  232. context->scene = new rack::app::Scene;
  233. context->event->rootWidget = context->scene;
  234. if (! isDummyInstance())
  235. context->window = new rack::window::Window;
  236. #ifdef DISTRHO_OS_WASM
  237. if ((rack::patchStorageSlug = getPatchStorageSlug()) == nullptr &&
  238. (rack::patchRemoteURL = getPatchRemoteURL()) == nullptr &&
  239. (rack::patchFromURL = getPatchFileEncodedInURL()) == nullptr)
  240. #endif
  241. {
  242. context->patch->loadTemplate();
  243. context->scene->rackScroll->reset();
  244. // swap to factory template after first load
  245. context->patch->templatePath = context->patch->factoryTemplatePath;
  246. }
  247. #ifdef CARDINAL_INIT_OSC_THREAD
  248. fInitializer->remotePluginInstance = this;
  249. #endif
  250. }
  251. ~CardinalPlugin() override
  252. {
  253. #ifdef CARDINAL_INIT_OSC_THREAD
  254. if (fInitializer->remotePluginInstance == this)
  255. fInitializer->remotePluginInstance = nullptr;
  256. #endif
  257. {
  258. const ScopedContext sc(this);
  259. context->patch->clear();
  260. // do a little dance to prevent context scene deletion from saving to temp dir
  261. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  262. const ScopedValueSetter<bool> svs(rack::settings::headless, true);
  263. #endif
  264. Engine_setAboutToClose(context->engine);
  265. delete context;
  266. }
  267. if (! fAutosavePath.empty())
  268. rack::system::removeRecursively(fAutosavePath);
  269. }
  270. CardinalPluginContext* getRackContext() const noexcept
  271. {
  272. return context;
  273. }
  274. protected:
  275. /* --------------------------------------------------------------------------------------------------------
  276. * Information */
  277. const char* getLabel() const override
  278. {
  279. return DISTRHO_PLUGIN_LABEL;
  280. }
  281. const char* getDescription() const override
  282. {
  283. return ""
  284. "Cardinal is a free and open-source virtual modular synthesizer plugin.\n"
  285. "It is based on the popular VCV Rack but with a focus on being a fully self-contained plugin version.\n"
  286. "It is not an official VCV project, and it is not affiliated with it in any way.\n"
  287. "\n"
  288. "Cardinal contains Rack, some 3rd-party modules and a few internal utilities.\n"
  289. "It does not load external modules and does not connect to the official Rack library/store.\n";
  290. }
  291. const char* getMaker() const override
  292. {
  293. return "DISTRHO";
  294. }
  295. const char* getHomePage() const override
  296. {
  297. return "https://github.com/DISTRHO/Cardinal";
  298. }
  299. const char* getLicense() const override
  300. {
  301. return "GPLv3+";
  302. }
  303. uint32_t getVersion() const override
  304. {
  305. return d_version(0, 23, 2);
  306. }
  307. int64_t getUniqueId() const override
  308. {
  309. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_NATIVE
  310. return d_cconst('d', 'C', 'd', 'n');
  311. #elif CARDINAL_VARIANT_MINI
  312. return d_cconst('d', 'C', 'd', 'M');
  313. #elif CARDINAL_VARIANT_FX
  314. return d_cconst('d', 'C', 'n', 'F');
  315. #elif CARDINAL_VARIANT_SYNTH
  316. return d_cconst('d', 'C', 'n', 'S');
  317. #else
  318. #error cardinal variant not set
  319. #endif
  320. }
  321. /* --------------------------------------------------------------------------------------------------------
  322. * Init */
  323. void initAudioPort(const bool input, uint32_t index, AudioPort& port) override
  324. {
  325. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  326. static_assert(CARDINAL_NUM_AUDIO_INPUTS == CARDINAL_NUM_AUDIO_OUTPUTS, "inputs == outputs");
  327. if (index < CARDINAL_NUM_AUDIO_INPUTS)
  328. {
  329. #if CARDINAL_VARIANT_MINI
  330. port.groupId = kPortGroupStereo;
  331. #else
  332. port.groupId = index / 2;
  333. #endif
  334. }
  335. else
  336. {
  337. port.hints = kAudioPortIsCV | kCVPortHasPositiveUnipolarRange | kCVPortHasScaledRange | kCVPortIsOptional;
  338. index -= CARDINAL_NUM_AUDIO_INPUTS;
  339. }
  340. #elif CARDINAL_VARIANT_NATIVE || CARDINAL_VARIANT_FX || CARDINAL_VARIANT_SYNTH
  341. if (index < 2)
  342. port.groupId = kPortGroupStereo;
  343. #endif
  344. CardinalBasePlugin::initAudioPort(input, index, port);
  345. }
  346. #if CARDINAL_VARIANT_MAIN
  347. void initPortGroup(const uint32_t index, PortGroup& portGroup) override
  348. {
  349. switch (index)
  350. {
  351. case 0:
  352. portGroup.name = "Audio 1+2";
  353. portGroup.symbol = "audio_1_and_2";
  354. break;
  355. case 1:
  356. portGroup.name = "Audio 3+4";
  357. portGroup.symbol = "audio_3_and_4";
  358. break;
  359. case 2:
  360. portGroup.name = "Audio 5+6";
  361. portGroup.symbol = "audio_5_and_6";
  362. break;
  363. case 3:
  364. portGroup.name = "Audio 7+8";
  365. portGroup.symbol = "audio_7_and_8";
  366. break;
  367. }
  368. }
  369. #endif
  370. void initParameter(const uint32_t index, Parameter& parameter) override
  371. {
  372. if (index < kCardinalParameterCountAtModules)
  373. {
  374. parameter.name = "Parameter ";
  375. parameter.name += String(index + 1);
  376. parameter.symbol = "param_";
  377. parameter.symbol += String(index + 1);
  378. parameter.unit = "v";
  379. parameter.hints = kParameterIsAutomatable;
  380. parameter.ranges.def = 0.0f;
  381. parameter.ranges.min = 0.0f;
  382. parameter.ranges.max = 10.0f;
  383. return;
  384. }
  385. if (index == kCardinalParameterBypass)
  386. {
  387. parameter.initDesignation(kParameterDesignationBypass);
  388. return;
  389. }
  390. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  391. if (index < kCardinalParameterCountAtWindow)
  392. {
  393. switch (index - kCardinalParameterStartWindow)
  394. {
  395. case kWindowParameterShowTooltips:
  396. parameter.name = "Show tooltips";
  397. parameter.symbol = "tooltips";
  398. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  399. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  400. parameter.hints |= kParameterIsHidden;
  401. #endif
  402. parameter.ranges.def = 1.0f;
  403. parameter.ranges.min = 0.0f;
  404. parameter.ranges.max = 1.0f;
  405. break;
  406. case kWindowParameterCableOpacity:
  407. parameter.name = "Cable opacity";
  408. parameter.symbol = "cableOpacity";
  409. parameter.unit = "%";
  410. parameter.hints = kParameterIsAutomatable;
  411. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  412. parameter.hints |= kParameterIsHidden;
  413. #endif
  414. parameter.ranges.def = 50.0f;
  415. parameter.ranges.min = 0.0f;
  416. parameter.ranges.max = 100.0f;
  417. break;
  418. case kWindowParameterCableTension:
  419. parameter.name = "Cable tension";
  420. parameter.symbol = "cableTension";
  421. parameter.unit = "%";
  422. parameter.hints = kParameterIsAutomatable;
  423. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  424. parameter.hints |= kParameterIsHidden;
  425. #endif
  426. parameter.ranges.def = 75.0f;
  427. parameter.ranges.min = 0.0f;
  428. parameter.ranges.max = 100.0f;
  429. break;
  430. case kWindowParameterRackBrightness:
  431. parameter.name = "Room brightness";
  432. parameter.symbol = "rackBrightness";
  433. parameter.unit = "%";
  434. parameter.hints = kParameterIsAutomatable;
  435. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  436. parameter.hints |= kParameterIsHidden;
  437. #endif
  438. parameter.ranges.def = 100.0f;
  439. parameter.ranges.min = 0.0f;
  440. parameter.ranges.max = 100.0f;
  441. break;
  442. case kWindowParameterHaloBrightness:
  443. parameter.name = "Light Bloom";
  444. parameter.symbol = "haloBrightness";
  445. parameter.unit = "%";
  446. parameter.hints = kParameterIsAutomatable;
  447. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  448. parameter.hints |= kParameterIsHidden;
  449. #endif
  450. parameter.ranges.def = 25.0f;
  451. parameter.ranges.min = 0.0f;
  452. parameter.ranges.max = 100.0f;
  453. break;
  454. case kWindowParameterKnobMode:
  455. parameter.name = "Knob mode";
  456. parameter.symbol = "knobMode";
  457. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  458. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  459. parameter.hints |= kParameterIsHidden;
  460. #endif
  461. parameter.ranges.def = 0.0f;
  462. parameter.ranges.min = 0.0f;
  463. parameter.ranges.max = 2.0f;
  464. parameter.enumValues.count = 3;
  465. parameter.enumValues.restrictedMode = true;
  466. parameter.enumValues.values = new ParameterEnumerationValue[3];
  467. parameter.enumValues.values[0].label = "Linear";
  468. parameter.enumValues.values[0].value = 0.0f;
  469. parameter.enumValues.values[1].label = "Absolute rotary";
  470. parameter.enumValues.values[1].value = 1.0f;
  471. parameter.enumValues.values[2].label = "Relative rotary";
  472. parameter.enumValues.values[2].value = 2.0f;
  473. break;
  474. case kWindowParameterWheelKnobControl:
  475. parameter.name = "Scroll wheel knob control";
  476. parameter.symbol = "knobScroll";
  477. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  478. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  479. parameter.hints |= kParameterIsHidden;
  480. #endif
  481. parameter.ranges.def = 0.0f;
  482. parameter.ranges.min = 0.0f;
  483. parameter.ranges.max = 1.0f;
  484. break;
  485. case kWindowParameterWheelSensitivity:
  486. parameter.name = "Scroll wheel knob sensitivity";
  487. parameter.symbol = "knobScrollSensitivity";
  488. parameter.hints = kParameterIsAutomatable|kParameterIsLogarithmic;
  489. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  490. parameter.hints |= kParameterIsHidden;
  491. #endif
  492. parameter.ranges.def = 1.0f;
  493. parameter.ranges.min = 0.1f;
  494. parameter.ranges.max = 10.0f;
  495. break;
  496. case kWindowParameterLockModulePositions:
  497. parameter.name = "Lock module positions";
  498. parameter.symbol = "lockModules";
  499. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  500. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  501. parameter.hints |= kParameterIsHidden;
  502. #endif
  503. parameter.ranges.def = 0.0f;
  504. parameter.ranges.min = 0.0f;
  505. parameter.ranges.max = 1.0f;
  506. break;
  507. case kWindowParameterUpdateRateLimit:
  508. parameter.name = "Update rate limit";
  509. parameter.symbol = "rateLimit";
  510. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  511. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  512. parameter.hints |= kParameterIsHidden;
  513. #endif
  514. parameter.ranges.def = 0.0f;
  515. parameter.ranges.min = 0.0f;
  516. parameter.ranges.max = 2.0f;
  517. parameter.enumValues.count = 3;
  518. parameter.enumValues.restrictedMode = true;
  519. parameter.enumValues.values = new ParameterEnumerationValue[3];
  520. parameter.enumValues.values[0].label = "None";
  521. parameter.enumValues.values[0].value = 0.0f;
  522. parameter.enumValues.values[1].label = "2x";
  523. parameter.enumValues.values[1].value = 1.0f;
  524. parameter.enumValues.values[2].label = "4x";
  525. parameter.enumValues.values[2].value = 2.0f;
  526. break;
  527. case kWindowParameterBrowserSort:
  528. parameter.name = "Browser sort";
  529. parameter.symbol = "browserSort";
  530. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  531. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  532. parameter.hints |= kParameterIsHidden;
  533. #endif
  534. parameter.ranges.def = 3.0f;
  535. parameter.ranges.min = 0.0f;
  536. parameter.ranges.max = 5.0f;
  537. parameter.enumValues.count = 6;
  538. parameter.enumValues.restrictedMode = true;
  539. parameter.enumValues.values = new ParameterEnumerationValue[6];
  540. parameter.enumValues.values[0].label = "Updated";
  541. parameter.enumValues.values[0].value = 0.0f;
  542. parameter.enumValues.values[1].label = "Last used";
  543. parameter.enumValues.values[1].value = 1.0f;
  544. parameter.enumValues.values[2].label = "Most used";
  545. parameter.enumValues.values[2].value = 2.0f;
  546. parameter.enumValues.values[3].label = "Brand";
  547. parameter.enumValues.values[3].value = 3.0f;
  548. parameter.enumValues.values[4].label = "Name";
  549. parameter.enumValues.values[4].value = 4.0f;
  550. parameter.enumValues.values[5].label = "Random";
  551. parameter.enumValues.values[5].value = 5.0f;
  552. break;
  553. case kWindowParameterBrowserZoom:
  554. parameter.name = "Browser zoom";
  555. parameter.symbol = "browserZoom";
  556. parameter.hints = kParameterIsAutomatable;
  557. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  558. parameter.hints |= kParameterIsHidden;
  559. #endif
  560. parameter.unit = "%";
  561. parameter.ranges.def = 50.0f;
  562. parameter.ranges.min = 25.0f;
  563. parameter.ranges.max = 200.0f;
  564. parameter.enumValues.count = 7;
  565. parameter.enumValues.restrictedMode = true;
  566. parameter.enumValues.values = new ParameterEnumerationValue[7];
  567. parameter.enumValues.values[0].label = "25";
  568. parameter.enumValues.values[0].value = 25.0f;
  569. parameter.enumValues.values[1].label = "35";
  570. parameter.enumValues.values[1].value = 35.0f;
  571. parameter.enumValues.values[2].label = "50";
  572. parameter.enumValues.values[2].value = 50.0f;
  573. parameter.enumValues.values[3].label = "71";
  574. parameter.enumValues.values[3].value = 71.0f;
  575. parameter.enumValues.values[4].label = "100";
  576. parameter.enumValues.values[4].value = 100.0f;
  577. parameter.enumValues.values[5].label = "141";
  578. parameter.enumValues.values[5].value = 141.0f;
  579. parameter.enumValues.values[6].label = "200";
  580. parameter.enumValues.values[6].value = 200.0f;
  581. break;
  582. case kWindowParameterInvertZoom:
  583. parameter.name = "Invert zoom";
  584. parameter.symbol = "invertZoom";
  585. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  586. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  587. parameter.hints |= kParameterIsHidden;
  588. #endif
  589. parameter.ranges.def = 0.0f;
  590. parameter.ranges.min = 0.0f;
  591. parameter.ranges.max = 1.0f;
  592. break;
  593. case kWindowParameterSqueezeModulePositions:
  594. parameter.name = "Auto-squeeze module positions";
  595. parameter.symbol = "squeezeModules";
  596. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  597. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  598. parameter.hints |= kParameterIsHidden;
  599. #endif
  600. parameter.ranges.def = 1.0f;
  601. parameter.ranges.min = 0.0f;
  602. parameter.ranges.max = 1.0f;
  603. break;
  604. }
  605. }
  606. #endif
  607. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  608. switch (index)
  609. {
  610. case kCardinalParameterMiniAudioIn1:
  611. parameter.name = "Report Audio Input 1";
  612. parameter.symbol = "r_audio_in_1";
  613. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  614. parameter.ranges.def = 0.0f;
  615. parameter.ranges.min = 0.0f;
  616. parameter.ranges.max = 1.0f;
  617. break;
  618. case kCardinalParameterMiniAudioIn2:
  619. parameter.name = "Report Audio Input 2";
  620. parameter.symbol = "r_audio_in_2";
  621. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  622. parameter.ranges.def = 0.0f;
  623. parameter.ranges.min = 0.0f;
  624. parameter.ranges.max = 1.0f;
  625. break;
  626. case kCardinalParameterMiniCVIn1:
  627. parameter.name = "Report CV Input 1";
  628. parameter.symbol = "r_cv_in_1";
  629. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  630. parameter.ranges.def = -10.0f;
  631. parameter.ranges.min = 0.0f;
  632. parameter.ranges.max = 10.0f;
  633. break;
  634. case kCardinalParameterMiniCVIn2:
  635. parameter.name = "Report CV Input 2";
  636. parameter.symbol = "r_cv_in_2";
  637. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  638. parameter.ranges.def = -10.0f;
  639. parameter.ranges.min = 0.0f;
  640. parameter.ranges.max = 10.0f;
  641. break;
  642. case kCardinalParameterMiniCVIn3:
  643. parameter.name = "Report CV Input 3";
  644. parameter.symbol = "r_cv_in_3";
  645. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  646. parameter.ranges.def = -10.0f;
  647. parameter.ranges.min = 0.0f;
  648. parameter.ranges.max = 10.0f;
  649. break;
  650. case kCardinalParameterMiniCVIn4:
  651. parameter.name = "Report CV Input 4";
  652. parameter.symbol = "r_cv_in_4";
  653. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  654. parameter.ranges.def = -10.0f;
  655. parameter.ranges.min = 0.0f;
  656. parameter.ranges.max = 10.0f;
  657. break;
  658. case kCardinalParameterMiniCVIn5:
  659. parameter.name = "Report CV Input 5";
  660. parameter.symbol = "r_cv_in_5";
  661. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  662. parameter.ranges.def = -10.0f;
  663. parameter.ranges.min = 0.0f;
  664. parameter.ranges.max = 10.0f;
  665. break;
  666. case kCardinalParameterMiniTimeFlags:
  667. parameter.name = "Report Time Flags";
  668. parameter.symbol = "r_time_flags";
  669. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  670. parameter.ranges.def = 0x0;
  671. parameter.ranges.min = 0x0;
  672. parameter.ranges.max = 0x7;
  673. break;
  674. case kCardinalParameterMiniTimeBar:
  675. parameter.name = "Report Time Bar";
  676. parameter.symbol = "r_time_bar";
  677. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  678. parameter.ranges.def = 1.0f;
  679. parameter.ranges.min = 1.0f;
  680. parameter.ranges.max = FLT_MAX;
  681. break;
  682. case kCardinalParameterMiniTimeBeat:
  683. parameter.name = "Report Time Beat";
  684. parameter.symbol = "r_time_beat";
  685. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  686. parameter.ranges.def = 1.0f;
  687. parameter.ranges.min = 1.0f;
  688. parameter.ranges.max = 128.0f;
  689. break;
  690. case kCardinalParameterMiniTimeBeatsPerBar:
  691. parameter.name = "Report Time Beats Per Bar";
  692. parameter.symbol = "r_time_beatsPerBar";
  693. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  694. parameter.ranges.def = 4.0f;
  695. parameter.ranges.min = 0.0f;
  696. parameter.ranges.max = 128.0f;
  697. break;
  698. case kCardinalParameterMiniTimeBeatType:
  699. parameter.name = "Report Time Beat Type";
  700. parameter.symbol = "r_time_beatType";
  701. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  702. parameter.ranges.def = 4.0f;
  703. parameter.ranges.min = 0.0f;
  704. parameter.ranges.max = 128.0f;
  705. break;
  706. case kCardinalParameterMiniTimeFrame:
  707. parameter.name = "Report Time Frame";
  708. parameter.symbol = "r_time_frame";
  709. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  710. parameter.ranges.def = 0.0f;
  711. parameter.ranges.min = 0.0f;
  712. parameter.ranges.max = FLT_MAX;
  713. break;
  714. case kCardinalParameterMiniTimeBarStartTick:
  715. parameter.name = "Report Time BarStartTick";
  716. parameter.symbol = "r_time_barStartTick";
  717. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  718. parameter.ranges.def = 0.0f;
  719. parameter.ranges.min = 0.0f;
  720. parameter.ranges.max = FLT_MAX;
  721. break;
  722. case kCardinalParameterMiniTimeBeatsPerMinute:
  723. parameter.name = "Report Time Beats Per Minute";
  724. parameter.symbol = "r_time_bpm";
  725. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  726. parameter.ranges.def = 20.0f;
  727. parameter.ranges.min = 120.0f;
  728. parameter.ranges.max = 999.0f;
  729. break;
  730. case kCardinalParameterMiniTimeTick:
  731. parameter.name = "Report Time Tick";
  732. parameter.symbol = "r_time_tick";
  733. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  734. parameter.ranges.def = 0.0f;
  735. parameter.ranges.min = 0.0f;
  736. parameter.ranges.max = 8192.0f;
  737. break;
  738. case kCardinalParameterMiniTimeTicksPerBeat:
  739. parameter.name = "Report Time Ticks Per Beat";
  740. parameter.symbol = "r_time_ticksPerBeat";
  741. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  742. parameter.ranges.def = 0.0f;
  743. parameter.ranges.min = 0.0f;
  744. parameter.ranges.max = 8192.0f;
  745. break;
  746. }
  747. #endif
  748. }
  749. void initState(const uint32_t index, State& state) override
  750. {
  751. switch (index)
  752. {
  753. case kCardinalStatePatch:
  754. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  755. state.hints = kStateIsHostReadable;
  756. #else
  757. state.hints = kStateIsOnlyForDSP | kStateIsBase64Blob;
  758. #endif
  759. if (FILE* const f = std::fopen(context->patch->factoryTemplatePath.c_str(), "r"))
  760. {
  761. std::fseek(f, 0, SEEK_END);
  762. if (const long fileSize = std::ftell(f))
  763. {
  764. std::fseek(f, 0, SEEK_SET);
  765. char* const fileContent = new char[fileSize+1];
  766. if (std::fread(fileContent, fileSize, 1, f) == 1)
  767. {
  768. fileContent[fileSize] = '\0';
  769. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  770. state.defaultValue = fileContent;
  771. #else
  772. state.defaultValue = String::asBase64(fileContent, fileSize);
  773. #endif
  774. }
  775. delete[] fileContent;
  776. }
  777. std::fclose(f);
  778. }
  779. state.key = "patch";
  780. state.label = "Patch";
  781. break;
  782. case kCardinalStateScreenshot:
  783. state.hints = kStateIsHostReadable | kStateIsBase64Blob;
  784. state.key = "screenshot";
  785. state.label = "Screenshot";
  786. break;
  787. case kCardinalStateComment:
  788. state.hints = kStateIsHostWritable;
  789. state.key = "comment";
  790. state.label = "Comment";
  791. break;
  792. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  793. case kCardinalStateModuleInfos:
  794. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  795. state.hints = kStateIsHostReadable;
  796. #else
  797. state.hints = kStateIsOnlyForDSP;
  798. #endif
  799. state.defaultValue = "{}";
  800. state.key = "moduleInfos";
  801. state.label = "moduleInfos";
  802. break;
  803. case kCardinalStateWindowSize:
  804. state.hints = kStateIsOnlyForUI;
  805. // state.defaultValue = String("%d:%d", DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT);
  806. state.key = "windowSize";
  807. state.label = "Window size";
  808. break;
  809. #endif
  810. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  811. case kCardinalStateParamChange:
  812. state.hints = kStateIsHostReadable | kStateIsOnlyForDSP;
  813. state.key = "param";
  814. state.label = "ParamChange";
  815. break;
  816. #endif
  817. }
  818. }
  819. /* --------------------------------------------------------------------------------------------------------
  820. * Internal data */
  821. float getParameterValue(uint32_t index) const override
  822. {
  823. // host mapped parameters
  824. if (index < kCardinalParameterCountAtModules)
  825. return context->parameters[index];
  826. // bypass
  827. if (index == kCardinalParameterBypass)
  828. return context->bypassed ? 1.0f : 0.0f;
  829. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  830. if (index < kCardinalParameterCountAtWindow)
  831. return fWindowParameters[index - kCardinalParameterStartWindow];
  832. #endif
  833. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  834. if (index < kCardinalParameterCountAtMini)
  835. return fMiniReportValues[index - kCardinalParameterStartMini];
  836. #endif
  837. return 0.0f;
  838. }
  839. void setParameterValue(uint32_t index, float value) override
  840. {
  841. // host mapped parameters
  842. if (index < kCardinalParameterCountAtModules)
  843. {
  844. context->parameters[index] = value;
  845. return;
  846. }
  847. // bypass
  848. if (index == kCardinalParameterBypass)
  849. {
  850. context->bypassed = value > 0.5f;
  851. return;
  852. }
  853. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  854. if (index < kCardinalParameterCountAtWindow)
  855. {
  856. fWindowParameters[index - kCardinalParameterStartWindow] = value;
  857. return;
  858. }
  859. #endif
  860. }
  861. String getState(const char* const key) const override
  862. {
  863. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  864. if (std::strcmp(key, "moduleInfos") == 0)
  865. {
  866. json_t* const rootJ = json_object();
  867. DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, String());
  868. for (const auto& pluginPair : rack::settings::moduleInfos)
  869. {
  870. json_t* const pluginJ = json_object();
  871. DISTRHO_SAFE_ASSERT_CONTINUE(pluginJ != nullptr);
  872. for (const auto& modulePair : pluginPair.second)
  873. {
  874. json_t* const moduleJ = json_object();
  875. DISTRHO_SAFE_ASSERT_CONTINUE(moduleJ != nullptr);
  876. const rack::settings::ModuleInfo& m(modulePair.second);
  877. // To make setting.json smaller, only set properties if not default values.
  878. if (m.favorite)
  879. json_object_set_new(moduleJ, "favorite", json_boolean(m.favorite));
  880. if (m.added > 0)
  881. json_object_set_new(moduleJ, "added", json_integer(m.added));
  882. if (std::isfinite(m.lastAdded) && d_isNotZero(m.lastAdded))
  883. json_object_set_new(moduleJ, "lastAdded", json_real(m.lastAdded));
  884. if (json_object_size(moduleJ))
  885. json_object_set_new(pluginJ, modulePair.first.c_str(), moduleJ);
  886. else
  887. json_decref(moduleJ);
  888. }
  889. if (json_object_size(pluginJ))
  890. json_object_set_new(rootJ, pluginPair.first.c_str(), pluginJ);
  891. else
  892. json_decref(pluginJ);
  893. }
  894. const String info(json_dumps(rootJ, JSON_COMPACT), false);
  895. json_decref(rootJ);
  896. return info;
  897. }
  898. if (std::strcmp(key, "windowSize") == 0)
  899. return fState.windowSize;
  900. #endif
  901. if (std::strcmp(key, "comment") == 0)
  902. return fState.comment;
  903. if (std::strcmp(key, "screenshot") == 0)
  904. return fState.screenshot;
  905. if (std::strcmp(key, "patch") != 0)
  906. return String();
  907. if (fAutosavePath.empty())
  908. return String();
  909. std::vector<uint8_t> data;
  910. {
  911. const ScopedContext sc(this);
  912. context->engine->prepareSave();
  913. context->patch->saveAutosave();
  914. context->patch->cleanAutosave();
  915. // context->history->setSaved();
  916. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  917. FILE* const f = std::fopen(rack::system::join(context->patch->autosavePath, "patch.json").c_str(), "r");
  918. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr, String());
  919. DEFER({
  920. std::fclose(f);
  921. });
  922. std::fseek(f, 0, SEEK_END);
  923. const long fileSize = std::ftell(f);
  924. DISTRHO_SAFE_ASSERT_RETURN(fileSize > 0, String());
  925. std::fseek(f, 0, SEEK_SET);
  926. char* const fileContent = static_cast<char*>(std::malloc(fileSize+1));
  927. DISTRHO_SAFE_ASSERT_RETURN(std::fread(fileContent, fileSize, 1, f) == 1, String());
  928. fileContent[fileSize] = '\0';
  929. return String(fileContent, false);
  930. #else
  931. try {
  932. data = rack::system::archiveDirectory(fAutosavePath, 1);
  933. } DISTRHO_SAFE_EXCEPTION_RETURN("getState archiveDirectory", String());
  934. #endif
  935. }
  936. return String::asBase64(data.data(), data.size());
  937. }
  938. void setState(const char* const key, const char* const value) override
  939. {
  940. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  941. if (std::strcmp(key, "param") == 0)
  942. {
  943. long long moduleId = 0;
  944. int paramId = 0;
  945. float paramValue = 0.f;
  946. {
  947. const ScopedSafeLocale cssl;
  948. std::sscanf(value, "%lld:%d:%f", &moduleId, &paramId, &paramValue);
  949. }
  950. rack::engine::Module* const module = context->engine->getModule(moduleId);
  951. DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);
  952. context->engine->setParamValue(module, paramId, paramValue);
  953. return;
  954. }
  955. #endif
  956. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  957. if (std::strcmp(key, "moduleInfos") == 0)
  958. {
  959. json_error_t error;
  960. json_t* const rootJ = json_loads(value, 0, &error);
  961. DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr,);
  962. const char* pluginSlug;
  963. json_t* pluginJ;
  964. json_object_foreach(rootJ, pluginSlug, pluginJ)
  965. {
  966. const char* moduleSlug;
  967. json_t* moduleJ;
  968. json_object_foreach(pluginJ, moduleSlug, moduleJ)
  969. {
  970. rack::settings::ModuleInfo m;
  971. if (json_t* const favoriteJ = json_object_get(moduleJ, "favorite"))
  972. m.favorite = json_boolean_value(favoriteJ);
  973. if (json_t* const addedJ = json_object_get(moduleJ, "added"))
  974. m.added = json_integer_value(addedJ);
  975. if (json_t* const lastAddedJ = json_object_get(moduleJ, "lastAdded"))
  976. m.lastAdded = json_number_value(lastAddedJ);
  977. rack::settings::moduleInfos[pluginSlug][moduleSlug] = m;
  978. }
  979. }
  980. json_decref(rootJ);
  981. return;
  982. }
  983. if (std::strcmp(key, "windowSize") == 0)
  984. {
  985. fState.windowSize = value;
  986. return;
  987. }
  988. #endif
  989. if (std::strcmp(key, "comment") == 0)
  990. {
  991. fState.comment = value;
  992. return;
  993. }
  994. if (std::strcmp(key, "screenshot") == 0)
  995. {
  996. fState.screenshot = value;
  997. return;
  998. }
  999. if (std::strcmp(key, "patch") != 0)
  1000. return;
  1001. if (fAutosavePath.empty())
  1002. return;
  1003. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1004. rack::system::removeRecursively(fAutosavePath);
  1005. rack::system::createDirectories(fAutosavePath);
  1006. FILE* const f = std::fopen(rack::system::join(fAutosavePath, "patch.json").c_str(), "w");
  1007. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
  1008. std::fwrite(value, std::strlen(value), 1, f);
  1009. std::fclose(f);
  1010. #else
  1011. const std::vector<uint8_t> data(d_getChunkFromBase64String(value));
  1012. DISTRHO_SAFE_ASSERT_RETURN(data.size() >= 4,);
  1013. rack::system::removeRecursively(fAutosavePath);
  1014. rack::system::createDirectories(fAutosavePath);
  1015. static constexpr const char zstdMagic[] = "\x28\xb5\x2f\xfd";
  1016. if (std::memcmp(data.data(), zstdMagic, sizeof(zstdMagic)) != 0)
  1017. {
  1018. FILE* const f = std::fopen(rack::system::join(fAutosavePath, "patch.json").c_str(), "w");
  1019. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
  1020. std::fwrite(data.data(), data.size(), 1, f);
  1021. std::fclose(f);
  1022. }
  1023. else
  1024. {
  1025. try {
  1026. rack::system::unarchiveToDirectory(data, fAutosavePath);
  1027. } DISTRHO_SAFE_EXCEPTION_RETURN("setState unarchiveToDirectory",);
  1028. }
  1029. #endif
  1030. const ScopedContext sc(this);
  1031. try {
  1032. context->patch->loadAutosave();
  1033. } catch(const rack::Exception& e) {
  1034. d_stderr(e.what());
  1035. } DISTRHO_SAFE_EXCEPTION_RETURN("setState loadAutosave",);
  1036. // context->history->setSaved();
  1037. }
  1038. /* --------------------------------------------------------------------------------------------------------
  1039. * Process */
  1040. void activate() override
  1041. {
  1042. context->bufferSize = getBufferSize();
  1043. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1044. fAudioBufferCopy = new float*[DISTRHO_PLUGIN_NUM_INPUTS];
  1045. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1046. fAudioBufferCopy[i] = new float[context->bufferSize];
  1047. #endif
  1048. fNextExpectedFrame = 0;
  1049. }
  1050. void deactivate() override
  1051. {
  1052. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1053. if (fAudioBufferCopy != nullptr)
  1054. {
  1055. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1056. delete[] fAudioBufferCopy[i];
  1057. delete[] fAudioBufferCopy;
  1058. fAudioBufferCopy = nullptr;
  1059. }
  1060. #endif
  1061. }
  1062. void run(const float** const inputs, float** const outputs, const uint32_t frames,
  1063. const MidiEvent* const midiEvents, const uint32_t midiEventCount) override
  1064. {
  1065. const ScopedDenormalDisable sdd;
  1066. rack::contextSet(context);
  1067. const bool bypassed = context->bypassed;
  1068. {
  1069. const TimePosition& timePos(getTimePosition());
  1070. bool reset = timePos.playing && (timePos.frame == 0 || d_isDiffHigherThanLimit(fNextExpectedFrame, timePos.frame, (uint64_t)2));
  1071. // ignore hosts which cannot supply time frame position
  1072. if (context->playing == timePos.playing && timePos.frame == 0 && context->frame == 0)
  1073. reset = false;
  1074. context->playing = timePos.playing;
  1075. context->bbtValid = timePos.bbt.valid;
  1076. context->frame = timePos.frame;
  1077. if (timePos.bbt.valid)
  1078. {
  1079. const double samplesPerTick = 60.0 * getSampleRate()
  1080. / timePos.bbt.beatsPerMinute
  1081. / timePos.bbt.ticksPerBeat;
  1082. context->bar = timePos.bbt.bar;
  1083. context->beat = timePos.bbt.beat;
  1084. context->beatsPerBar = timePos.bbt.beatsPerBar;
  1085. context->beatType = timePos.bbt.beatType;
  1086. context->barStartTick = timePos.bbt.barStartTick;
  1087. context->beatsPerMinute = timePos.bbt.beatsPerMinute;
  1088. context->tick = timePos.bbt.tick;
  1089. context->ticksPerBeat = timePos.bbt.ticksPerBeat;
  1090. context->ticksPerClock = timePos.bbt.ticksPerBeat / timePos.bbt.beatType;
  1091. context->ticksPerFrame = 1.0 / samplesPerTick;
  1092. context->tickClock = std::fmod(timePos.bbt.tick, context->ticksPerClock);
  1093. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1094. fMiniReportValues[kCardinalParameterMiniTimeBar - kCardinalParameterStartMini] = timePos.bbt.bar;
  1095. fMiniReportValues[kCardinalParameterMiniTimeBeat - kCardinalParameterStartMini] = timePos.bbt.beat;
  1096. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerBar - kCardinalParameterStartMini] = timePos.bbt.beatsPerBar;
  1097. fMiniReportValues[kCardinalParameterMiniTimeBeatType - kCardinalParameterStartMini] = timePos.bbt.beatType;
  1098. fMiniReportValues[kCardinalParameterMiniTimeBarStartTick - kCardinalParameterStartMini] = timePos.bbt.barStartTick;
  1099. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerMinute - kCardinalParameterStartMini] = timePos.bbt.beatsPerMinute;
  1100. fMiniReportValues[kCardinalParameterMiniTimeTick - kCardinalParameterStartMini] = timePos.bbt.tick;
  1101. fMiniReportValues[kCardinalParameterMiniTimeTicksPerBeat - kCardinalParameterStartMini] = timePos.bbt.ticksPerBeat;
  1102. #endif
  1103. }
  1104. context->reset = reset;
  1105. fNextExpectedFrame = timePos.playing ? timePos.frame + frames : 0;
  1106. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1107. const int flags = (timePos.playing ? 0x1 : 0x0)
  1108. | (timePos.bbt.valid ? 0x2 : 0x0)
  1109. | (reset ? 0x4 : 0x0);
  1110. fMiniReportValues[kCardinalParameterMiniTimeFlags - kCardinalParameterStartMini] = flags;
  1111. fMiniReportValues[kCardinalParameterMiniTimeFrame - kCardinalParameterStartMini] = timePos.frame / getSampleRate();
  1112. #endif
  1113. }
  1114. // separate buffers, use them
  1115. if (inputs != outputs && (inputs == nullptr || inputs[0] != outputs[0]))
  1116. {
  1117. context->dataIns = inputs;
  1118. context->dataOuts = outputs;
  1119. }
  1120. // inline processing, use a safe copy
  1121. else
  1122. {
  1123. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1124. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1125. {
  1126. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  1127. // can be null on main and mini variants
  1128. if (inputs[i] != nullptr)
  1129. #endif
  1130. std::memcpy(fAudioBufferCopy[i], inputs[i], sizeof(float)*frames);
  1131. }
  1132. context->dataIns = fAudioBufferCopy;
  1133. #else
  1134. context->dataIns = nullptr;
  1135. #endif
  1136. context->dataOuts = outputs;
  1137. }
  1138. for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  1139. {
  1140. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  1141. // can be null on main and mini variants
  1142. if (outputs[i] != nullptr)
  1143. #endif
  1144. std::memset(outputs[i], 0, sizeof(float)*frames);
  1145. }
  1146. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1147. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1148. fMiniReportValues[i] = context->dataIns[i][0];
  1149. #endif
  1150. if (bypassed)
  1151. {
  1152. if (fWasBypassed != bypassed)
  1153. {
  1154. context->midiEvents = bypassMidiEvents;
  1155. context->midiEventCount = 16;
  1156. }
  1157. else
  1158. {
  1159. context->midiEvents = nullptr;
  1160. context->midiEventCount = 0;
  1161. }
  1162. }
  1163. else
  1164. {
  1165. context->midiEvents = midiEvents;
  1166. context->midiEventCount = midiEventCount;
  1167. }
  1168. ++context->processCounter;
  1169. context->engine->stepBlock(frames);
  1170. fWasBypassed = bypassed;
  1171. }
  1172. void sampleRateChanged(const double newSampleRate) override
  1173. {
  1174. rack::contextSet(context);
  1175. rack::settings::sampleRate = newSampleRate;
  1176. context->sampleRate = newSampleRate;
  1177. context->engine->setSampleRate(newSampleRate);
  1178. }
  1179. // -------------------------------------------------------------------------------------------------------
  1180. private:
  1181. /**
  1182. Set our plugin class as non-copyable and add a leak detector just in case.
  1183. */
  1184. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalPlugin)
  1185. };
  1186. CardinalPluginContext* getRackContextFromPlugin(void* const ptr)
  1187. {
  1188. return static_cast<CardinalPlugin*>(ptr)->getRackContext();
  1189. }
  1190. /* ------------------------------------------------------------------------------------------------------------
  1191. * Plugin entry point, called by DPF to create a new plugin instance. */
  1192. Plugin* createPlugin()
  1193. {
  1194. return new CardinalPlugin();
  1195. }
  1196. // --------------------------------------------------------------------------------------------------------------------
  1197. END_NAMESPACE_DISTRHO