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.

1316 lines
51KB

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