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.

417 lines
12KB

  1. #include "dsp/digital.hpp"
  2. #include "AH.hpp"
  3. #include "Core.hpp"
  4. #include "UI.hpp"
  5. #include <iostream>
  6. namespace rack_plugin_AmalgamatedHarmonics {
  7. struct Imperfect2 : AHModule {
  8. enum ParamIds {
  9. ENUMS(DELAY_PARAM,4),
  10. ENUMS(DELAYSPREAD_PARAM,4),
  11. ENUMS(LENGTH_PARAM,4),
  12. ENUMS(LENGTHSPREAD_PARAM,4),
  13. ENUMS(DIVISION_PARAM,4),
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. ENUMS(TRIG_INPUT,4),
  18. ENUMS(DELAY_INPUT,4),
  19. ENUMS(DELAYSPREAD_INPUT,4),
  20. ENUMS(LENGTH_INPUT,4),
  21. ENUMS(LENGTHSPREAD_INPUT,4),
  22. NUM_INPUTS
  23. };
  24. enum OutputIds {
  25. ENUMS(OUT_OUTPUT,4),
  26. NUM_OUTPUTS
  27. };
  28. enum LightIds {
  29. ENUMS(OUT_LIGHT,8),
  30. NUM_LIGHTS
  31. };
  32. Imperfect2() : AHModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  33. reset();
  34. }
  35. void step() override;
  36. void reset() override {
  37. for (int i = 0; i < 4; i++) {
  38. delayState[i] = false;
  39. gateState[i] = false;
  40. delayTime[i] = 0.0;
  41. gateTime[i] = 0.0;
  42. bpm[i] = 0.0;
  43. }
  44. }
  45. Core core;
  46. bool delayState[4];
  47. bool gateState[4];
  48. float delayTime[4];
  49. int delayTimeMs[4];
  50. int delaySprMs[4];
  51. float gateTime[4];
  52. int gateTimeMs[4];
  53. int gateSprMs[4];
  54. float bpm[4];
  55. int division[4];
  56. int actDelayMs[4] = {0, 0, 0, 0};
  57. int actGateMs[4] = {0, 0, 0, 0};
  58. AHPulseGenerator delayPhase[4];
  59. AHPulseGenerator gatePhase[4];
  60. SchmittTrigger inTrigger[4];
  61. int counter[4];
  62. BpmCalculator bpmCalc[4];
  63. };
  64. void Imperfect2::step() {
  65. stepX++;
  66. float dlyLen;
  67. float dlySpr;
  68. float gateLen;
  69. float gateSpr;
  70. int lastValidInput = -1;
  71. for (int i = 0; i < 4; i++) {
  72. bool generateSignal = false;
  73. bool inputActive = inputs[TRIG_INPUT + i].active;
  74. bool haveTrigger = inTrigger[i].process(inputs[TRIG_INPUT + i].value);
  75. bool outputActive = outputs[OUT_OUTPUT + i].active;
  76. // This is where we manage row-chaining/normalisation, i.e a row can be active without an
  77. // input by receiving the input clock from a previous (higher) row
  78. // If we have an active input, we should forget about previous valid inputs
  79. if (inputActive) {
  80. bpm[i] = bpmCalc[i].calculateBPM(delta, inputs[TRIG_INPUT + i].value);
  81. lastValidInput = -1;
  82. if (haveTrigger) {
  83. if (debugEnabled()) { std::cout << stepX << " " << i << " has active input and has received trigger" << std::endl; }
  84. generateSignal = true;
  85. lastValidInput = i; // Row i has a valid input
  86. }
  87. } else {
  88. // We have an output plugged in this row and previously seen a trigger on previous row
  89. if (outputActive && lastValidInput > -1) {
  90. if (debugEnabled()) { std::cout << stepX << " " << i << " has active out and has seen trigger on " << lastValidInput << std::endl; }
  91. generateSignal = true;
  92. }
  93. bpm[i] = 0.0;
  94. }
  95. if (inputs[DELAY_INPUT + i].active) {
  96. dlyLen = log2(fabs(inputs[DELAY_INPUT + i].value) + 1.0f);
  97. } else {
  98. dlyLen = log2(params[DELAY_PARAM + i].value);
  99. }
  100. if (inputs[DELAYSPREAD_INPUT + i].active) {
  101. dlySpr = log2(fabs(inputs[DELAYSPREAD_INPUT + i].value) + 1.0f);
  102. } else {
  103. dlySpr = log2(params[DELAYSPREAD_PARAM + i].value);
  104. }
  105. if (inputs[LENGTH_INPUT + i].active) {
  106. gateLen = log2(fabs(inputs[LENGTH_INPUT + i].value) + 1.001f);
  107. } else {
  108. gateLen = log2(params[LENGTH_PARAM + i].value);
  109. }
  110. if (inputs[LENGTHSPREAD_INPUT + i].active) {
  111. gateSpr = log2(fabs(inputs[LENGTHSPREAD_INPUT + i].value) + 1.0f);
  112. } else {
  113. gateSpr = log2(params[LENGTHSPREAD_PARAM + i].value);
  114. }
  115. division[i] = params[DIVISION_PARAM + i].value;
  116. delayTimeMs[i] = dlyLen * 1000;
  117. delaySprMs[i] = dlySpr * 2000; // scaled by ±2 below
  118. gateTimeMs[i] = gateLen * 1000;
  119. gateSprMs[i] = gateSpr * 2000; // scaled by ±2 below
  120. if (generateSignal) {
  121. counter[i]++;
  122. int target = division[i];
  123. if (debugEnabled()) {
  124. std::cout << stepX << " Div: " << i << ": Target: " << target << " Cnt: " << counter[lastValidInput] << " Exp: " << counter[lastValidInput] % division[i] << std::endl;
  125. }
  126. if (counter[lastValidInput] % target == 0) {
  127. // check that we are not in the gate phase
  128. if (!gatePhase[i].ishigh() && !delayPhase[i].ishigh()) {
  129. // Determine delay and gate times for all active outputs
  130. double rndD = clamp(core.gaussrand(), -2.0f, 2.0f);
  131. delayTime[i] = clamp(dlyLen + dlySpr * rndD, 0.0f, 100.0f);
  132. // The modified gate time cannot be earlier than the start of the delay
  133. double rndG = clamp(core.gaussrand(), -2.0f, 2.0f);
  134. gateTime[i] = clamp(gateLen + gateSpr * rndG, Core::TRIGGER, 100.0f);
  135. if (debugEnabled()) {
  136. std::cout << stepX << " Delay: " << i << ": Len: " << dlyLen << " Spr: " << dlySpr << " r: " << rndD << " = " << delayTime[i] << std::endl;
  137. std::cout << stepX << " Gate: " << i << ": Len: " << gateLen << ", Spr: " << gateSpr << " r: " << rndG << " = " << gateTime[i] << std::endl;
  138. }
  139. // Trigger the respective delay pulse generator
  140. delayState[i] = true;
  141. if (delayPhase[i].trigger(delayTime[i])) {
  142. actDelayMs[i] = delayTime[i] * 1000;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. for (int i = 0; i < 4; i++) {
  149. if (delayState[i] && !delayPhase[i].process(delta)) {
  150. if (gatePhase[i].trigger(gateTime[i])) {
  151. actGateMs[i] = gateTime[i] * 1000;
  152. }
  153. gateState[i] = true;
  154. delayState[i] = false;
  155. }
  156. lights[OUT_LIGHT + i * 2].value = 0.0f;
  157. lights[OUT_LIGHT + i * 2 + 1].value = 0.0f;
  158. if (gatePhase[i].process(delta)) {
  159. outputs[OUT_OUTPUT + i].value = 10.0f;
  160. lights[OUT_LIGHT + i * 2].value = 1.0f;
  161. lights[OUT_LIGHT + i * 2 + 1].value = 0.0f;
  162. } else {
  163. outputs[OUT_OUTPUT + i].value = 0.0f;
  164. gateState[i] = false;
  165. if (delayState[i]) {
  166. lights[OUT_LIGHT + i * 2].value = 0.0f;
  167. lights[OUT_LIGHT + i * 2 + 1].value = 1.0f;
  168. }
  169. }
  170. }
  171. }
  172. struct Imperfect2Box : TransparentWidget {
  173. Imperfect2 *module;
  174. int frame = 0;
  175. std::shared_ptr<Font> font;
  176. float *bpm;
  177. int *dly;
  178. int *dlySpr;
  179. int *gate;
  180. int *gateSpr;
  181. int *division;
  182. int *actDly;
  183. int *actGate;
  184. Imperfect2Box() {
  185. font = Font::load(assetPlugin(plugin, "res/DSEG14ClassicMini-BoldItalic.ttf"));
  186. }
  187. void draw(NVGcontext *vg) override {
  188. Vec pos = Vec(0, 15);
  189. nvgFontSize(vg, 10);
  190. nvgFontFaceId(vg, font->handle);
  191. nvgTextLetterSpacing(vg, -1);
  192. nvgTextAlign(vg, NVGalign::NVG_ALIGN_CENTER);
  193. nvgFillColor(vg, nvgRGBA(255, 0, 0, 0xff));
  194. char text[10];
  195. if (*bpm == 0.0f) {
  196. snprintf(text, sizeof(text), "-");
  197. } else {
  198. snprintf(text, sizeof(text), "%.1f", *bpm);
  199. }
  200. nvgText(vg, pos.x + 20, pos.y, text, NULL);
  201. snprintf(text, sizeof(text), "%d", *dly);
  202. nvgText(vg, pos.x + 74, pos.y, text, NULL);
  203. if (*dlySpr != 0) {
  204. snprintf(text, sizeof(text), "%d", *dlySpr);
  205. nvgText(vg, pos.x + 144, pos.y, text, NULL);
  206. }
  207. snprintf(text, sizeof(text), "%d", *gate);
  208. nvgText(vg, pos.x + 214, pos.y, text, NULL);
  209. if (*gateSpr != 0) {
  210. snprintf(text, sizeof(text), "%d", *gateSpr);
  211. nvgText(vg, pos.x + 284, pos.y, text, NULL);
  212. }
  213. snprintf(text, sizeof(text), "%d", *division);
  214. nvgText(vg, pos.x + 334, pos.y, text, NULL);
  215. nvgFillColor(vg, nvgRGBA(0, 0, 0, 0xff));
  216. snprintf(text, sizeof(text), "%d", *actDly);
  217. nvgText(vg, pos.x + 372, pos.y, text, NULL);
  218. snprintf(text, sizeof(text), "%d", *actGate);
  219. nvgText(vg, pos.x + 408, pos.y, text, NULL);
  220. }
  221. };
  222. struct Imperfect2Widget : ModuleWidget {
  223. Imperfect2Widget(Imperfect2 *module);
  224. };
  225. Imperfect2Widget::Imperfect2Widget(Imperfect2 *module) : ModuleWidget(module) {
  226. UI ui;
  227. box.size = Vec(450, 380);
  228. {
  229. SVGPanel *panel = new SVGPanel();
  230. panel->box.size = box.size;
  231. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Imperfect2.svg")));
  232. addChild(panel);
  233. }
  234. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  235. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  236. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  237. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  238. {
  239. Imperfect2Box *display = new Imperfect2Box();
  240. display->module = module;
  241. display->box.pos = Vec(10, 95);
  242. display->box.size = Vec(200, 20);
  243. display->bpm = &(module->bpm[0]);
  244. display->dly = &(module->delayTimeMs[0]);
  245. display->dlySpr = &(module->delaySprMs[0]);
  246. display->gate = &(module->gateTimeMs[0]);
  247. display->gateSpr = &(module->gateSprMs[0]);
  248. display->division = &(module->division[0]);
  249. display->actDly = &(module->actDelayMs[0]);
  250. display->actGate = &(module->actGateMs[0]);
  251. addChild(display);
  252. }
  253. {
  254. Imperfect2Box *display = new Imperfect2Box();
  255. display->module = module;
  256. display->box.pos = Vec(10, 165);
  257. display->box.size = Vec(200, 20);
  258. display->bpm = &(module->bpm[1]);
  259. display->dly = &(module->delayTimeMs[1]);
  260. display->dlySpr = &(module->delaySprMs[1]);
  261. display->gate = &(module->gateTimeMs[1]);
  262. display->gateSpr = &(module->gateSprMs[1]);
  263. display->division = &(module->division[1]);
  264. display->actDly = &(module->actDelayMs[1]);
  265. display->actGate = &(module->actGateMs[1]);
  266. addChild(display);
  267. }
  268. {
  269. Imperfect2Box *display = new Imperfect2Box();
  270. display->module = module;
  271. display->box.pos = Vec(10, 235);
  272. display->box.size = Vec(200, 20);
  273. display->bpm = &(module->bpm[2]);
  274. display->dly = &(module->delayTimeMs[2]);
  275. display->dlySpr = &(module->delaySprMs[2]);
  276. display->gate = &(module->gateTimeMs[2]);
  277. display->gateSpr = &(module->gateSprMs[2]);
  278. display->division = &(module->division[2]);
  279. display->actDly = &(module->actDelayMs[2]);
  280. display->actGate = &(module->actGateMs[2]);
  281. addChild(display);
  282. }
  283. {
  284. Imperfect2Box *display = new Imperfect2Box();
  285. display->module = module;
  286. display->box.pos = Vec(10, 305);
  287. display->box.size = Vec(200, 20);
  288. display->bpm = &(module->bpm[3]);
  289. display->dly = &(module->delayTimeMs[3]);
  290. display->dlySpr = &(module->delaySprMs[3]);
  291. display->gate = &(module->gateTimeMs[3]);
  292. display->gateSpr = &(module->gateSprMs[3]);
  293. display->division = &(module->division[3]);
  294. display->actDly = &(module->actDelayMs[3]);
  295. display->actGate = &(module->actGateMs[3]);
  296. addChild(display);
  297. }
  298. for (int i = 0; i < 4; i++) {
  299. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 0, i * 2 + 1, true, true), Port::INPUT, module, Imperfect2::TRIG_INPUT + i));
  300. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 1, i * 2 + 1, true, true), Port::INPUT, module, Imperfect2::DELAY_INPUT + i));
  301. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 2, i * 2 + 1, true, true), module, Imperfect2::DELAY_PARAM + i, 1.0f, 2.0f, 1.0f));
  302. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 3, i * 2 + 1, true, true), Port::INPUT, module, Imperfect2::DELAYSPREAD_INPUT + i));
  303. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, i * 2 + 1, true, true), module, Imperfect2::DELAYSPREAD_PARAM + i, 1.0f, 2.0f, 1.0f));
  304. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 5, i * 2 + 1, true, true), Port::INPUT, module, Imperfect2::LENGTH_INPUT + i));
  305. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 6, i * 2 + 1, true, true), module, Imperfect2::LENGTH_PARAM + i, 1.001f, 2.0f, 1.001f)); // Always produce gate
  306. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 7, i * 2 + 1, true, true), Port::INPUT, module, Imperfect2::LENGTHSPREAD_INPUT + i));
  307. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 8, i * 2 + 1, true, true), module, Imperfect2::LENGTHSPREAD_PARAM + i, 1.0, 2.0, 1.0f));
  308. addParam(ParamWidget::create<AHKnobSnap>(ui.getPosition(UI::KNOB, 9, i * 2 + 1, true, true), module, Imperfect2::DIVISION_PARAM + i, 1, 64, 1));
  309. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(ui.getPosition(UI::LIGHT, 10, i * 2 + 1, true, true), module, Imperfect2::OUT_LIGHT + i * 2));
  310. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 11, i * 2+ 1, true, true), Port::OUTPUT, module, Imperfect2::OUT_OUTPUT + i));
  311. }
  312. }
  313. } // namespace rack_plugin_AmalgamatedHarmonics
  314. using namespace rack_plugin_AmalgamatedHarmonics;
  315. RACK_PLUGIN_MODEL_INIT(AmalgamatedHarmonics, Imperfect2) {
  316. Model *modelImperfect2 = Model::create<Imperfect2, Imperfect2Widget>( "Amalgamated Harmonics", "Imperfect2", "Imperfect MkII", UTILITY_TAG);
  317. return modelImperfect2;
  318. }