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
7.1KB

  1. ///////////////////////////////////////////////////
  2. //
  3. // Chord Creator VCV Module
  4. //
  5. // Strum 2017
  6. //
  7. ///////////////////////////////////////////////////
  8. #include "mental.hpp"
  9. namespace rack_plugin_mental {
  10. /////////////////////////////////////////////////
  11. struct MentalChord : Module {
  12. enum ParamIds {
  13. OFFSET_PARAM,
  14. INVERSION_PARAM,
  15. VOICING_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. INPUT,
  20. OFFSET_CV_INPUT,
  21. INVERSION_CV_INPUT,
  22. VOICING_CV_INPUT,
  23. FLAT_3RD_INPUT,
  24. FLAT_5TH_INPUT,
  25. FLAT_7TH_INPUT,
  26. SUS_2_INPUT,
  27. SUS_4_INPUT,
  28. SIX_FOR_5_INPUT,
  29. ONE_FOR_7_INPUT,
  30. FLAT_9_INPUT,
  31. SHARP_9_INPUT,
  32. SIX_FOR_7_INPUT,
  33. SHARP_5_INPUT,
  34. NUM_INPUTS
  35. };
  36. enum OutputIds {
  37. OUTPUT_1,
  38. OUTPUT_2,
  39. OUTPUT_3,
  40. OUTPUT_4,
  41. OUTPUT_ROOT,
  42. OUTPUT_THIRD,
  43. OUTPUT_FIFTH,
  44. OUTPUT_SEVENTH,
  45. NUM_OUTPUTS
  46. };
  47. MentalChord() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
  48. void step() override;
  49. };
  50. /////////////////////////////////////////////////////
  51. void MentalChord::step() {
  52. float in = inputs[INPUT].value;
  53. int octave = round(in);
  54. float offset_raw = (params[OFFSET_PARAM].value) * 12 - 6 + (inputs[OFFSET_CV_INPUT].value) / 1.5;
  55. float pitch_offset = round(offset_raw) / 12;
  56. float root = in - 1.0*octave + pitch_offset;
  57. float root_or_2nd = root;
  58. float inversion_raw = (params[INVERSION_PARAM].value) * 4 - 1 + (inputs[INVERSION_CV_INPUT].value / 3);
  59. int inversion = round(inversion_raw);
  60. if (inversion > 2) inversion = 2;
  61. if (inversion < -1) inversion = -1;
  62. float voicing_raw = (params[VOICING_PARAM].value) * 5 - 2 + (inputs[VOICING_CV_INPUT].value / 3);
  63. int voicing = round(voicing_raw);
  64. if (voicing > 2) voicing = 2;
  65. if (voicing < -2) voicing = -2;
  66. float voice_1 = 0.0;
  67. float voice_2 = 0.0;
  68. float voice_3 = 0.0;
  69. float voice_4 = 0.0;
  70. int third = 4;
  71. int fifth = 7;
  72. int seventh = 11;
  73. if (inputs[FLAT_3RD_INPUT].value > 0.0) third = 3;
  74. if (inputs[FLAT_5TH_INPUT].value > 0.0) fifth = 6;
  75. if (inputs[SHARP_5_INPUT].value > 0.0) fifth = 8;
  76. if (inputs[FLAT_7TH_INPUT].value > 0.0) seventh = 10;
  77. if (inputs[SUS_2_INPUT].value > 0.0) root_or_2nd = root + (2 * (1.0/12.0));
  78. if (inputs[SUS_4_INPUT].value > 0.0) third = 5;
  79. if (inputs[SIX_FOR_5_INPUT].value > 0.0) fifth = 9;
  80. if (inputs[SIX_FOR_7_INPUT].value > 0.0) seventh = 9;
  81. if (inputs[FLAT_9_INPUT].value > 0.0) root_or_2nd = root + 1.0/12.0;
  82. if (inputs[SHARP_9_INPUT].value > 0.0) root_or_2nd = root + (3 * (1.0/12.0));
  83. if (inputs[ONE_FOR_7_INPUT].value > 0.0) seventh = 12;
  84. outputs[OUTPUT_ROOT].value = root;
  85. outputs[OUTPUT_THIRD].value = root + third * (1.0/12.0);
  86. outputs[OUTPUT_FIFTH].value = root + fifth * (1.0/12.0);
  87. outputs[OUTPUT_SEVENTH].value = root + seventh * (1.0/12.0);
  88. if (inversion == -1 )
  89. {
  90. voice_1 = root_or_2nd;
  91. voice_2 = root + third * (1.0/12.0);
  92. voice_3 = root + fifth * (1.0/12.0);
  93. voice_4 = root + seventh * (1.0/12.0);
  94. }
  95. if (inversion == 0 )
  96. {
  97. voice_1 = root + third * (1.0/12.0);
  98. voice_2 = root + fifth * (1.0/12.0);
  99. voice_3 = root + seventh * (1.0/12.0);
  100. voice_4 = root_or_2nd + 1.0;
  101. }
  102. if (inversion == 1)
  103. {
  104. voice_1 = root + fifth * (1.0/12.0);
  105. voice_2 = root + seventh * (1.0/12.0);
  106. voice_3 = root_or_2nd + 1.0;
  107. voice_4 = root + 1.0 + third * (1.0/12.0);
  108. }
  109. if (inversion == 2 )
  110. {
  111. voice_1 = root + seventh * (1.0/12.0);
  112. voice_2 = root_or_2nd + 1.0;
  113. voice_3 = root + 1.0 + third * (1.0/12.0);
  114. voice_4 = root + 1.0 + fifth * (1.0/12.0);
  115. }
  116. if (voicing == -1) voice_2 -= 1.0;
  117. if (voicing == -0) voice_3 -= 1.0;
  118. if (voicing == 1)
  119. {
  120. voice_2 -= 1.0;
  121. voice_4 -= 1.0;
  122. }
  123. if (voicing == 2)
  124. {
  125. voice_2 += 1.0;
  126. voice_4 += 1.0;
  127. }
  128. outputs[OUTPUT_1].value = voice_1;
  129. outputs[OUTPUT_2].value = voice_2;
  130. outputs[OUTPUT_3].value = voice_3;
  131. outputs[OUTPUT_4].value = voice_4;
  132. }
  133. //////////////////////////////////////////////////////////////////
  134. struct MentalChordWidget : ModuleWidget {
  135. MentalChordWidget(MentalChord *module);
  136. };
  137. MentalChordWidget::MentalChordWidget(MentalChord *module) : ModuleWidget(module)
  138. {
  139. setPanel(SVG::load(assetPlugin(plugin, "res/MentalChord.svg")));
  140. addParam(ParamWidget::create<MedKnob>(Vec(3, 20), module, MentalChord::OFFSET_PARAM, 0.0, 1.0, 0.5));
  141. addInput(Port::create<CVInPort>(Vec(3, 50), Port::INPUT, module, MentalChord::OFFSET_CV_INPUT));
  142. addParam(ParamWidget::create<MedKnob>(Vec(33, 20), module, MentalChord::INVERSION_PARAM, 0.0, 1.0, 0.0));
  143. addParam(ParamWidget::create<MedKnob>(Vec(63, 20), module, MentalChord::VOICING_PARAM, 0.0, 1.0, 0.0));
  144. addInput(Port::create<CVInPort>(Vec(3, 100), Port::INPUT, module, MentalChord::INPUT));
  145. addInput(Port::create<CVInPort>(Vec(33, 50), Port::INPUT, module, MentalChord::INVERSION_CV_INPUT));
  146. addInput(Port::create<CVInPort>(Vec(63, 50), Port::INPUT, module, MentalChord::VOICING_CV_INPUT));
  147. addInput(Port::create<GateInPort>(Vec(3, 130), Port::INPUT, module, MentalChord::FLAT_3RD_INPUT));
  148. addInput(Port::create<GateInPort>(Vec(3, 155), Port::INPUT, module, MentalChord::FLAT_5TH_INPUT));
  149. addInput(Port::create<GateInPort>(Vec(3, 180), Port::INPUT, module, MentalChord::FLAT_7TH_INPUT));
  150. addInput(Port::create<GateInPort>(Vec(3, 210), Port::INPUT, module, MentalChord::SUS_2_INPUT));
  151. addInput(Port::create<GateInPort>(Vec(3, 235), Port::INPUT, module, MentalChord::SUS_4_INPUT));
  152. addInput(Port::create<GateInPort>(Vec(3, 260), Port::INPUT, module, MentalChord::SIX_FOR_5_INPUT));
  153. addInput(Port::create<GateInPort>(Vec(3, 285), Port::INPUT, module, MentalChord::ONE_FOR_7_INPUT));
  154. addInput(Port::create<GateInPort>(Vec(3, 310), Port::INPUT, module, MentalChord::FLAT_9_INPUT));
  155. addInput(Port::create<GateInPort>(Vec(3, 335), Port::INPUT, module, MentalChord::SHARP_9_INPUT));
  156. addInput(Port::create<GateInPort>(Vec(33, 316), Port::INPUT, module, MentalChord::SIX_FOR_7_INPUT));
  157. addInput(Port::create<GateInPort>(Vec(33, 341), Port::INPUT, module, MentalChord::SHARP_5_INPUT));
  158. addOutput(Port::create<CVOutPort>(Vec(63, 100), Port::OUTPUT, module, MentalChord::OUTPUT_ROOT));
  159. addOutput(Port::create<CVOutPort>(Vec(63, 125), Port::OUTPUT, module, MentalChord::OUTPUT_THIRD));
  160. addOutput(Port::create<CVOutPort>(Vec(63, 150), Port::OUTPUT, module, MentalChord::OUTPUT_FIFTH));
  161. addOutput(Port::create<CVOutPort>(Vec(63, 175), Port::OUTPUT, module, MentalChord::OUTPUT_SEVENTH));
  162. addOutput(Port::create<CVOutPort>(Vec(63, 250), Port::OUTPUT, module, MentalChord::OUTPUT_1));
  163. addOutput(Port::create<CVOutPort>(Vec(63, 275), Port::OUTPUT, module, MentalChord::OUTPUT_2));
  164. addOutput(Port::create<CVOutPort>(Vec(63, 300), Port::OUTPUT, module, MentalChord::OUTPUT_3));
  165. addOutput(Port::create<CVOutPort>(Vec(63, 325), Port::OUTPUT, module, MentalChord::OUTPUT_4));
  166. }
  167. } // namespace rack_plugin_mental
  168. using namespace rack_plugin_mental;
  169. RACK_PLUGIN_MODEL_INIT(mental, MentalChord) {
  170. Model *modelMentalChord = Model::create<MentalChord, MentalChordWidget>("mental", "MentalChord", "Chord", CONTROLLER_TAG);
  171. return modelMentalChord;
  172. }