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.

946 lines
30KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021 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 <asset.hpp>
  18. #include <library.hpp>
  19. #include <midi.hpp>
  20. #include <patch.hpp>
  21. #include <plugin.hpp>
  22. #include <random.hpp>
  23. #include <settings.hpp>
  24. #include <system.hpp>
  25. #include <app/Scene.hpp>
  26. #include <engine/Engine.hpp>
  27. #include <ui/common.hpp>
  28. #include <window/Window.hpp>
  29. #ifdef NDEBUG
  30. # undef DEBUG
  31. #endif
  32. #ifdef HAVE_LIBLO
  33. # include <lo/lo.h>
  34. # include "extra/Thread.hpp"
  35. #endif
  36. #include <list>
  37. #include "DistrhoPluginUtils.hpp"
  38. #include "PluginDriver.hpp"
  39. #include "WindowParameters.hpp"
  40. #include "extra/Base64.hpp"
  41. #include "extra/SharedResourcePointer.hpp"
  42. #define REMOTE_HOST_PORT "2228"
  43. namespace rack {
  44. namespace plugin {
  45. void initStaticPlugins();
  46. void destroyStaticPlugins();
  47. }
  48. #if defined(__MOD_DEVICES__) && !defined(HEADLESS)
  49. namespace window {
  50. void WindowInit(Window* window, DISTRHO_NAMESPACE::Plugin* plugin);
  51. }
  52. #endif
  53. }
  54. START_NAMESPACE_DISTRHO
  55. // -----------------------------------------------------------------------------------------------------------
  56. struct Initializer
  57. #ifdef HAVE_LIBLO
  58. : public Thread
  59. #endif
  60. {
  61. #ifdef HAVE_LIBLO
  62. lo_server oscServer = nullptr;
  63. CardinalBasePlugin* oscPlugin = nullptr;
  64. #endif
  65. std::string templatePath;
  66. Initializer(const CardinalBasePlugin* const plugin)
  67. {
  68. using namespace rack;
  69. settings::allowCursorLock = false;
  70. settings::autoCheckUpdates = false;
  71. settings::autosaveInterval = 0;
  72. settings::devMode = true;
  73. settings::discordUpdateActivity = false;
  74. settings::isPlugin = true;
  75. settings::skipLoadOnLaunch = true;
  76. settings::showTipsOnLaunch = false;
  77. settings::windowPos = math::Vec(0, 0);
  78. #ifdef HEADLESS
  79. settings::headless = true;
  80. #endif
  81. system::init();
  82. logger::init();
  83. random::init();
  84. ui::init();
  85. if (asset::systemDir.empty())
  86. {
  87. if (const char* const bundlePath = plugin->getBundlePath())
  88. {
  89. if (const char* const resourcePath = getResourcePath(bundlePath))
  90. {
  91. asset::bundlePath = system::join(resourcePath, "PluginManifests");
  92. asset::systemDir = resourcePath;
  93. templatePath = system::join(asset::systemDir, "template.vcv");
  94. }
  95. }
  96. if (asset::systemDir.empty())
  97. {
  98. #ifdef CARDINAL_PLUGIN_SOURCE_DIR
  99. // Make system dir point to source code location as fallback
  100. asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack";
  101. if (system::exists(system::join(asset::systemDir, "res")))
  102. {
  103. templatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "template.vcv";
  104. }
  105. else
  106. // If source code dir does not exist use install target prefix as system dir
  107. #endif
  108. {
  109. asset::bundlePath = CARDINAL_PLUGIN_PREFIX "/share/Cardinal/PluginManifests";
  110. asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/Cardinal";
  111. templatePath = system::join(asset::systemDir, "template.vcv");
  112. }
  113. }
  114. asset::userDir = asset::systemDir;
  115. }
  116. // Log environment
  117. INFO("%s %s v%s", APP_NAME.c_str(), APP_EDITION.c_str(), APP_VERSION.c_str());
  118. INFO("%s", system::getOperatingSystemInfo().c_str());
  119. INFO("Binary filename: %s", getBinaryFilename());
  120. INFO("Bundle path: %s", plugin->getBundlePath());
  121. INFO("System directory: %s", asset::systemDir.c_str());
  122. INFO("User directory: %s", asset::userDir.c_str());
  123. INFO("Template patch: %s", templatePath.c_str());
  124. // Check existence of the system res/ directory
  125. if (! system::exists(asset::systemDir))
  126. {
  127. d_stderr2("System directory \"%s\" does not exist.\n"
  128. "Make sure Cardinal was downloaded and installed correctly.", asset::systemDir.c_str());
  129. }
  130. INFO("Initializing audio driver");
  131. rack::audio::addDriver(0, new CardinalAudioDriver);
  132. INFO("Initializing midi driver");
  133. rack::midi::addDriver(0, new CardinalMidiDriver);
  134. INFO("Initializing plugins");
  135. plugin::initStaticPlugins();
  136. #ifdef HAVE_LIBLO
  137. INFO("Initializing OSC Remote control");
  138. oscServer = lo_server_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler);
  139. DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,);
  140. lo_server_add_method(oscServer, "/hello", "", osc_hello_handler, this);
  141. lo_server_add_method(oscServer, "/load", "b", osc_load_handler, this);
  142. lo_server_add_method(oscServer, nullptr, nullptr, osc_fallback_handler, nullptr);
  143. startThread();
  144. #else
  145. INFO("OSC Remote control is not enabled in this build");
  146. #endif
  147. }
  148. ~Initializer()
  149. {
  150. using namespace rack;
  151. #ifdef HAVE_LIBLO
  152. if (oscServer != nullptr)
  153. {
  154. stopThread(5000);
  155. lo_server_del_method(oscServer, nullptr, nullptr);
  156. lo_server_free(oscServer);
  157. oscServer = nullptr;
  158. }
  159. #endif
  160. INFO("Clearing asset paths");
  161. asset::bundlePath.clear();
  162. asset::systemDir.clear();
  163. asset::userDir.clear();
  164. INFO("Destroying plugins");
  165. plugin::destroyStaticPlugins();
  166. INFO("Destroying MIDI devices");
  167. midi::destroy();
  168. INFO("Destroying audio devices");
  169. audio::destroy();
  170. INFO("Destroying logger");
  171. logger::destroy();
  172. }
  173. #ifdef HAVE_LIBLO
  174. void run() override
  175. {
  176. INFO("OSC Thread Listening for remote commands");
  177. while (! shouldThreadExit())
  178. {
  179. d_msleep(200);
  180. while (lo_server_recv_noblock(oscServer, 0) != 0) {}
  181. }
  182. INFO("OSC Thread Closed");
  183. }
  184. static void osc_error_handler(int num, const char* msg, const char* path)
  185. {
  186. d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path);
  187. }
  188. static int osc_fallback_handler(const char* const path, const char* const types, lo_arg**, int, lo_message, void*)
  189. {
  190. d_stderr("Cardinal OSC unhandled message \"%s\" with types \"%s\"", path, types);
  191. return 0;
  192. }
  193. static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self)
  194. {
  195. d_stdout("osc_hello_handler()");
  196. const lo_address source = lo_message_get_source(m);
  197. lo_send_from(source, static_cast<Initializer*>(self)->oscServer, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok");
  198. return 0;
  199. }
  200. static int osc_load_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self)
  201. {
  202. d_stdout("osc_load_handler()");
  203. DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
  204. DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);
  205. const int32_t size = argv[0]->blob.size;
  206. DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0);
  207. const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data);
  208. DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0);
  209. bool ok = false;
  210. if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin)
  211. {
  212. CardinalPluginContext* const context = plugin->context;
  213. std::vector<uint8_t> data(size);
  214. std::memcpy(data.data(), blob, size);
  215. rack::contextSet(context);
  216. rack::system::removeRecursively(context->patch->autosavePath);
  217. rack::system::createDirectories(context->patch->autosavePath);
  218. try {
  219. rack::system::unarchiveToDirectory(data, context->patch->autosavePath);
  220. context->patch->loadAutosave();
  221. ok = true;
  222. }
  223. catch (rack::Exception& e) {
  224. WARN("%s", e.what());
  225. }
  226. rack::contextSet(nullptr);
  227. }
  228. const lo_address source = lo_message_get_source(m);
  229. lo_send_from(source, static_cast<Initializer*>(self)->oscServer,
  230. LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail");
  231. return 0;
  232. }
  233. #endif
  234. };
  235. // -----------------------------------------------------------------------------------------------------------
  236. struct ScopedContext {
  237. ScopedContext(const CardinalBasePlugin* const plugin)
  238. {
  239. rack::contextSet(plugin->context);
  240. }
  241. ~ScopedContext()
  242. {
  243. rack::contextSet(nullptr);
  244. }
  245. };
  246. // -----------------------------------------------------------------------------------------------------------
  247. class CardinalPlugin : public CardinalBasePlugin
  248. {
  249. SharedResourcePointer<Initializer> fInitializer;
  250. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  251. float* fAudioBufferIn;
  252. #endif
  253. float* fAudioBufferOut;
  254. std::string fAutosavePath;
  255. String fWindowSize;
  256. // for base/context handling
  257. bool fIsActive;
  258. CardinalAudioDevice* fCurrentAudioDevice;
  259. CardinalMidiInputDevice* fCurrentMidiInput;
  260. CardinalMidiOutputDevice* fCurrentMidiOutput;
  261. uint64_t fPreviousFrame;
  262. Mutex fDeviceMutex;
  263. // real values, not VCV interpreted ones
  264. float fWindowParameters[kWindowParameterCount];
  265. public:
  266. CardinalPlugin()
  267. : CardinalBasePlugin(kModuleParameters + kWindowParameterCount, 0, 2),
  268. fInitializer(this),
  269. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  270. fAudioBufferIn(nullptr),
  271. #endif
  272. fAudioBufferOut(nullptr),
  273. fIsActive(false),
  274. fCurrentAudioDevice(nullptr),
  275. fCurrentMidiInput(nullptr),
  276. fCurrentMidiOutput(nullptr),
  277. fPreviousFrame(0)
  278. {
  279. fWindowParameters[kWindowParameterShowTooltips] = 1.0f;
  280. fWindowParameters[kWindowParameterCableOpacity] = 50.0f;
  281. fWindowParameters[kWindowParameterCableTension] = 50.0f;
  282. fWindowParameters[kWindowParameterRackBrightness] = 100.0f;
  283. fWindowParameters[kWindowParameterHaloBrightness] = 25.0f;
  284. fWindowParameters[kWindowParameterKnobMode] = 0.0f;
  285. fWindowParameters[kWindowParameterWheelKnobControl] = 0.0f;
  286. fWindowParameters[kWindowParameterWheelSensitivity] = 1.0f;
  287. fWindowParameters[kWindowParameterLockModulePositions] = 0.0f;
  288. // create unique temporary path for this instance
  289. try {
  290. char uidBuf[24];
  291. const std::string tmp = rack::system::getTempDirectory();
  292. for (int i=1;; ++i)
  293. {
  294. std::snprintf(uidBuf, sizeof(uidBuf), "Cardinal.%04d", i);
  295. const std::string trypath = rack::system::join(tmp, uidBuf);
  296. if (! rack::system::exists(trypath))
  297. {
  298. if (rack::system::createDirectories(trypath))
  299. fAutosavePath = trypath;
  300. break;
  301. }
  302. }
  303. } DISTRHO_SAFE_EXCEPTION("create unique temporary path");
  304. const float sampleRate = getSampleRate();
  305. rack::settings::sampleRate = sampleRate;
  306. context->bufferSize = getBufferSize();
  307. context->sampleRate = sampleRate;
  308. const ScopedContext sc(this);
  309. context->engine = new rack::engine::Engine;
  310. context->engine->setSampleRate(sampleRate);
  311. context->history = new rack::history::State;
  312. context->patch = new rack::patch::Manager;
  313. context->patch->autosavePath = fAutosavePath;
  314. context->patch->templatePath = fInitializer->templatePath;
  315. context->event = new rack::widget::EventState;
  316. context->scene = new rack::app::Scene;
  317. context->event->rootWidget = context->scene;
  318. if (! isDummyInstance())
  319. context->window = new rack::window::Window;
  320. context->patch->loadTemplate();
  321. context->scene->rackScroll->reset();
  322. #ifdef HAVE_LIBLO
  323. fInitializer->oscPlugin = this;
  324. #endif
  325. }
  326. ~CardinalPlugin() override
  327. {
  328. #ifdef HAVE_LIBLO
  329. fInitializer->oscPlugin = nullptr;
  330. #endif
  331. {
  332. const MutexLocker cml(fDeviceMutex);
  333. fCurrentAudioDevice = nullptr;
  334. fCurrentMidiInput = nullptr;
  335. fCurrentMidiOutput = nullptr;
  336. }
  337. {
  338. const ScopedContext sc(this);
  339. context->patch->clear();
  340. delete context;
  341. }
  342. if (! fAutosavePath.empty())
  343. rack::system::removeRecursively(fAutosavePath);
  344. }
  345. CardinalPluginContext* getRackContext() const noexcept
  346. {
  347. return context;
  348. }
  349. protected:
  350. /* --------------------------------------------------------------------------------------------------------
  351. * Cardinal Base things */
  352. bool isActive() const noexcept override
  353. {
  354. return fIsActive;
  355. }
  356. bool canAssignAudioDevice() const noexcept override
  357. {
  358. const MutexLocker cml(fDeviceMutex);
  359. return fCurrentAudioDevice == nullptr;
  360. }
  361. bool canAssignMidiInputDevice() const noexcept override
  362. {
  363. const MutexLocker cml(fDeviceMutex);
  364. return fCurrentMidiInput == nullptr;
  365. }
  366. bool canAssignMidiOutputDevice() const noexcept override
  367. {
  368. const MutexLocker cml(fDeviceMutex);
  369. return fCurrentMidiOutput == nullptr;
  370. }
  371. void assignAudioDevice(CardinalAudioDevice* const dev) noexcept override
  372. {
  373. DISTRHO_SAFE_ASSERT_RETURN(fCurrentAudioDevice == nullptr,);
  374. const MutexLocker cml(fDeviceMutex);
  375. fCurrentAudioDevice = dev;
  376. }
  377. void assignMidiInputDevice(CardinalMidiInputDevice* const dev) noexcept override
  378. {
  379. DISTRHO_SAFE_ASSERT_RETURN(fCurrentMidiInput == nullptr,);
  380. const MutexLocker cml(fDeviceMutex);
  381. fCurrentMidiInput = dev;
  382. }
  383. void assignMidiOutputDevice(CardinalMidiOutputDevice* const dev) noexcept override
  384. {
  385. DISTRHO_SAFE_ASSERT_RETURN(fCurrentMidiOutput == nullptr,);
  386. const MutexLocker cml(fDeviceMutex);
  387. fCurrentMidiOutput = dev;
  388. }
  389. bool clearAudioDevice(CardinalAudioDevice* const dev) noexcept override
  390. {
  391. const MutexLocker cml(fDeviceMutex);
  392. if (fCurrentAudioDevice != dev)
  393. return false;
  394. fCurrentAudioDevice = nullptr;
  395. return true;
  396. }
  397. bool clearMidiInputDevice(CardinalMidiInputDevice* const dev) noexcept override
  398. {
  399. const MutexLocker cml(fDeviceMutex);
  400. if (fCurrentMidiInput != dev)
  401. return false;
  402. fCurrentMidiInput = nullptr;
  403. return true;
  404. }
  405. bool clearMidiOutputDevice(CardinalMidiOutputDevice* const dev) noexcept override
  406. {
  407. const MutexLocker cml(fDeviceMutex);
  408. if (fCurrentMidiOutput != dev)
  409. return false;
  410. fCurrentMidiOutput = nullptr;
  411. return true;
  412. }
  413. /* --------------------------------------------------------------------------------------------------------
  414. * Information */
  415. const char* getLabel() const override
  416. {
  417. #if DISTRHO_PLUGIN_IS_SYNTH
  418. return "CardinalSynth";
  419. #elif DISTRHO_PLUGIN_NUM_INPUTS == 2
  420. return "CardinalFX";
  421. #else
  422. return "Cardinal";
  423. #endif
  424. }
  425. const char* getDescription() const override
  426. {
  427. return ""
  428. "Cardinal is an open-source self-contained special plugin version of VCVRack, using DPF.\n"
  429. "It is NOT an official VCV project, and it is not affiliated with it in any way.\n";
  430. }
  431. const char* getMaker() const override
  432. {
  433. return "DISTRHO";
  434. }
  435. const char* getHomePage() const override
  436. {
  437. return "https://github.com/DISTRHO/Cardinal";
  438. }
  439. const char* getLicense() const override
  440. {
  441. return "GPLv3+";
  442. }
  443. uint32_t getVersion() const override
  444. {
  445. return d_version(2, 0, 0);
  446. }
  447. int64_t getUniqueId() const override
  448. {
  449. #if DISTRHO_PLUGIN_IS_SYNTH
  450. return d_cconst('d', 'C', 'n', 'S');
  451. #elif DISTRHO_PLUGIN_NUM_INPUTS == 2
  452. return d_cconst('d', 'C', 'n', 'F');
  453. #else
  454. return d_cconst('d', 'C', 'd', 'n');
  455. #endif
  456. }
  457. /* --------------------------------------------------------------------------------------------------------
  458. * Init */
  459. void initAudioPort(const bool input, uint32_t index, AudioPort& port) override
  460. {
  461. if (index >= 2)
  462. {
  463. port.hints = kAudioPortIsCV | kCVPortHasPositiveUnipolarRange | kCVPortHasScaledRange;
  464. index -= 2;
  465. }
  466. CardinalBasePlugin::initAudioPort(input, index, port);
  467. }
  468. void initParameter(const uint32_t index, Parameter& parameter) override
  469. {
  470. if (index < kModuleParameters)
  471. {
  472. parameter.name = "Parameter ";
  473. parameter.name += String(index + 1);
  474. parameter.symbol = "param_";
  475. parameter.symbol += String(index + 1);
  476. parameter.unit = "v";
  477. parameter.hints = kParameterIsAutomable;
  478. parameter.ranges.def = 0.0f;
  479. parameter.ranges.min = 0.0f;
  480. parameter.ranges.max = 10.0f;
  481. return;
  482. }
  483. switch (index - kModuleParameters)
  484. {
  485. case kWindowParameterShowTooltips:
  486. parameter.name = "Show tooltips";
  487. parameter.symbol = "tooltips";
  488. parameter.hints = kParameterIsAutomable|kParameterIsInteger|kParameterIsBoolean;
  489. parameter.ranges.def = 1.0f;
  490. parameter.ranges.min = 0.0f;
  491. parameter.ranges.max = 1.0f;
  492. break;
  493. case kWindowParameterCableOpacity:
  494. parameter.name = "Cable opacity";
  495. parameter.symbol = "cableOpacity";
  496. parameter.unit = "%";
  497. parameter.hints = kParameterIsAutomable;
  498. parameter.ranges.def = 50.0f;
  499. parameter.ranges.min = 0.0f;
  500. parameter.ranges.max = 100.0f;
  501. break;
  502. case kWindowParameterCableTension:
  503. parameter.name = "Cable tension";
  504. parameter.symbol = "cableTension";
  505. parameter.unit = "%";
  506. parameter.hints = kParameterIsAutomable;
  507. parameter.ranges.def = 50.0f;
  508. parameter.ranges.min = 0.0f;
  509. parameter.ranges.max = 100.0f;
  510. break;
  511. case kWindowParameterRackBrightness:
  512. parameter.name = "Room brightness";
  513. parameter.symbol = "rackBrightness";
  514. parameter.unit = "%";
  515. parameter.hints = kParameterIsAutomable;
  516. parameter.ranges.def = 100.0f;
  517. parameter.ranges.min = 0.0f;
  518. parameter.ranges.max = 100.0f;
  519. break;
  520. case kWindowParameterHaloBrightness:
  521. parameter.name = "Light Bloom";
  522. parameter.symbol = "haloBrightness";
  523. parameter.unit = "%";
  524. parameter.hints = kParameterIsAutomable;
  525. parameter.ranges.def = 25.0f;
  526. parameter.ranges.min = 0.0f;
  527. parameter.ranges.max = 100.0f;
  528. break;
  529. case kWindowParameterKnobMode:
  530. parameter.name = "Knob mode";
  531. parameter.symbol = "knobMode";
  532. parameter.hints = kParameterIsAutomable|kParameterIsInteger;
  533. parameter.ranges.def = 0.0f;
  534. parameter.ranges.min = 0.0f;
  535. parameter.ranges.max = 2.0f;
  536. parameter.enumValues.count = 3;
  537. parameter.enumValues.restrictedMode = true;
  538. parameter.enumValues.values = new ParameterEnumerationValue[3];
  539. parameter.enumValues.values[0].label = "Linear";
  540. parameter.enumValues.values[0].value = 0.0f;
  541. parameter.enumValues.values[1].label = "Absolute rotary";
  542. parameter.enumValues.values[1].value = 1.0f;
  543. parameter.enumValues.values[2].label = "Relative rotary";
  544. parameter.enumValues.values[2].value = 2.0f;
  545. break;
  546. case kWindowParameterWheelKnobControl:
  547. parameter.name = "Scroll wheel knob control";
  548. parameter.symbol = "knobScroll";
  549. parameter.hints = kParameterIsAutomable|kParameterIsInteger|kParameterIsBoolean;
  550. parameter.ranges.def = 0.0f;
  551. parameter.ranges.min = 0.0f;
  552. parameter.ranges.max = 1.0f;
  553. break;
  554. case kWindowParameterWheelSensitivity:
  555. parameter.name = "Scroll wheel knob sensitivity";
  556. parameter.symbol = "knobScrollSensitivity";
  557. parameter.hints = kParameterIsAutomable|kParameterIsLogarithmic;
  558. parameter.ranges.def = 1.0f;
  559. parameter.ranges.min = 0.1f;
  560. parameter.ranges.max = 10.0f;
  561. break;
  562. case kWindowParameterLockModulePositions:
  563. parameter.name = "Lock module positions";
  564. parameter.symbol = "lockModules";
  565. parameter.hints = kParameterIsAutomable|kParameterIsInteger|kParameterIsBoolean;
  566. parameter.ranges.def = 0.0f;
  567. parameter.ranges.min = 0.0f;
  568. parameter.ranges.max = 1.0f;
  569. break;
  570. }
  571. }
  572. void initState(const uint32_t index, String& stateKey, String& defaultStateValue) override
  573. {
  574. defaultStateValue = "";
  575. switch (index)
  576. {
  577. case 0:
  578. stateKey = "patch";
  579. break;
  580. case 1:
  581. stateKey = "windowSize";
  582. break;
  583. }
  584. }
  585. /* --------------------------------------------------------------------------------------------------------
  586. * Internal data */
  587. float getParameterValue(uint32_t index) const override
  588. {
  589. if (index < kModuleParameters)
  590. return context->parameters[index];
  591. index -= kModuleParameters;
  592. if (index < kWindowParameterCount)
  593. return fWindowParameters[index];
  594. return 0.0f;
  595. }
  596. void setParameterValue(uint32_t index, float value) override
  597. {
  598. if (index < kModuleParameters)
  599. {
  600. context->parameters[index] = value;
  601. return;
  602. }
  603. index -= kModuleParameters;
  604. if (index < kWindowParameterCount)
  605. {
  606. fWindowParameters[index] = value;
  607. return;
  608. }
  609. }
  610. String getState(const char* const key) const override
  611. {
  612. if (std::strcmp(key, "windowSize") == 0)
  613. return fWindowSize;
  614. if (std::strcmp(key, "patch") != 0)
  615. return String();
  616. if (fAutosavePath.empty())
  617. return String();
  618. std::vector<uint8_t> data;
  619. {
  620. const ScopedContext sc(this);
  621. context->engine->prepareSave();
  622. context->patch->saveAutosave();
  623. context->patch->cleanAutosave();
  624. // context->history->setSaved();
  625. try {
  626. data = rack::system::archiveDirectory(fAutosavePath, 1);
  627. } DISTRHO_SAFE_EXCEPTION_RETURN("getState archiveDirectory", String());
  628. }
  629. return String::asBase64(data.data(), data.size());
  630. }
  631. void setState(const char* const key, const char* const value) override
  632. {
  633. if (std::strcmp(key, "windowSize") == 0)
  634. {
  635. fWindowSize = value;
  636. return;
  637. }
  638. if (std::strcmp(key, "patch") != 0)
  639. return;
  640. if (fAutosavePath.empty())
  641. return;
  642. const std::vector<uint8_t> data(d_getChunkFromBase64String(value));
  643. const ScopedContext sc(this);
  644. rack::system::removeRecursively(fAutosavePath);
  645. rack::system::createDirectories(fAutosavePath);
  646. rack::system::unarchiveToDirectory(data, fAutosavePath);
  647. try {
  648. context->patch->loadAutosave();
  649. } DISTRHO_SAFE_EXCEPTION_RETURN("setState loadAutosave",);
  650. // context->history->setSaved();
  651. }
  652. /* --------------------------------------------------------------------------------------------------------
  653. * Process */
  654. void activate() override
  655. {
  656. const uint32_t bufferSize = getBufferSize();
  657. fAudioBufferOut = new float[bufferSize * DISTRHO_PLUGIN_NUM_OUTPUTS];
  658. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  659. fAudioBufferIn = new float[bufferSize * DISTRHO_PLUGIN_NUM_INPUTS];
  660. std::memset(fAudioBufferIn, 0, sizeof(float)*bufferSize * DISTRHO_PLUGIN_NUM_INPUTS);
  661. #endif
  662. fPreviousFrame = 0;
  663. {
  664. const MutexLocker cml(fDeviceMutex);
  665. if (fCurrentAudioDevice != nullptr)
  666. {
  667. rack::contextSet(context);
  668. fCurrentAudioDevice->onStartStream();
  669. }
  670. }
  671. }
  672. void deactivate() override
  673. {
  674. {
  675. const MutexLocker cml(fDeviceMutex);
  676. if (fCurrentAudioDevice != nullptr)
  677. {
  678. rack::contextSet(context);
  679. fCurrentAudioDevice->onStopStream();
  680. }
  681. }
  682. delete[] fAudioBufferOut;
  683. fAudioBufferOut = nullptr;
  684. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  685. delete[] fAudioBufferIn;
  686. fAudioBufferIn = nullptr;
  687. #endif
  688. }
  689. void run(const float** const inputs, float** const outputs, const uint32_t frames,
  690. const MidiEvent* const midiEvents, const uint32_t midiEventCount) override
  691. {
  692. const MutexLocker cml(fDeviceMutex);
  693. rack::contextSet(context);
  694. {
  695. const TimePosition& timePos(getTimePosition());
  696. context->playing = timePos.playing;
  697. context->bbtValid = timePos.bbt.valid;
  698. context->frame = timePos.frame;
  699. if (timePos.bbt.valid)
  700. {
  701. const double samplesPerTick = 60.0 * getSampleRate()
  702. / timePos.bbt.beatsPerMinute
  703. / timePos.bbt.ticksPerBeat;
  704. context->bar = timePos.bbt.bar;
  705. context->beat = timePos.bbt.beat;
  706. context->beatsPerBar = timePos.bbt.beatsPerBar;
  707. context->beatType = timePos.bbt.beatType;
  708. context->barStartTick = timePos.bbt.barStartTick;
  709. context->beatsPerMinute = timePos.bbt.beatsPerMinute;
  710. context->tick = timePos.bbt.tick;
  711. context->ticksPerBeat = timePos.bbt.ticksPerBeat;
  712. context->ticksPerClock = timePos.bbt.ticksPerBeat / timePos.bbt.beatType;
  713. context->ticksPerFrame = 1.0 / samplesPerTick;
  714. context->tickClock = std::fmod(timePos.bbt.tick, context->ticksPerClock);
  715. }
  716. if (timePos.playing && fPreviousFrame + frames != timePos.frame)
  717. context->reset = true;
  718. fPreviousFrame = timePos.frame;
  719. }
  720. if (fCurrentMidiInput != nullptr)
  721. fCurrentMidiInput->handleMessagesFromHost(midiEvents, midiEventCount);
  722. if (fCurrentAudioDevice != nullptr)
  723. {
  724. #if DISTRHO_PLUGIN_NUM_INPUTS != 0
  725. for (uint32_t i=0, j=0; i<frames; ++i)
  726. {
  727. fAudioBufferIn[j++] = inputs[0][i];
  728. fAudioBufferIn[j++] = inputs[1][i];
  729. }
  730. fCurrentAudioDevice->processInput(fAudioBufferIn, 2, frames);
  731. #endif
  732. }
  733. #if DISTRHO_PLUGIN_NUM_OUTPUTS != 2
  734. context->dataFrame = 0;
  735. context->dataIns = inputs;
  736. context->dataOuts = outputs;
  737. #endif
  738. context->engine->stepBlock(frames);
  739. if (fCurrentAudioDevice != nullptr)
  740. {
  741. std::memset(fAudioBufferOut, 0, sizeof(float)*frames*2);
  742. fCurrentAudioDevice->processOutput(fAudioBufferOut, 2, frames);
  743. for (uint32_t i=0, j=0; i<frames; ++i)
  744. {
  745. outputs[0][i] = fAudioBufferOut[j++];
  746. outputs[1][i] = fAudioBufferOut[j++];
  747. }
  748. }
  749. else
  750. {
  751. std::memset(outputs[0], 0, sizeof(float)*frames);
  752. std::memset(outputs[1], 0, sizeof(float)*frames);
  753. }
  754. if (fCurrentMidiOutput != nullptr)
  755. fCurrentMidiOutput->processMessages();
  756. }
  757. void bufferSizeChanged(const uint32_t newBufferSize) override
  758. {
  759. rack::contextSet(context);
  760. context->bufferSize = newBufferSize;
  761. }
  762. void sampleRateChanged(const double newSampleRate) override
  763. {
  764. rack::contextSet(context);
  765. rack::settings::sampleRate = newSampleRate;
  766. context->sampleRate = newSampleRate;
  767. context->engine->setSampleRate(newSampleRate);
  768. }
  769. // -------------------------------------------------------------------------------------------------------
  770. private:
  771. /**
  772. Set our plugin class as non-copyable and add a leak detector just in case.
  773. */
  774. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalPlugin)
  775. };
  776. CardinalPluginContext* getRackContextFromPlugin(void* const ptr)
  777. {
  778. return static_cast<CardinalPlugin*>(ptr)->getRackContext();
  779. }
  780. /* ------------------------------------------------------------------------------------------------------------
  781. * Plugin entry point, called by DPF to create a new plugin instance. */
  782. Plugin* createPlugin()
  783. {
  784. return new CardinalPlugin();
  785. }
  786. // -----------------------------------------------------------------------------------------------------------
  787. END_NAMESPACE_DISTRHO