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.

1335 lines
52KB

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