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.

1338 lines
52KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2026 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->parameters;
  289. delete context;
  290. rack::contextSet(nullptr);
  291. }
  292. if (! fAutosavePath.empty())
  293. rack::system::removeRecursively(fAutosavePath);
  294. }
  295. CardinalPluginContext* getRackContext() const noexcept
  296. {
  297. return context;
  298. }
  299. #ifdef HAVE_LIBLO
  300. bool startRemoteServer(const char* const port) override
  301. {
  302. if (fInitializer->remotePluginInstance != nullptr)
  303. return false;
  304. if (fInitializer->startRemoteServer(port))
  305. {
  306. fInitializer->remotePluginInstance = this;
  307. return true;
  308. }
  309. return false;
  310. }
  311. void stopRemoteServer() override
  312. {
  313. DISTRHO_SAFE_ASSERT_RETURN(fInitializer->remotePluginInstance == this,);
  314. fInitializer->remotePluginInstance = nullptr;
  315. fInitializer->stopRemoteServer();
  316. }
  317. void stepRemoteServer() override
  318. {
  319. DISTRHO_SAFE_ASSERT_RETURN(fInitializer->remotePluginInstance == this,);
  320. fInitializer->stepRemoteServer();
  321. }
  322. #endif
  323. protected:
  324. /* --------------------------------------------------------------------------------------------------------
  325. * Information */
  326. const char* getLabel() const override
  327. {
  328. return DISTRHO_PLUGIN_LABEL;
  329. }
  330. const char* getDescription() const override
  331. {
  332. return ""
  333. "Cardinal is a free and open-source virtual modular synthesizer plugin.\n"
  334. "It is based on the popular VCV Rack but with a focus on being a fully self-contained plugin version.\n"
  335. "It is not an official VCV project, and it is not affiliated with it in any way.\n"
  336. "\n"
  337. "Cardinal contains Rack, some 3rd-party modules and a few internal utilities.\n"
  338. "It does not load external modules and does not connect to the official Rack library/store.\n";
  339. }
  340. const char* getMaker() const override
  341. {
  342. return "DISTRHO";
  343. }
  344. const char* getHomePage() const override
  345. {
  346. return "https://github.com/DISTRHO/Cardinal";
  347. }
  348. const char* getLicense() const override
  349. {
  350. return "GPLv3+";
  351. }
  352. uint32_t getVersion() const override
  353. {
  354. return d_version(0, 26, 2);
  355. }
  356. int64_t getUniqueId() const override
  357. {
  358. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_NATIVE
  359. return d_cconst('d', 'C', 'd', 'n');
  360. #elif CARDINAL_VARIANT_MINI
  361. return d_cconst('d', 'C', 'd', 'M');
  362. #elif CARDINAL_VARIANT_FX
  363. return d_cconst('d', 'C', 'n', 'F');
  364. #elif CARDINAL_VARIANT_SYNTH
  365. return d_cconst('d', 'C', 'n', 'S');
  366. #else
  367. #error cardinal variant not set
  368. #endif
  369. }
  370. /* --------------------------------------------------------------------------------------------------------
  371. * Init */
  372. void initAudioPort(const bool input, uint32_t index, AudioPort& port) override
  373. {
  374. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  375. static_assert(CARDINAL_NUM_AUDIO_INPUTS == CARDINAL_NUM_AUDIO_OUTPUTS, "inputs == outputs");
  376. if (index < CARDINAL_NUM_AUDIO_INPUTS)
  377. {
  378. #if CARDINAL_VARIANT_MINI
  379. port.groupId = kPortGroupStereo;
  380. #else
  381. port.groupId = index / 2;
  382. #endif
  383. }
  384. else
  385. {
  386. port.hints = kAudioPortIsCV | kCVPortHasPositiveUnipolarRange | kCVPortHasScaledRange | kCVPortIsOptional;
  387. index -= CARDINAL_NUM_AUDIO_INPUTS;
  388. }
  389. #elif CARDINAL_VARIANT_NATIVE || CARDINAL_VARIANT_FX || CARDINAL_VARIANT_SYNTH
  390. if (index < 2)
  391. port.groupId = kPortGroupStereo;
  392. #endif
  393. CardinalBasePlugin::initAudioPort(input, index, port);
  394. }
  395. #if CARDINAL_VARIANT_MAIN
  396. void initPortGroup(const uint32_t index, PortGroup& portGroup) override
  397. {
  398. switch (index)
  399. {
  400. case 0:
  401. portGroup.name = "Audio 1+2";
  402. portGroup.symbol = "audio_1_and_2";
  403. break;
  404. case 1:
  405. portGroup.name = "Audio 3+4";
  406. portGroup.symbol = "audio_3_and_4";
  407. break;
  408. case 2:
  409. portGroup.name = "Audio 5+6";
  410. portGroup.symbol = "audio_5_and_6";
  411. break;
  412. case 3:
  413. portGroup.name = "Audio 7+8";
  414. portGroup.symbol = "audio_7_and_8";
  415. break;
  416. }
  417. }
  418. #endif
  419. void initParameter(const uint32_t index, Parameter& parameter) override
  420. {
  421. if (index < kCardinalParameterCountAtModules)
  422. {
  423. parameter.name = "Parameter ";
  424. parameter.name += String(index + 1);
  425. parameter.symbol = "param_";
  426. parameter.symbol += String(index + 1);
  427. parameter.unit = "v";
  428. parameter.hints = kParameterIsAutomatable;
  429. parameter.ranges.def = 0.0f;
  430. parameter.ranges.min = 0.0f;
  431. parameter.ranges.max = 10.0f;
  432. return;
  433. }
  434. if (index == kCardinalParameterBypass)
  435. {
  436. parameter.initDesignation(kParameterDesignationBypass);
  437. return;
  438. }
  439. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  440. if (index < kCardinalParameterCountAtWindow)
  441. {
  442. switch (index - kCardinalParameterStartWindow)
  443. {
  444. case kWindowParameterShowTooltips:
  445. parameter.name = "Show tooltips";
  446. parameter.symbol = "tooltips";
  447. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  448. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  449. parameter.hints |= kParameterIsHidden;
  450. #endif
  451. parameter.ranges.def = rack::settings::tooltips ? 1.f : 0.f;
  452. parameter.ranges.min = 0.0f;
  453. parameter.ranges.max = 1.0f;
  454. break;
  455. case kWindowParameterCableOpacity:
  456. parameter.name = "Cable opacity";
  457. parameter.symbol = "cableOpacity";
  458. parameter.unit = "%";
  459. parameter.hints = kParameterIsAutomatable;
  460. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  461. parameter.hints |= kParameterIsHidden;
  462. #endif
  463. parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::cableOpacity * 100));
  464. parameter.ranges.min = 0.0f;
  465. parameter.ranges.max = 100.0f;
  466. break;
  467. case kWindowParameterCableTension:
  468. parameter.name = "Cable tension";
  469. parameter.symbol = "cableTension";
  470. parameter.unit = "%";
  471. parameter.hints = kParameterIsAutomatable;
  472. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  473. parameter.hints |= kParameterIsHidden;
  474. #endif
  475. parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::cableTension * 100));
  476. parameter.ranges.min = 0.0f;
  477. parameter.ranges.max = 100.0f;
  478. break;
  479. case kWindowParameterRackBrightness:
  480. parameter.name = "Room brightness";
  481. parameter.symbol = "rackBrightness";
  482. parameter.unit = "%";
  483. parameter.hints = kParameterIsAutomatable;
  484. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  485. parameter.hints |= kParameterIsHidden;
  486. #endif
  487. parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::rackBrightness * 100));
  488. parameter.ranges.min = 0.0f;
  489. parameter.ranges.max = 100.0f;
  490. break;
  491. case kWindowParameterHaloBrightness:
  492. parameter.name = "Light Bloom";
  493. parameter.symbol = "haloBrightness";
  494. parameter.unit = "%";
  495. parameter.hints = kParameterIsAutomatable;
  496. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  497. parameter.hints |= kParameterIsHidden;
  498. #endif
  499. parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::haloBrightness * 100));
  500. parameter.ranges.min = 0.0f;
  501. parameter.ranges.max = 100.0f;
  502. break;
  503. case kWindowParameterKnobMode:
  504. parameter.name = "Knob mode";
  505. parameter.symbol = "knobMode";
  506. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  507. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  508. parameter.hints |= kParameterIsHidden;
  509. #endif
  510. parameter.ranges.def = RackKnobModeToFloat(rack::settings::knobMode);
  511. parameter.ranges.min = 0.0f;
  512. parameter.ranges.max = 2.0f;
  513. parameter.enumValues.count = 3;
  514. parameter.enumValues.restrictedMode = true;
  515. parameter.enumValues.values = new ParameterEnumerationValue[3];
  516. parameter.enumValues.values[0].label = "Linear";
  517. parameter.enumValues.values[0].value = 0.0f;
  518. parameter.enumValues.values[1].label = "Absolute rotary";
  519. parameter.enumValues.values[1].value = 1.0f;
  520. parameter.enumValues.values[2].label = "Relative rotary";
  521. parameter.enumValues.values[2].value = 2.0f;
  522. break;
  523. case kWindowParameterWheelKnobControl:
  524. parameter.name = "Scroll wheel knob control";
  525. parameter.symbol = "knobScroll";
  526. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  527. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  528. parameter.hints |= kParameterIsHidden;
  529. #endif
  530. parameter.ranges.def = rack::settings::knobScroll ? 1.f : 0.f;
  531. parameter.ranges.min = 0.0f;
  532. parameter.ranges.max = 1.0f;
  533. break;
  534. case kWindowParameterWheelSensitivity:
  535. parameter.name = "Scroll wheel knob sensitivity";
  536. parameter.symbol = "knobScrollSensitivity";
  537. parameter.hints = kParameterIsAutomatable|kParameterIsLogarithmic;
  538. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  539. parameter.hints |= kParameterIsHidden;
  540. #endif
  541. parameter.ranges.def = std::min(10.f, std::max(0.1f, rack::settings::knobScrollSensitivity * 1000));
  542. parameter.ranges.min = 0.1f;
  543. parameter.ranges.max = 10.0f;
  544. break;
  545. case kWindowParameterLockModulePositions:
  546. parameter.name = "Lock module positions";
  547. parameter.symbol = "lockModules";
  548. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  549. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  550. parameter.hints |= kParameterIsHidden;
  551. #endif
  552. parameter.ranges.def = rack::settings::lockModules ? 1.f : 0.f;
  553. parameter.ranges.min = 0.0f;
  554. parameter.ranges.max = 1.0f;
  555. break;
  556. case kWindowParameterUpdateRateLimit:
  557. parameter.name = "Update rate limit";
  558. parameter.symbol = "rateLimit";
  559. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  560. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  561. parameter.hints |= kParameterIsHidden;
  562. #endif
  563. parameter.ranges.def = 0.0f;
  564. parameter.ranges.min = 0.0f;
  565. parameter.ranges.max = 2.0f;
  566. parameter.enumValues.count = 3;
  567. parameter.enumValues.restrictedMode = true;
  568. parameter.enumValues.values = new ParameterEnumerationValue[3];
  569. parameter.enumValues.values[0].label = "None";
  570. parameter.enumValues.values[0].value = 0.0f;
  571. parameter.enumValues.values[1].label = "2x";
  572. parameter.enumValues.values[1].value = 1.0f;
  573. parameter.enumValues.values[2].label = "4x";
  574. parameter.enumValues.values[2].value = 2.0f;
  575. break;
  576. case kWindowParameterBrowserSort:
  577. parameter.name = "Browser sort";
  578. parameter.symbol = "browserSort";
  579. parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
  580. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  581. parameter.hints |= kParameterIsHidden;
  582. #endif
  583. parameter.ranges.def = std::min(rack::settings::BROWSER_SORT_RANDOM,
  584. std::max(rack::settings::BROWSER_SORT_UPDATED,
  585. rack::settings::browserSort));
  586. parameter.ranges.min = rack::settings::BROWSER_SORT_UPDATED;
  587. parameter.ranges.max = rack::settings::BROWSER_SORT_RANDOM;
  588. parameter.enumValues.count = 6;
  589. parameter.enumValues.restrictedMode = true;
  590. parameter.enumValues.values = new ParameterEnumerationValue[6];
  591. parameter.enumValues.values[0].label = "Updated";
  592. parameter.enumValues.values[0].value = rack::settings::BROWSER_SORT_UPDATED;
  593. parameter.enumValues.values[1].label = "Last used";
  594. parameter.enumValues.values[1].value = rack::settings::BROWSER_SORT_LAST_USED;
  595. parameter.enumValues.values[2].label = "Most used";
  596. parameter.enumValues.values[2].value = rack::settings::BROWSER_SORT_MOST_USED;
  597. parameter.enumValues.values[3].label = "Brand";
  598. parameter.enumValues.values[3].value = rack::settings::BROWSER_SORT_BRAND;
  599. parameter.enumValues.values[4].label = "Name";
  600. parameter.enumValues.values[4].value = rack::settings::BROWSER_SORT_NAME;
  601. parameter.enumValues.values[5].label = "Random";
  602. parameter.enumValues.values[5].value = rack::settings::BROWSER_SORT_RANDOM;
  603. break;
  604. case kWindowParameterBrowserZoom:
  605. parameter.name = "Browser zoom";
  606. parameter.symbol = "browserZoom";
  607. parameter.hints = kParameterIsAutomatable;
  608. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  609. parameter.hints |= kParameterIsHidden;
  610. #endif
  611. parameter.unit = "%";
  612. parameter.ranges.def = std::min(200.f, std::max(25.f, std::pow(2.f, rack::settings::browserZoom) * 100.0f));
  613. parameter.ranges.min = 25.0f;
  614. parameter.ranges.max = 200.0f;
  615. parameter.enumValues.count = 7;
  616. parameter.enumValues.restrictedMode = true;
  617. parameter.enumValues.values = new ParameterEnumerationValue[7];
  618. parameter.enumValues.values[0].label = "25";
  619. parameter.enumValues.values[0].value = 25.0f;
  620. parameter.enumValues.values[1].label = "35";
  621. parameter.enumValues.values[1].value = 35.0f;
  622. parameter.enumValues.values[2].label = "50";
  623. parameter.enumValues.values[2].value = 50.0f;
  624. parameter.enumValues.values[3].label = "71";
  625. parameter.enumValues.values[3].value = 71.0f;
  626. parameter.enumValues.values[4].label = "100";
  627. parameter.enumValues.values[4].value = 100.0f;
  628. parameter.enumValues.values[5].label = "141";
  629. parameter.enumValues.values[5].value = 141.0f;
  630. parameter.enumValues.values[6].label = "200";
  631. parameter.enumValues.values[6].value = 200.0f;
  632. break;
  633. case kWindowParameterInvertZoom:
  634. parameter.name = "Invert zoom";
  635. parameter.symbol = "invertZoom";
  636. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  637. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  638. parameter.hints |= kParameterIsHidden;
  639. #endif
  640. parameter.ranges.def = rack::settings::invertZoom ? 1.f : 0.f;
  641. parameter.ranges.min = 0.0f;
  642. parameter.ranges.max = 1.0f;
  643. break;
  644. case kWindowParameterSqueezeModulePositions:
  645. parameter.name = "Auto-squeeze module positions";
  646. parameter.symbol = "squeezeModules";
  647. parameter.hints = kParameterIsAutomatable|kParameterIsInteger|kParameterIsBoolean;
  648. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  649. parameter.hints |= kParameterIsHidden;
  650. #endif
  651. parameter.ranges.def = rack::settings::squeezeModules ? 1.f : 0.f;
  652. parameter.ranges.min = 0.0f;
  653. parameter.ranges.max = 1.0f;
  654. break;
  655. }
  656. }
  657. #endif
  658. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  659. switch (index)
  660. {
  661. case kCardinalParameterMiniAudioIn1:
  662. parameter.name = "Report Audio Input 1";
  663. parameter.symbol = "r_audio_in_1";
  664. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  665. parameter.ranges.def = 0.0f;
  666. parameter.ranges.min = 0.0f;
  667. parameter.ranges.max = 1.0f;
  668. break;
  669. case kCardinalParameterMiniAudioIn2:
  670. parameter.name = "Report Audio Input 2";
  671. parameter.symbol = "r_audio_in_2";
  672. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  673. parameter.ranges.def = 0.0f;
  674. parameter.ranges.min = 0.0f;
  675. parameter.ranges.max = 1.0f;
  676. break;
  677. case kCardinalParameterMiniCVIn1:
  678. parameter.name = "Report CV Input 1";
  679. parameter.symbol = "r_cv_in_1";
  680. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  681. parameter.ranges.def = -10.0f;
  682. parameter.ranges.min = 0.0f;
  683. parameter.ranges.max = 10.0f;
  684. break;
  685. case kCardinalParameterMiniCVIn2:
  686. parameter.name = "Report CV Input 2";
  687. parameter.symbol = "r_cv_in_2";
  688. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  689. parameter.ranges.def = -10.0f;
  690. parameter.ranges.min = 0.0f;
  691. parameter.ranges.max = 10.0f;
  692. break;
  693. case kCardinalParameterMiniCVIn3:
  694. parameter.name = "Report CV Input 3";
  695. parameter.symbol = "r_cv_in_3";
  696. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  697. parameter.ranges.def = -10.0f;
  698. parameter.ranges.min = 0.0f;
  699. parameter.ranges.max = 10.0f;
  700. break;
  701. case kCardinalParameterMiniCVIn4:
  702. parameter.name = "Report CV Input 4";
  703. parameter.symbol = "r_cv_in_4";
  704. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  705. parameter.ranges.def = -10.0f;
  706. parameter.ranges.min = 0.0f;
  707. parameter.ranges.max = 10.0f;
  708. break;
  709. case kCardinalParameterMiniCVIn5:
  710. parameter.name = "Report CV Input 5";
  711. parameter.symbol = "r_cv_in_5";
  712. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  713. parameter.ranges.def = -10.0f;
  714. parameter.ranges.min = 0.0f;
  715. parameter.ranges.max = 10.0f;
  716. break;
  717. case kCardinalParameterMiniTimeFlags:
  718. parameter.name = "Report Time Flags";
  719. parameter.symbol = "r_time_flags";
  720. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  721. parameter.ranges.def = 0x0;
  722. parameter.ranges.min = 0x0;
  723. parameter.ranges.max = 0x7;
  724. break;
  725. case kCardinalParameterMiniTimeBar:
  726. parameter.name = "Report Time Bar";
  727. parameter.symbol = "r_time_bar";
  728. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  729. parameter.ranges.def = 1.0f;
  730. parameter.ranges.min = 1.0f;
  731. parameter.ranges.max = FLT_MAX;
  732. break;
  733. case kCardinalParameterMiniTimeBeat:
  734. parameter.name = "Report Time Beat";
  735. parameter.symbol = "r_time_beat";
  736. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  737. parameter.ranges.def = 1.0f;
  738. parameter.ranges.min = 1.0f;
  739. parameter.ranges.max = 128.0f;
  740. break;
  741. case kCardinalParameterMiniTimeBeatsPerBar:
  742. parameter.name = "Report Time Beats Per Bar";
  743. parameter.symbol = "r_time_beatsPerBar";
  744. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  745. parameter.ranges.def = 4.0f;
  746. parameter.ranges.min = 0.0f;
  747. parameter.ranges.max = 128.0f;
  748. break;
  749. case kCardinalParameterMiniTimeBeatType:
  750. parameter.name = "Report Time Beat Type";
  751. parameter.symbol = "r_time_beatType";
  752. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  753. parameter.ranges.def = 4.0f;
  754. parameter.ranges.min = 0.0f;
  755. parameter.ranges.max = 128.0f;
  756. break;
  757. case kCardinalParameterMiniTimeFrame:
  758. parameter.name = "Report Time Frame";
  759. parameter.symbol = "r_time_frame";
  760. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  761. parameter.ranges.def = 0.0f;
  762. parameter.ranges.min = 0.0f;
  763. parameter.ranges.max = FLT_MAX;
  764. break;
  765. case kCardinalParameterMiniTimeBarStartTick:
  766. parameter.name = "Report Time BarStartTick";
  767. parameter.symbol = "r_time_barStartTick";
  768. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  769. parameter.ranges.def = 0.0f;
  770. parameter.ranges.min = 0.0f;
  771. parameter.ranges.max = FLT_MAX;
  772. break;
  773. case kCardinalParameterMiniTimeBeatsPerMinute:
  774. parameter.name = "Report Time Beats Per Minute";
  775. parameter.symbol = "r_time_bpm";
  776. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  777. parameter.ranges.def = 20.0f;
  778. parameter.ranges.min = 120.0f;
  779. parameter.ranges.max = 999.0f;
  780. break;
  781. case kCardinalParameterMiniTimeTick:
  782. parameter.name = "Report Time Tick";
  783. parameter.symbol = "r_time_tick";
  784. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  785. parameter.ranges.def = 0.0f;
  786. parameter.ranges.min = 0.0f;
  787. parameter.ranges.max = 8192.0f;
  788. break;
  789. case kCardinalParameterMiniTimeTicksPerBeat:
  790. parameter.name = "Report Time Ticks Per Beat";
  791. parameter.symbol = "r_time_ticksPerBeat";
  792. parameter.hints = kParameterIsAutomatable|kParameterIsOutput;
  793. parameter.ranges.def = 0.0f;
  794. parameter.ranges.min = 0.0f;
  795. parameter.ranges.max = 8192.0f;
  796. break;
  797. }
  798. #endif
  799. }
  800. void initState(const uint32_t index, State& state) override
  801. {
  802. switch (index)
  803. {
  804. case kCardinalStatePatch:
  805. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  806. state.hints = kStateIsHostReadable;
  807. #else
  808. state.hints = kStateIsOnlyForDSP | kStateIsBase64Blob;
  809. #endif
  810. if (FILE* const f = std::fopen(context->patch->factoryTemplatePath.c_str(), "r"))
  811. {
  812. std::fseek(f, 0, SEEK_END);
  813. if (const long fileSize = std::ftell(f))
  814. {
  815. std::fseek(f, 0, SEEK_SET);
  816. char* const fileContent = new char[fileSize+1];
  817. if (std::fread(fileContent, fileSize, 1, f) == 1)
  818. {
  819. fileContent[fileSize] = '\0';
  820. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  821. state.defaultValue = fileContent;
  822. #else
  823. state.defaultValue = String::asBase64(fileContent, fileSize);
  824. #endif
  825. }
  826. delete[] fileContent;
  827. }
  828. std::fclose(f);
  829. }
  830. state.key = "patch";
  831. state.label = "Patch";
  832. break;
  833. case kCardinalStateScreenshot:
  834. state.hints = kStateIsHostReadable | kStateIsBase64Blob;
  835. state.key = "screenshot";
  836. state.label = "Screenshot";
  837. break;
  838. case kCardinalStateComment:
  839. state.hints = kStateIsHostWritable;
  840. state.key = "comment";
  841. state.label = "Comment";
  842. break;
  843. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  844. case kCardinalStateWindowSize:
  845. state.hints = kStateIsOnlyForUI;
  846. // state.defaultValue = String("%d:%d", DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT);
  847. state.key = "windowSize";
  848. state.label = "Window size";
  849. break;
  850. #endif
  851. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  852. case kCardinalStateParamChange:
  853. state.hints = kStateIsHostReadable | kStateIsOnlyForDSP;
  854. state.key = "param";
  855. state.label = "ParamChange";
  856. break;
  857. #endif
  858. }
  859. }
  860. /* --------------------------------------------------------------------------------------------------------
  861. * Internal data */
  862. float getParameterValue(uint32_t index) const override
  863. {
  864. // host mapped parameters
  865. if (index < kCardinalParameterCountAtModules)
  866. return context->parameters[index];
  867. // bypass
  868. if (index == kCardinalParameterBypass)
  869. return context->bypassed ? 1.0f : 0.0f;
  870. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  871. if (index < kCardinalParameterCountAtWindow)
  872. return fWindowParameters[index - kCardinalParameterStartWindow];
  873. #endif
  874. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  875. if (index < kCardinalParameterCountAtMini)
  876. return fMiniReportValues[index - kCardinalParameterStartMini];
  877. #endif
  878. return 0.0f;
  879. }
  880. void setParameterValue(uint32_t index, float value) override
  881. {
  882. // host mapped parameters
  883. if (index < kCardinalParameterCountAtModules)
  884. {
  885. context->parameters[index] = value;
  886. return;
  887. }
  888. // bypass
  889. if (index == kCardinalParameterBypass)
  890. {
  891. context->bypassed = value > 0.5f;
  892. return;
  893. }
  894. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  895. if (index < kCardinalParameterCountAtWindow)
  896. {
  897. fWindowParameters[index - kCardinalParameterStartWindow] = value;
  898. return;
  899. }
  900. #endif
  901. }
  902. String getState(const char* const key) const override
  903. {
  904. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  905. if (std::strcmp(key, "windowSize") == 0)
  906. return fState.windowSize;
  907. #endif
  908. if (std::strcmp(key, "comment") == 0)
  909. return fState.comment;
  910. if (std::strcmp(key, "screenshot") == 0)
  911. return fState.screenshot;
  912. if (std::strcmp(key, "patch") != 0)
  913. return String();
  914. if (fAutosavePath.empty())
  915. return String();
  916. std::vector<uint8_t> data;
  917. {
  918. const ScopedContext sc(this);
  919. context->engine->prepareSave();
  920. context->patch->saveAutosave();
  921. context->patch->cleanAutosave();
  922. // context->history->setSaved();
  923. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  924. FILE* const f = std::fopen(rack::system::join(context->patch->autosavePath, "patch.json").c_str(), "r");
  925. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr, String());
  926. DEFER({
  927. std::fclose(f);
  928. });
  929. std::fseek(f, 0, SEEK_END);
  930. const long fileSize = std::ftell(f);
  931. DISTRHO_SAFE_ASSERT_RETURN(fileSize > 0, String());
  932. std::fseek(f, 0, SEEK_SET);
  933. char* const fileContent = static_cast<char*>(std::malloc(fileSize+1));
  934. DISTRHO_SAFE_ASSERT_RETURN(std::fread(fileContent, fileSize, 1, f) == 1, String());
  935. fileContent[fileSize] = '\0';
  936. return String(fileContent, false);
  937. #else
  938. try {
  939. data = rack::system::archiveDirectory(fAutosavePath, 1);
  940. } DISTRHO_SAFE_EXCEPTION_RETURN("getState archiveDirectory", String());
  941. #endif
  942. }
  943. return String::asBase64(data.data(), data.size());
  944. }
  945. void setState(const char* const key, const char* const value) override
  946. {
  947. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  948. if (std::strcmp(key, "param") == 0)
  949. {
  950. long long moduleId = 0;
  951. int paramId = 0;
  952. float paramValue = 0.f;
  953. {
  954. const ScopedSafeLocale cssl;
  955. std::sscanf(value, "%lld:%d:%f", &moduleId, &paramId, &paramValue);
  956. }
  957. rack::engine::Module* const module = context->engine->getModule(moduleId);
  958. DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);
  959. context->engine->setParamValue(module, paramId, paramValue);
  960. return;
  961. }
  962. #endif
  963. #if CARDINAL_VARIANT_MINI || !defined(HEADLESS)
  964. if (std::strcmp(key, "windowSize") == 0)
  965. {
  966. fState.windowSize = value;
  967. return;
  968. }
  969. #endif
  970. if (std::strcmp(key, "comment") == 0)
  971. {
  972. fState.comment = value;
  973. return;
  974. }
  975. if (std::strcmp(key, "screenshot") == 0)
  976. {
  977. fState.screenshot = value;
  978. return;
  979. }
  980. if (std::strcmp(key, "patch") != 0)
  981. return;
  982. if (fAutosavePath.empty())
  983. return;
  984. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  985. rack::system::removeRecursively(fAutosavePath);
  986. rack::system::createDirectories(fAutosavePath);
  987. FILE* const f = std::fopen(rack::system::join(fAutosavePath, "patch.json").c_str(), "w");
  988. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
  989. std::fwrite(value, std::strlen(value), 1, f);
  990. std::fclose(f);
  991. #else
  992. const std::vector<uint8_t> data(d_getChunkFromBase64String(value));
  993. DISTRHO_SAFE_ASSERT_RETURN(data.size() >= 4,);
  994. rack::system::removeRecursively(fAutosavePath);
  995. rack::system::createDirectories(fAutosavePath);
  996. static constexpr const uint8_t zstdMagic[4] = { 0x28, 0xb5, 0x2f, 0xfd };
  997. if (std::memcmp(data.data(), zstdMagic, sizeof(zstdMagic)) != 0)
  998. {
  999. FILE* const f = std::fopen(rack::system::join(fAutosavePath, "patch.json").c_str(), "w");
  1000. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
  1001. std::fwrite(data.data(), data.size(), 1, f);
  1002. std::fclose(f);
  1003. }
  1004. else
  1005. {
  1006. try {
  1007. rack::system::unarchiveToDirectory(data, fAutosavePath);
  1008. } DISTRHO_SAFE_EXCEPTION_RETURN("setState unarchiveToDirectory",);
  1009. }
  1010. #endif
  1011. const ScopedContext sc(this);
  1012. try {
  1013. context->patch->loadAutosave();
  1014. } catch(const rack::Exception& e) {
  1015. d_stderr(e.what());
  1016. } DISTRHO_SAFE_EXCEPTION_RETURN("setState loadAutosave",);
  1017. // context->history->setSaved();
  1018. }
  1019. /* --------------------------------------------------------------------------------------------------------
  1020. * Process */
  1021. void activate() override
  1022. {
  1023. context->bufferSize = getBufferSize();
  1024. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1025. fAudioBufferCopy = new float*[DISTRHO_PLUGIN_NUM_INPUTS];
  1026. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1027. {
  1028. fAudioBufferCopy[i] = new float[context->bufferSize];
  1029. std::memset(fAudioBufferCopy[i], 0, sizeof(float) * context->bufferSize);
  1030. }
  1031. #endif
  1032. fNextExpectedFrame = 0;
  1033. }
  1034. void deactivate() override
  1035. {
  1036. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1037. if (fAudioBufferCopy != nullptr)
  1038. {
  1039. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1040. delete[] fAudioBufferCopy[i];
  1041. delete[] fAudioBufferCopy;
  1042. fAudioBufferCopy = nullptr;
  1043. }
  1044. #endif
  1045. }
  1046. void run(const float** const inputs, float** const outputs, const uint32_t frames,
  1047. const MidiEvent* const midiEvents, const uint32_t midiEventCount) override
  1048. {
  1049. const ScopedDenormalDisable sdd;
  1050. rack::contextSet(context);
  1051. const bool bypassed = context->bypassed;
  1052. {
  1053. const TimePosition& timePos(getTimePosition());
  1054. bool reset = timePos.playing && (timePos.frame == 0 || d_isDiffHigherThanLimit(fNextExpectedFrame, timePos.frame, (uint64_t)2));
  1055. // ignore hosts which cannot supply time frame position
  1056. if (context->playing == timePos.playing && timePos.frame == 0 && context->frame == 0)
  1057. reset = false;
  1058. context->playing = timePos.playing;
  1059. context->bbtValid = timePos.bbt.valid;
  1060. context->frame = timePos.frame;
  1061. if (timePos.bbt.valid)
  1062. {
  1063. const double samplesPerTick = 60.0 * getSampleRate()
  1064. / timePos.bbt.beatsPerMinute
  1065. / timePos.bbt.ticksPerBeat;
  1066. context->bar = timePos.bbt.bar;
  1067. context->beat = timePos.bbt.beat;
  1068. context->beatsPerBar = timePos.bbt.beatsPerBar;
  1069. context->beatType = timePos.bbt.beatType;
  1070. context->barStartTick = timePos.bbt.barStartTick;
  1071. context->beatsPerMinute = timePos.bbt.beatsPerMinute;
  1072. context->tick = timePos.bbt.tick;
  1073. context->ticksPerBeat = timePos.bbt.ticksPerBeat;
  1074. context->ticksPerClock = timePos.bbt.ticksPerBeat / timePos.bbt.beatType;
  1075. context->ticksPerFrame = 1.0 / samplesPerTick;
  1076. context->tickClock = std::fmod(timePos.bbt.tick, context->ticksPerClock);
  1077. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1078. fMiniReportValues[kCardinalParameterMiniTimeBar - kCardinalParameterStartMini] = timePos.bbt.bar;
  1079. fMiniReportValues[kCardinalParameterMiniTimeBeat - kCardinalParameterStartMini] = timePos.bbt.beat;
  1080. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerBar - kCardinalParameterStartMini] = timePos.bbt.beatsPerBar;
  1081. fMiniReportValues[kCardinalParameterMiniTimeBeatType - kCardinalParameterStartMini] = timePos.bbt.beatType;
  1082. fMiniReportValues[kCardinalParameterMiniTimeBarStartTick - kCardinalParameterStartMini] = timePos.bbt.barStartTick;
  1083. fMiniReportValues[kCardinalParameterMiniTimeBeatsPerMinute - kCardinalParameterStartMini] = timePos.bbt.beatsPerMinute;
  1084. fMiniReportValues[kCardinalParameterMiniTimeTick - kCardinalParameterStartMini] = timePos.bbt.tick;
  1085. fMiniReportValues[kCardinalParameterMiniTimeTicksPerBeat - kCardinalParameterStartMini] = timePos.bbt.ticksPerBeat;
  1086. #endif
  1087. }
  1088. context->reset = reset;
  1089. fNextExpectedFrame = timePos.playing ? timePos.frame + frames : 0;
  1090. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1091. const int flags = (timePos.playing ? 0x1 : 0x0)
  1092. | (timePos.bbt.valid ? 0x2 : 0x0)
  1093. | (reset ? 0x4 : 0x0);
  1094. fMiniReportValues[kCardinalParameterMiniTimeFlags - kCardinalParameterStartMini] = flags;
  1095. fMiniReportValues[kCardinalParameterMiniTimeFrame - kCardinalParameterStartMini] = timePos.frame / getSampleRate();
  1096. #endif
  1097. }
  1098. // separate buffers, use them
  1099. if (inputs != outputs && (inputs == nullptr || inputs[0] != outputs[0]))
  1100. {
  1101. context->dataIns = inputs;
  1102. context->dataOuts = outputs;
  1103. }
  1104. // inline processing, use a safe copy
  1105. else
  1106. {
  1107. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  1108. for (int i=0; i<fNumActiveInputs; ++i)
  1109. {
  1110. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  1111. // can be null on main and mini variants
  1112. if (inputs[i] != nullptr)
  1113. #endif
  1114. std::memcpy(fAudioBufferCopy[i], inputs[i], sizeof(float)*frames);
  1115. }
  1116. context->dataIns = fAudioBufferCopy;
  1117. #else
  1118. context->dataIns = nullptr;
  1119. #endif
  1120. context->dataOuts = outputs;
  1121. }
  1122. for (int i=0; i<fNumActiveOutputs; ++i)
  1123. {
  1124. #if CARDINAL_VARIANT_MAIN || CARDINAL_VARIANT_MINI
  1125. // can be null on main and mini variants
  1126. if (outputs[i] != nullptr)
  1127. #endif
  1128. std::memset(outputs[i], 0, sizeof(float)*frames);
  1129. }
  1130. #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  1131. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  1132. fMiniReportValues[i] = context->dataIns[i][0];
  1133. #endif
  1134. if (bypassed)
  1135. {
  1136. if (fWasBypassed != bypassed)
  1137. {
  1138. context->midiEvents = bypassMidiEvents;
  1139. context->midiEventCount = 16;
  1140. }
  1141. else
  1142. {
  1143. context->midiEvents = nullptr;
  1144. context->midiEventCount = 0;
  1145. }
  1146. }
  1147. else
  1148. {
  1149. context->midiEvents = midiEvents;
  1150. context->midiEventCount = midiEventCount;
  1151. }
  1152. ++context->processCounter;
  1153. context->engine->stepBlock(frames);
  1154. fWasBypassed = bypassed;
  1155. }
  1156. void sampleRateChanged(const double newSampleRate) override
  1157. {
  1158. rack::contextSet(context);
  1159. rack::settings::sampleRate = newSampleRate;
  1160. context->sampleRate = newSampleRate;
  1161. context->engine->setSampleRate(newSampleRate);
  1162. }
  1163. #ifdef DISTRHO_PLUGIN_EXTRA_IO
  1164. void ioChanged(const uint16_t numInputs, const uint16_t numOutputs) override
  1165. {
  1166. fNumActiveInputs = numInputs;
  1167. fNumActiveOutputs = numOutputs;
  1168. }
  1169. #endif
  1170. // -------------------------------------------------------------------------------------------------------
  1171. private:
  1172. /**
  1173. Set our plugin class as non-copyable and add a leak detector just in case.
  1174. */
  1175. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalPlugin)
  1176. };
  1177. CardinalPluginContext* getRackContextFromPlugin(void* const ptr)
  1178. {
  1179. return static_cast<CardinalPlugin*>(ptr)->getRackContext();
  1180. }
  1181. /* ------------------------------------------------------------------------------------------------------------
  1182. * Plugin entry point, called by DPF to create a new plugin instance. */
  1183. Plugin* createPlugin()
  1184. {
  1185. return new CardinalPlugin();
  1186. }
  1187. // --------------------------------------------------------------------------------------------------------------------
  1188. END_NAMESPACE_DISTRHO