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.

1327 lines
52KB

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