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.

214 lines
6.2KB

  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 Imperfect : AHModule {
  8. enum ParamIds {
  9. DELAY_PARAM,
  10. DELAYSPREAD_PARAM = DELAY_PARAM + 8,
  11. LENGTH_PARAM = DELAYSPREAD_PARAM + 8,
  12. LENGTHSPREAD_PARAM = LENGTH_PARAM + 8,
  13. DIVISION_PARAM = LENGTHSPREAD_PARAM + 8,
  14. NUM_PARAMS = DIVISION_PARAM + 8
  15. };
  16. enum InputIds {
  17. TRIG_INPUT,
  18. NUM_INPUTS = TRIG_INPUT + 8
  19. };
  20. enum OutputIds {
  21. OUT_OUTPUT,
  22. NUM_OUTPUTS = OUT_OUTPUT + 8
  23. };
  24. enum LightIds {
  25. OUT_LIGHT,
  26. NUM_LIGHTS = OUT_LIGHT + 16
  27. };
  28. Imperfect() : AHModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  29. reset();
  30. }
  31. void step() override;
  32. void reset() override {
  33. for (int i = 0; i < 8; i++) {
  34. delayState[i] = false;
  35. gateState[i] = false;
  36. delayTime[i] = 0.0;
  37. gateTime[i] = 0.0;
  38. }
  39. }
  40. Core core;
  41. bool delayState[8];
  42. bool gateState[8];
  43. float delayTime[8];
  44. float gateTime[8];
  45. PulseGenerator delayPhase[8];
  46. PulseGenerator gatePhase[8];
  47. SchmittTrigger inTrigger[8];
  48. int counter[8];
  49. };
  50. void Imperfect::step() {
  51. stepX++;
  52. float dlyLen;
  53. float dlySpr;
  54. float gateLen;
  55. float gateSpr;
  56. int lastValidInput = -1;
  57. for (int i = 0; i < 8; i++) {
  58. bool generateSignal = false;
  59. bool inputActive = inputs[TRIG_INPUT + i].active;
  60. bool haveTrigger = inTrigger[i].process(inputs[TRIG_INPUT + i].value);
  61. bool outputActive = outputs[OUT_OUTPUT + i].active;
  62. // If we have an active input, we should forget about previous valid inputs
  63. if (inputActive) {
  64. lastValidInput = -1;
  65. if (haveTrigger) {
  66. if (debugEnabled()) { std::cout << stepX << " " << i << " has active input and has received trigger" << std::endl; }
  67. generateSignal = true;
  68. lastValidInput = i;
  69. }
  70. } else {
  71. // We have an output and previously seen a trigger
  72. if (outputActive && lastValidInput > -1) {
  73. if (debugEnabled()) { std::cout << stepX << " " << i << " has active out and has seen trigger on " << lastValidInput << std::endl; }
  74. generateSignal = true;
  75. }
  76. }
  77. if (generateSignal) {
  78. counter[i]++;
  79. int target = core.ipow(2,params[DIVISION_PARAM + i].value);
  80. if (debugEnabled()) {
  81. std::cout << stepX << " Div: " << i << ": Target: " << target << " Cnt: " << counter[lastValidInput] << " Exp: " << counter[lastValidInput] % target << std::endl;
  82. }
  83. if (counter[lastValidInput] % target == 0) {
  84. dlyLen = log2(params[DELAY_PARAM + i].value);
  85. dlySpr = log2(params[DELAYSPREAD_PARAM + i].value);
  86. gateLen = log2(params[LENGTH_PARAM + i].value);
  87. gateSpr = log2(params[LENGTHSPREAD_PARAM + i].value);
  88. // Determine delay and gate times for all active outputs
  89. double rndD = clamp(core.gaussrand(), -2.0f, 2.0f);
  90. delayTime[i] = clamp(dlyLen + dlySpr * rndD, 0.0f, 100.0f);
  91. // The modified gate time cannot be earlier than the start of the delay
  92. double rndG = clamp(core.gaussrand(), -2.0f, 2.0f);
  93. gateTime[i] = clamp(gateLen + gateSpr * rndG, 0.02f, 100.0f);
  94. if (debugEnabled()) {
  95. std::cout << stepX << " Delay: " << i << ": Len: " << dlyLen << " Spr: " << dlySpr << " r: " << rndD << " = " << delayTime[i] << std::endl;
  96. std::cout << stepX << " Gate: " << i << ": Len: " << gateLen << ", Spr: " << gateSpr << " r: " << rndG << " = " << gateTime[i] << std::endl;
  97. }
  98. // Trigger the respective delay pulse generators
  99. delayState[i] = true;
  100. delayPhase[i].trigger(delayTime[i]);
  101. }
  102. }
  103. }
  104. for (int i = 0; i < 8; i++) {
  105. if (delayState[i] && !delayPhase[i].process(delta)) {
  106. gatePhase[i].trigger(gateTime[i]);
  107. gateState[i] = true;
  108. delayState[i] = false;
  109. }
  110. lights[OUT_LIGHT + i * 2].value = 0.0;
  111. lights[OUT_LIGHT + i * 2 + 1].value = 0.0;
  112. if (gatePhase[i].process(delta)) {
  113. outputs[OUT_OUTPUT + i].value = 10.0;
  114. lights[OUT_LIGHT + i * 2].value = 1.0;
  115. lights[OUT_LIGHT + i * 2 + 1].value = 0.0;
  116. } else {
  117. outputs[OUT_OUTPUT + i].value = 0.0;
  118. gateState[i] = false;
  119. if (delayState[i]) {
  120. lights[OUT_LIGHT + i * 2].value = 0.0;
  121. lights[OUT_LIGHT + i * 2 + 1].value = 1.0;
  122. }
  123. }
  124. }
  125. }
  126. struct ImperfectWidget : ModuleWidget {
  127. ImperfectWidget(Imperfect *module);
  128. };
  129. ImperfectWidget::ImperfectWidget(Imperfect *module) : ModuleWidget(module) {
  130. UI ui;
  131. box.size = Vec(315, 380);
  132. {
  133. SVGPanel *panel = new SVGPanel();
  134. panel->box.size = box.size;
  135. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Imperfect.svg")));
  136. addChild(panel);
  137. }
  138. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  139. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  140. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  141. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  142. for (int i = 0; i < 8; i++) {
  143. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 0, i + 1, true, true), Port::INPUT, module, Imperfect::TRIG_INPUT + i));
  144. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 1, i + 1, true, true), module, Imperfect::DELAY_PARAM + i, 1.0, 2.0, 1.0));
  145. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 2, i + 1, true, true), module, Imperfect::DELAYSPREAD_PARAM + i, 1.0, 2.0, 1.0));
  146. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 3, i + 1, true, true), module, Imperfect::LENGTH_PARAM + i, 1.001, 2.0, 1.001)); // Always produce gate
  147. addParam(ParamWidget::create<AHKnobNoSnap>(ui.getPosition(UI::KNOB, 4, i + 1, true, true), module, Imperfect::LENGTHSPREAD_PARAM + i, 1.0, 2.0, 1.0));
  148. addParam(ParamWidget::create<AHKnobSnap>(ui.getPosition(UI::KNOB, 5, i + 1, true, true), module, Imperfect::DIVISION_PARAM + i, 0, 8, 0));
  149. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(ui.getPosition(UI::LIGHT, 6, i + 1, true, true), module, Imperfect::OUT_LIGHT + i * 2));
  150. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 7, i + 1, true, true), Port::OUTPUT, module, Imperfect::OUT_OUTPUT + i));
  151. }
  152. }
  153. } // namespace rack_plugin_AmalgamatedHarmonics
  154. using namespace rack_plugin_AmalgamatedHarmonics;
  155. RACK_PLUGIN_MODEL_INIT(AmalgamatedHarmonics, Imperfect) {
  156. Model *modelImperfect = Model::create<Imperfect, ImperfectWidget>( "Amalgamated Harmonics", "Imperfect", "Imperfect (deprecated)", UTILITY_TAG);
  157. return modelImperfect;
  158. }