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.

453 lines
13KB

  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. #include "Widgets.hpp"
  20. // -----------------------------------------------------------------------------------------------------------
  21. USE_NAMESPACE_DISTRHO;
  22. template<int numIO>
  23. struct HostAudio : TerminalModule {
  24. CardinalPluginContext* const pcontext;
  25. const int numParams;
  26. const int numInputs;
  27. const int numOutputs;
  28. bool bypassed = false;
  29. bool in1connected = false;
  30. bool in2connected = false;
  31. uint32_t dataFrame = 0;
  32. uint32_t lastProcessCounter = 0;
  33. // for rack core audio module compatibility
  34. dsp::RCFilter dcFilters[numIO];
  35. bool dcFilterEnabled = (numIO == 2);
  36. HostAudio()
  37. : pcontext(static_cast<CardinalPluginContext*>(APP)),
  38. numParams(numIO == 2 ? 1 : 0),
  39. numInputs(pcontext->variant == kCardinalVariantMain ? numIO : 2),
  40. numOutputs(pcontext->variant == kCardinalVariantSynth ? 0 : pcontext->variant == kCardinalVariantMain ? numIO : 2)
  41. {
  42. if (pcontext == nullptr)
  43. throw rack::Exception("Plugin context is null");
  44. config(numParams, numIO, numIO, 0);
  45. if (numParams != 0)
  46. configParam(0, 0.f, 2.f, 1.f, "Level", " dB", -10, 40);
  47. const float sampleTime = pcontext->engine->getSampleTime();
  48. for (int i=0; i<numIO; ++i)
  49. dcFilters[i].setCutoffFreq(10.f * sampleTime);
  50. }
  51. void onReset() override
  52. {
  53. dcFilterEnabled = (numIO == 2);
  54. }
  55. void onSampleRateChange(const SampleRateChangeEvent& e) override
  56. {
  57. for (int i=0; i<numIO; ++i)
  58. dcFilters[i].setCutoffFreq(10.f * e.sampleTime);
  59. }
  60. void processTerminalInput(const ProcessArgs&) override
  61. {
  62. const uint32_t bufferSize = pcontext->bufferSize;
  63. const uint32_t processCounter = pcontext->processCounter;
  64. // only checked on input
  65. if (lastProcessCounter != processCounter)
  66. {
  67. bypassed = isBypassed();
  68. dataFrame = 0;
  69. lastProcessCounter = processCounter;
  70. if (numIO == 2)
  71. {
  72. in1connected = inputs[0].isConnected();
  73. in2connected = inputs[1].isConnected();
  74. }
  75. }
  76. // only incremented on output
  77. const uint32_t k = dataFrame;
  78. DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);
  79. // from host into cardinal, shows as output plug
  80. if (bypassed)
  81. {
  82. for (int i=0; i<numOutputs; ++i)
  83. outputs[i].setVoltage(0.0f);
  84. }
  85. else if (const float* const* const dataIns = pcontext->dataIns)
  86. {
  87. for (int i=0; i<numOutputs; ++i)
  88. outputs[i].setVoltage(dataIns[i][k] * 10.0f);
  89. }
  90. }
  91. json_t* dataToJson() override
  92. {
  93. json_t* const rootJ = json_object();
  94. DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr);
  95. json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled));
  96. return rootJ;
  97. }
  98. void dataFromJson(json_t* const rootJ) override
  99. {
  100. json_t* const dcFilterJ = json_object_get(rootJ, "dcFilter");
  101. DISTRHO_SAFE_ASSERT_RETURN(dcFilterJ != nullptr,);
  102. dcFilterEnabled = json_boolean_value(dcFilterJ);
  103. }
  104. };
  105. struct HostAudio2 : HostAudio<2> {
  106. #ifndef HEADLESS
  107. // for stereo meter
  108. uint32_t internalDataFrame = 0;
  109. float internalDataBufferL[128] = {};
  110. float internalDataBufferR[128] = {};
  111. volatile bool resetMeters = true;
  112. float gainMeterL = 0.0f;
  113. float gainMeterR = 0.0f;
  114. #endif
  115. #ifndef HEADLESS
  116. void onReset() override
  117. {
  118. HostAudio<2>::onReset();
  119. resetMeters = true;
  120. }
  121. void onSampleRateChange(const SampleRateChangeEvent& e) override
  122. {
  123. HostAudio<2>::onSampleRateChange(e);
  124. resetMeters = true;
  125. }
  126. #endif
  127. void processTerminalOutput(const ProcessArgs&) override
  128. {
  129. if (pcontext->bypassed || (!in1connected && !in2connected))
  130. {
  131. #ifndef HEADLESS
  132. if (resetMeters)
  133. {
  134. internalDataFrame = 0;
  135. gainMeterL = gainMeterR = 0.0f;
  136. resetMeters = false;
  137. }
  138. #endif
  139. return;
  140. }
  141. const uint32_t bufferSize = pcontext->bufferSize;
  142. // only incremented on output
  143. const uint32_t k = dataFrame++;
  144. DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);
  145. if (bypassed)
  146. return;
  147. float** const dataOuts = pcontext->dataOuts;
  148. // gain (stereo variant only)
  149. const float gain = std::pow(params[0].getValue(), 2.f);
  150. // read stereo values
  151. float valueL, valueR;
  152. if (in1connected)
  153. {
  154. if (!std::isfinite(dataOuts[0][k]))
  155. __builtin_unreachable();
  156. valueL = inputs[0].getVoltageSum() * 0.1f;
  157. if (dcFilterEnabled)
  158. {
  159. dcFilters[0].process(valueL);
  160. valueL = dcFilters[0].highpass();
  161. }
  162. valueL = clamp(valueL * gain, -1.0f, 1.0f);
  163. dataOuts[0][k] += valueL;
  164. }
  165. else
  166. {
  167. valueL = 0.0f;
  168. }
  169. if (in2connected)
  170. {
  171. if (!std::isfinite(dataOuts[1][k]))
  172. __builtin_unreachable();
  173. valueR = inputs[1].getVoltageSum() * 0.1f;
  174. if (dcFilterEnabled)
  175. {
  176. dcFilters[1].process(valueR);
  177. valueR = dcFilters[1].highpass();
  178. }
  179. valueR = clamp(valueR * gain, -1.0f, 1.0f);
  180. dataOuts[1][k] += valueR;
  181. }
  182. else if (in1connected)
  183. {
  184. valueR = valueL;
  185. dataOuts[1][k] += valueL;
  186. }
  187. #ifndef HEADLESS
  188. else
  189. {
  190. valueR = 0.0f;
  191. }
  192. if (pcontext->variant == kCardinalVariantMini)
  193. {
  194. const uint32_t j = internalDataFrame++;
  195. internalDataBufferL[j] = valueL;
  196. internalDataBufferR[j] = valueR;
  197. if (internalDataFrame == 4)
  198. {
  199. internalDataFrame = 0;
  200. if (resetMeters)
  201. gainMeterL = gainMeterR = 0.0f;
  202. gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat<4>(internalDataBufferL));
  203. if (in2connected)
  204. gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat<4>(internalDataBufferR));
  205. else
  206. gainMeterR = gainMeterL;
  207. resetMeters = false;
  208. }
  209. }
  210. else
  211. {
  212. const uint32_t j = internalDataFrame++;
  213. internalDataBufferL[j] = valueL;
  214. internalDataBufferR[j] = valueR;
  215. if (internalDataFrame == 128)
  216. {
  217. internalDataFrame = 0;
  218. if (resetMeters)
  219. gainMeterL = gainMeterR = 0.0f;
  220. gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat128(internalDataBufferL));
  221. if (in2connected)
  222. gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat128(internalDataBufferR));
  223. else
  224. gainMeterR = gainMeterL;
  225. resetMeters = false;
  226. }
  227. }
  228. #endif
  229. }
  230. };
  231. struct HostAudio8 : HostAudio<8> {
  232. // no meters in this variant
  233. void processTerminalOutput(const ProcessArgs&) override
  234. {
  235. if (pcontext->bypassed)
  236. return;
  237. const uint32_t bufferSize = pcontext->bufferSize;
  238. // only incremented on output
  239. const uint32_t k = dataFrame++;
  240. DISTRHO_SAFE_ASSERT_INT2_RETURN(k < bufferSize, k, bufferSize,);
  241. if (bypassed)
  242. return;
  243. float** const dataOuts = pcontext->dataOuts;
  244. for (int i=0; i<numInputs; ++i)
  245. {
  246. float v = inputs[i].getVoltageSum() * 0.1f;
  247. if (dcFilterEnabled)
  248. {
  249. dcFilters[i].process(v);
  250. v = dcFilters[i].highpass();
  251. }
  252. dataOuts[i][k] += clamp(v, -1.0f, 1.0f);
  253. }
  254. }
  255. };
  256. #ifndef HEADLESS
  257. // --------------------------------------------------------------------------------------------------------------------
  258. template<int numIO>
  259. struct HostAudioWidget : ModuleWidgetWith8HP {
  260. HostAudio<numIO>* const module;
  261. CardinalPluginContext* const pcontext;
  262. HostAudioWidget(HostAudio<numIO>* const m)
  263. : module(m),
  264. pcontext(static_cast<CardinalPluginContext*>(APP))
  265. {
  266. setModule(m);
  267. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg")));
  268. createAndAddScrews();
  269. const uint8_t ioCount = pcontext->variant == kCardinalVariantMain ? 8 : 2;
  270. for (uint i=0; i<numIO; ++i)
  271. {
  272. createAndAddInput(i, i, i<ioCount);
  273. createAndAddOutput(i, i, i<ioCount);
  274. }
  275. }
  276. void appendContextMenu(Menu* const menu) override {
  277. menu->addChild(new MenuSeparator);
  278. menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled));
  279. }
  280. };
  281. // --------------------------------------------------------------------------------------------------------------------
  282. struct HostAudioNanoMeter : NanoMeter {
  283. HostAudio2* const module;
  284. HostAudioNanoMeter(HostAudio2* const m)
  285. : module(m)
  286. {
  287. hasGainKnob = true;
  288. }
  289. void updateMeters() override
  290. {
  291. if (module == nullptr || module->resetMeters)
  292. return;
  293. // Only fetch new values once DSP side is updated
  294. gainMeterL = module->gainMeterL;
  295. gainMeterR = module->gainMeterR;
  296. module->resetMeters = true;
  297. }
  298. };
  299. // --------------------------------------------------------------------------------------------------------------------
  300. struct HostAudioWidget2 : HostAudioWidget<2> {
  301. typedef NanoKnob<> Knob;
  302. HostAudioWidget2(HostAudio2* const m)
  303. : HostAudioWidget<2>(m)
  304. {
  305. // FIXME
  306. const float middleX = box.size.x * 0.5f;
  307. addParam(createParamCentered<Knob>(Vec(middleX, 310.0f), m, 0));
  308. HostAudioNanoMeter* const meter = new HostAudioNanoMeter(m);
  309. meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2);
  310. meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f);
  311. addChild(meter);
  312. }
  313. void draw(const DrawArgs& args) override
  314. {
  315. drawBackground(args.vg);
  316. drawOutputJacksArea(args.vg, 2);
  317. setupTextLines(args.vg);
  318. drawTextLine(args.vg, 0, "Left/M");
  319. drawTextLine(args.vg, 1, "Right");
  320. ModuleWidgetWith8HP::draw(args);
  321. }
  322. };
  323. struct HostAudioWidget8 : HostAudioWidget<8> {
  324. HostAudioWidget8(HostAudio8* const m)
  325. : HostAudioWidget<8>(m) {}
  326. void draw(const DrawArgs& args) override
  327. {
  328. const uint8_t ioCount = pcontext->variant == kCardinalVariantMain ? 8 : 2;
  329. drawBackground(args.vg);
  330. drawOutputJacksArea(args.vg, ioCount);
  331. setupTextLines(args.vg);
  332. for (int i=0; i<ioCount; ++i)
  333. {
  334. char text[] = {'A','u','d','i','o',' ',static_cast<char>('0'+i+1),'\0'};
  335. drawTextLine(args.vg, i, text);
  336. }
  337. ModuleWidgetWith8HP::draw(args);
  338. }
  339. };
  340. #else
  341. // --------------------------------------------------------------------------------------------------------------------
  342. struct HostAudioWidget2 : ModuleWidget {
  343. HostAudioWidget2(HostAudio2* const module) {
  344. setModule(module);
  345. for (uint i=0; i<2; ++i) {
  346. addInput(createInput<PJ301MPort>({}, module, i));
  347. addOutput(createOutput<PJ301MPort>({}, module, i));
  348. }
  349. }
  350. };
  351. struct HostAudioWidget8 : ModuleWidget {
  352. HostAudioWidget8(HostAudio8* const module) {
  353. setModule(module);
  354. for (uint i=0; i<8; ++i) {
  355. addInput(createInput<PJ301MPort>({}, module, i));
  356. addOutput(createOutput<PJ301MPort>({}, module, i));
  357. }
  358. }
  359. };
  360. // --------------------------------------------------------------------------------------------------------------------
  361. #endif
  362. Model* modelHostAudio2 = createModel<HostAudio2, HostAudioWidget2>("HostAudio2");
  363. Model* modelHostAudio8 = createModel<HostAudio8, HostAudioWidget8>("HostAudio8");
  364. // --------------------------------------------------------------------------------------------------------------------