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.

216 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 ScaleQuantizer2 : AHModule {
  8. enum ParamIds {
  9. KEY_PARAM,
  10. SCALE_PARAM,
  11. ENUMS(SHIFT_PARAM,8),
  12. TRANS_PARAM,
  13. NUM_PARAMS
  14. };
  15. enum InputIds {
  16. ENUMS(IN_INPUT,8),
  17. KEY_INPUT,
  18. SCALE_INPUT,
  19. TRANS_INPUT,
  20. ENUMS(HOLD_INPUT,8),
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. ENUMS(OUT_OUTPUT,8),
  25. ENUMS(TRIG_OUTPUT,8),
  26. NUM_OUTPUTS
  27. };
  28. enum LightIds {
  29. ENUMS(KEY_LIGHT,12),
  30. ENUMS(SCALE_LIGHT,12),
  31. NUM_LIGHTS
  32. };
  33. ScaleQuantizer2() : AHModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  34. void step() override;
  35. bool firstStep = true;
  36. int lastScale = 0;
  37. int lastRoot = 0;
  38. float lastTrans = -10000.0f;
  39. SchmittTrigger holdTrigger[8];
  40. float holdPitch[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
  41. float lastPitch[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
  42. PulseGenerator triggerPulse[8];
  43. int currScale = 0;
  44. int currRoot = 0;
  45. };
  46. void ScaleQuantizer2::step() {
  47. AHModule::step();
  48. lastScale = currScale;
  49. lastRoot = currRoot;
  50. int currNote = 0;
  51. int currDegree = 0;
  52. if (inputs[KEY_INPUT].active) {
  53. float fRoot = inputs[KEY_INPUT].value;
  54. currRoot = CoreUtil().getKeyFromVolts(fRoot);
  55. } else {
  56. currRoot = params[KEY_PARAM].value;
  57. }
  58. if (inputs[SCALE_INPUT].active) {
  59. float fScale = inputs[SCALE_INPUT].value;
  60. currScale = CoreUtil().getScaleFromVolts(fScale);
  61. } else {
  62. currScale = params[SCALE_PARAM].value;
  63. }
  64. float trans = (inputs[TRANS_INPUT].value + params[TRANS_PARAM].value) / 12.0;
  65. if (trans != 0.0) {
  66. if (trans != lastTrans) {
  67. int i;
  68. int d;
  69. trans = CoreUtil().getPitchFromVolts(trans, Core::NOTE_C, Core::SCALE_CHROMATIC, &i, &d);
  70. lastTrans = trans;
  71. } else {
  72. trans = lastTrans;
  73. }
  74. }
  75. for (int i = 0; i < 8; i++) {
  76. float holdInput = inputs[HOLD_INPUT + i].value;
  77. bool holdActive = inputs[HOLD_INPUT + i].active;
  78. bool holdStatus = holdTrigger[i].process(holdInput);
  79. float volts = inputs[IN_INPUT + i].value;
  80. float shift = params[SHIFT_PARAM + i].value;
  81. if (holdActive) {
  82. // Sample the pitch
  83. if (holdStatus && inputs[IN_INPUT + i].active) {
  84. holdPitch[i] = CoreUtil().getPitchFromVolts(volts, currRoot, currScale, &currNote, &currDegree);
  85. }
  86. } else {
  87. if (inputs[IN_INPUT + i].active) {
  88. holdPitch[i] = CoreUtil().getPitchFromVolts(volts, currRoot, currScale, &currNote, &currDegree);
  89. }
  90. }
  91. // If the quantised pitch has changed
  92. if (lastPitch[i] != holdPitch[i]) {
  93. // Pulse the gate
  94. triggerPulse[i].trigger(Core::TRIGGER);
  95. // Record the pitch
  96. lastPitch[i] = holdPitch[i];
  97. }
  98. if (triggerPulse[i].process(delta)) {
  99. outputs[TRIG_OUTPUT + i].value = 10.0f;
  100. } else {
  101. outputs[TRIG_OUTPUT + i].value = 0.0f;
  102. }
  103. outputs[OUT_OUTPUT + i].value = holdPitch[i] + shift + trans;
  104. }
  105. if (lastScale != currScale || firstStep) {
  106. for (int i = 0; i < Core::NUM_NOTES; i++) {
  107. lights[SCALE_LIGHT + i].value = 0.0f;
  108. }
  109. lights[SCALE_LIGHT + currScale].value = 1.0f;
  110. }
  111. if (lastRoot != currRoot || firstStep) {
  112. for (int i = 0; i < Core::NUM_NOTES; i++) {
  113. lights[KEY_LIGHT + i].value = 0.0f;
  114. }
  115. lights[KEY_LIGHT + currRoot].value = 1.0f;
  116. }
  117. firstStep = false;
  118. }
  119. struct ScaleQuantizer2Widget : ModuleWidget {
  120. ScaleQuantizer2Widget(ScaleQuantizer2 *module);
  121. };
  122. ScaleQuantizer2Widget::ScaleQuantizer2Widget(ScaleQuantizer2 *module) : ModuleWidget(module) {
  123. UI ui;
  124. box.size = Vec(300, 380);
  125. {
  126. SVGPanel *panel = new SVGPanel();
  127. panel->box.size = box.size;
  128. panel->setBackground(SVG::load(assetPlugin(plugin, "res/ScaleQuantizerMkII.svg")));
  129. addChild(panel);
  130. }
  131. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  132. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  133. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  134. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  135. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 0, 5, true, false), Port::INPUT, module, ScaleQuantizer2::KEY_INPUT));
  136. addParam(ParamWidget::create<AHKnobSnap>(ui.getPosition(UI::KNOB, 1, 5, true, false), module, ScaleQuantizer2::KEY_PARAM, 0.0f, 11.0f, 0.0f)); // 12 notes
  137. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 3, 5, true, false), Port::INPUT, module, ScaleQuantizer2::SCALE_INPUT));
  138. addParam(ParamWidget::create<AHKnobSnap>(ui.getPosition(UI::PORT, 4, 5, true, false), module, ScaleQuantizer2::SCALE_PARAM, 0.0f, 11.0f, 0.0f)); // 12 notes
  139. addInput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 6, 5, true, false), Port::INPUT, module, ScaleQuantizer2::TRANS_INPUT));
  140. addParam(ParamWidget::create<AHKnobSnap>(ui.getPosition(UI::PORT, 7, 5, true, false), module, ScaleQuantizer2::TRANS_PARAM, -11.0f, 11.0f, 0.0f)); // 12 notes
  141. for (int i = 0; i < 8; i++) {
  142. addInput(Port::create<PJ301MPort>(Vec(6 + i * 29, 41), Port::INPUT, module, ScaleQuantizer2::IN_INPUT + i));
  143. addParam(ParamWidget::create<AHTrimpotSnap>(Vec(9 + i * 29.1, 101), module, ScaleQuantizer2::SHIFT_PARAM + i, -3.0f, 3.0f, 0.0f));
  144. addOutput(Port::create<PJ301MPort>(Vec(6 + i * 29, 125), Port::OUTPUT, module, ScaleQuantizer2::OUT_OUTPUT + i));
  145. addInput(Port::create<PJ301MPort>(Vec(6 + i * 29, 71), Port::INPUT, module, ScaleQuantizer2::HOLD_INPUT + i));
  146. addOutput(Port::create<PJ301MPort>(Vec(6 + i * 29, 155), Port::OUTPUT, module, ScaleQuantizer2::TRIG_OUTPUT + i));
  147. }
  148. float xOffset = 18.0;
  149. float xSpace = 21.0;
  150. float xPos = 0.0;
  151. float yPos = 0.0;
  152. int scale = 0;
  153. for (int i = 0; i < 12; i++) {
  154. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(xOffset + i * 18.0f, 280.0f), module, ScaleQuantizer2::SCALE_LIGHT + i));
  155. ui.calculateKeyboard(i, xSpace, xOffset, 230.0f, &xPos, &yPos, &scale);
  156. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(xPos, yPos), module, ScaleQuantizer2::KEY_LIGHT + scale));
  157. }
  158. }
  159. } // namespace rack_plugin_AmalgamatedHarmonics
  160. using namespace rack_plugin_AmalgamatedHarmonics;
  161. RACK_PLUGIN_MODEL_INIT(AmalgamatedHarmonics, ScaleQuantizer2) {
  162. Model *modelScaleQuantizer2 = Model::create<ScaleQuantizer2, ScaleQuantizer2Widget>( "Amalgamated Harmonics", "ScaleQuantizer2", "Scale Quantizer MkII", QUANTIZER_TAG);
  163. return modelScaleQuantizer2;
  164. }