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.

292 lines
10KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2022 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 "plugincontext.hpp"
  18. #include "ModuleWidgets.hpp"
  19. // -----------------------------------------------------------------------------------------------------------
  20. USE_NAMESPACE_DISTRHO;
  21. struct HostCV : TerminalModule {
  22. CardinalPluginContext* const pcontext;
  23. bool bypassed = false;
  24. int dataFrame = 0;
  25. uint32_t lastProcessCounter = 0;
  26. enum ParamIds {
  27. BIPOLAR_INPUTS_1_5,
  28. BIPOLAR_INPUTS_6_10,
  29. BIPOLAR_OUTPUTS_1_5,
  30. BIPOLAR_OUTPUTS_6_10,
  31. NUM_PARAMS
  32. };
  33. enum InputIds {
  34. NUM_INPUTS = 10
  35. };
  36. enum OutputIds {
  37. NUM_OUTPUTS = 10
  38. };
  39. enum LightIds {
  40. NUM_LIGHTS
  41. };
  42. HostCV()
  43. : pcontext(static_cast<CardinalPluginContext*>(APP))
  44. {
  45. if (pcontext == nullptr)
  46. throw rack::Exception("Plugin context is null");
  47. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  48. if (pcontext->variant == kCardinalVariantMini)
  49. {
  50. configParam<SwitchQuantity>(BIPOLAR_INPUTS_1_5, 0.f, 1.f, 0.f, "Bipolar Inputs")->randomizeEnabled = false;
  51. configParam<SwitchQuantity>(BIPOLAR_OUTPUTS_1_5, 0.f, 1.f, 0.f, "Bipolar Outputs")->randomizeEnabled = false;
  52. }
  53. else
  54. {
  55. configParam<SwitchQuantity>(BIPOLAR_INPUTS_1_5, 0.f, 1.f, 0.f, "Bipolar Inputs 1-5")->randomizeEnabled = false;
  56. configParam<SwitchQuantity>(BIPOLAR_OUTPUTS_1_5, 0.f, 1.f, 0.f, "Bipolar Outputs 1-5")->randomizeEnabled = false;
  57. }
  58. configParam<SwitchQuantity>(BIPOLAR_INPUTS_6_10, 0.f, 1.f, 0.f, "Bipolar Inputs 6-10")->randomizeEnabled = false;
  59. configParam<SwitchQuantity>(BIPOLAR_OUTPUTS_6_10, 0.f, 1.f, 0.f, "Bipolar Outputs 6-10")->randomizeEnabled = false;
  60. }
  61. void processTerminalInput(const ProcessArgs&) override
  62. {
  63. if (pcontext->variant != kCardinalVariantMain && pcontext->variant != kCardinalVariantMini)
  64. return;
  65. const uint8_t ioOffset = pcontext->variant == kCardinalVariantMini ? 2 : 8;
  66. const uint32_t bufferSize = pcontext->bufferSize;
  67. const uint32_t processCounter = pcontext->processCounter;
  68. // only checked on input
  69. if (lastProcessCounter != processCounter)
  70. {
  71. bypassed = isBypassed();
  72. dataFrame = 0;
  73. lastProcessCounter = processCounter;
  74. }
  75. // only incremented on output
  76. const uint32_t k = dataFrame;
  77. DISTRHO_SAFE_ASSERT_RETURN(k < bufferSize,);
  78. if (bypassed)
  79. {
  80. for (int i=0; i<10; ++i)
  81. outputs[i].setVoltage(0.0f);
  82. }
  83. else if (const float* const* const dataIns = pcontext->dataIns)
  84. {
  85. if (dataIns[ioOffset] == nullptr)
  86. return;
  87. float outputOffset;
  88. outputOffset = params[BIPOLAR_OUTPUTS_1_5].getValue() > 0.1f ? 5.f : 0.f;
  89. for (int i=0; i<5; ++i)
  90. outputs[i].setVoltage(dataIns[i+ioOffset][k] - outputOffset);
  91. if (pcontext->variant == kCardinalVariantMain)
  92. {
  93. outputOffset = params[BIPOLAR_OUTPUTS_6_10].getValue() > 0.1f ? 5.f : 0.f;
  94. for (int i=5; i<10; ++i)
  95. outputs[i].setVoltage(dataIns[i+ioOffset][k] - outputOffset);
  96. }
  97. else
  98. {
  99. for (int i=5; i<10; ++i)
  100. outputs[i].setVoltage(0.f);
  101. }
  102. }
  103. }
  104. void processTerminalOutput(const ProcessArgs&) override
  105. {
  106. if (pcontext->variant != kCardinalVariantMain && pcontext->variant != kCardinalVariantMini)
  107. return;
  108. if (pcontext->bypassed)
  109. return;
  110. const uint8_t ioOffset = pcontext->variant == kCardinalVariantMini ? 2 : 8;
  111. const uint32_t bufferSize = pcontext->bufferSize;
  112. // only incremented on output
  113. const uint32_t k = dataFrame++;
  114. DISTRHO_SAFE_ASSERT_RETURN(k < bufferSize,);
  115. if (bypassed)
  116. return;
  117. float** const dataOuts = pcontext->dataOuts;
  118. if (dataOuts[ioOffset] == nullptr)
  119. return;
  120. float inputOffset;
  121. inputOffset = params[BIPOLAR_INPUTS_1_5].getValue() > 0.1f ? 5.0f : 0.0f;
  122. for (int i=0; i<5; ++i)
  123. {
  124. if (!std::isfinite(dataOuts[i+ioOffset][k]))
  125. __builtin_unreachable();
  126. dataOuts[i+ioOffset][k] += inputs[i].getVoltage() + inputOffset;
  127. }
  128. if (pcontext->variant == kCardinalVariantMain)
  129. {
  130. inputOffset = params[BIPOLAR_INPUTS_6_10].getValue() > 0.1f ? 5.0f : 0.0f;
  131. for (int i=5; i<10; ++i)
  132. {
  133. if (!std::isfinite(dataOuts[i+ioOffset][k]))
  134. __builtin_unreachable();
  135. dataOuts[i+ioOffset][k] += inputs[i].getVoltage() + inputOffset;
  136. }
  137. }
  138. }
  139. };
  140. #ifndef HEADLESS
  141. struct HostCVWidget : ModuleWidgetWith8HP {
  142. CardinalPluginContext* const pcontext;
  143. HostCVWidget(HostCV* const module)
  144. : pcontext(static_cast<CardinalPluginContext*>(APP))
  145. {
  146. setModule(module);
  147. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostCV.svg")));
  148. createAndAddScrews();
  149. uint8_t maxVisible;
  150. switch (pcontext->variant)
  151. {
  152. case kCardinalVariantMain:
  153. maxVisible = HostCV::NUM_INPUTS;
  154. break;
  155. case kCardinalVariantMini:
  156. maxVisible = 5;
  157. break;
  158. default:
  159. maxVisible = 0;
  160. break;
  161. }
  162. for (uint i=0; i<HostCV::NUM_INPUTS; ++i)
  163. createAndAddInput(i, i, i<maxVisible);
  164. for (uint i=0; i<HostCV::NUM_OUTPUTS; ++i)
  165. createAndAddOutput(i, i, i<maxVisible);
  166. }
  167. void draw(const DrawArgs& args) override
  168. {
  169. drawBackground(args.vg);
  170. if (pcontext->variant == kCardinalVariantMain || pcontext->variant == kCardinalVariantMini)
  171. {
  172. drawOutputJacksArea(args.vg, pcontext->variant == kCardinalVariantMini ? 5 : HostCV::NUM_INPUTS);
  173. setupTextLines(args.vg);
  174. switch (pcontext->variant)
  175. {
  176. case kCardinalVariantMain:
  177. drawTextLine(args.vg, 5, "CV 6");
  178. drawTextLine(args.vg, 6, "CV 7");
  179. drawTextLine(args.vg, 7, "CV 8");
  180. drawTextLine(args.vg, 8, "CV 9");
  181. drawTextLine(args.vg, 9, "CV 10");
  182. // fall through
  183. case kCardinalVariantMini:
  184. drawTextLine(args.vg, 0, "CV 1");
  185. drawTextLine(args.vg, 1, "CV 2");
  186. drawTextLine(args.vg, 2, "CV 3");
  187. drawTextLine(args.vg, 3, "CV 4");
  188. drawTextLine(args.vg, 4, "CV 5");
  189. break;
  190. default:
  191. break;
  192. }
  193. }
  194. ModuleWidgetWith8HP::draw(args);
  195. }
  196. void appendContextMenu(ui::Menu* const menu) override
  197. {
  198. menu->addChild(new ui::MenuSeparator);
  199. if (pcontext->variant == kCardinalVariantMini)
  200. {
  201. menu->addChild(createCheckMenuItem("Bipolar Inputs", "",
  202. [=]() {return module->params[HostCV::BIPOLAR_INPUTS_1_5].getValue() > 0.1f;},
  203. [=]() {module->params[HostCV::BIPOLAR_INPUTS_1_5].setValue(1.0f - module->params[HostCV::BIPOLAR_INPUTS_1_5].getValue());}
  204. ));
  205. menu->addChild(createCheckMenuItem("Bipolar Outputs", "",
  206. [=]() {return module->params[HostCV::BIPOLAR_OUTPUTS_1_5].getValue() > 0.1f;},
  207. [=]() {module->params[HostCV::BIPOLAR_OUTPUTS_1_5].setValue(1.0f - module->params[HostCV::BIPOLAR_OUTPUTS_1_5].getValue());}
  208. ));
  209. }
  210. else
  211. {
  212. menu->addChild(createCheckMenuItem("Bipolar Inputs 1-5", "",
  213. [=]() {return module->params[HostCV::BIPOLAR_INPUTS_1_5].getValue() > 0.1f;},
  214. [=]() {module->params[HostCV::BIPOLAR_INPUTS_1_5].setValue(1.0f - module->params[HostCV::BIPOLAR_INPUTS_1_5].getValue());}
  215. ));
  216. menu->addChild(createCheckMenuItem("Bipolar Inputs 6-10", "",
  217. [=]() {return module->params[HostCV::BIPOLAR_INPUTS_6_10].getValue() > 0.1f;},
  218. [=]() {module->params[HostCV::BIPOLAR_INPUTS_6_10].setValue(1.0f - module->params[HostCV::BIPOLAR_INPUTS_6_10].getValue());}
  219. ));
  220. menu->addChild(createCheckMenuItem("Bipolar Outputs 1-5", "",
  221. [=]() {return module->params[HostCV::BIPOLAR_OUTPUTS_1_5].getValue() > 0.1f;},
  222. [=]() {module->params[HostCV::BIPOLAR_OUTPUTS_1_5].setValue(1.0f - module->params[HostCV::BIPOLAR_OUTPUTS_1_5].getValue());}
  223. ));
  224. menu->addChild(createCheckMenuItem("Bipolar Outputs 6-10", "",
  225. [=]() {return module->params[HostCV::BIPOLAR_OUTPUTS_6_10].getValue() > 0.1f;},
  226. [=]() {module->params[HostCV::BIPOLAR_OUTPUTS_6_10].setValue(1.0f - module->params[HostCV::BIPOLAR_OUTPUTS_6_10].getValue());}
  227. ));
  228. }
  229. }
  230. };
  231. #else
  232. struct HostCVWidget : ModuleWidget {
  233. HostCVWidget(HostCV* const module) {
  234. setModule(module);
  235. for (uint i=0; i<HostCV::NUM_INPUTS; ++i)
  236. addInput(createInput<PJ301MPort>({}, module, i));
  237. for (uint i=0; i<HostCV::NUM_OUTPUTS; ++i)
  238. addOutput(createOutput<PJ301MPort>({}, module, i));
  239. }
  240. };
  241. #endif
  242. // --------------------------------------------------------------------------------------------------------------------
  243. Model* modelHostCV = createModel<HostCV, HostCVWidget>("HostCV");
  244. // --------------------------------------------------------------------------------------------------------------------