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.

162 lines
4.6KB

  1. ///////////////////////////////////////////////////
  2. //
  3. // Pitch Quantiser VCV Module
  4. //
  5. // Strum 2017
  6. //
  7. ///////////////////////////////////////////////////
  8. #include "mental.hpp"
  9. #include "dsp/digital.hpp"
  10. namespace rack_plugin_mental {
  11. /////////////////////////////////////////////////
  12. struct MentalQuantiser : Module {
  13. enum ParamIds {
  14. PITCH_PARAM,
  15. BUTTON_PARAM,
  16. NUM_PARAMS = BUTTON_PARAM + 12
  17. };
  18. enum InputIds {
  19. INPUT,
  20. PITCH_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. OUTPUT,
  25. REF_OUT,
  26. NUM_OUTPUTS = REF_OUT + 12
  27. };
  28. enum LightIds {
  29. BUTTON_LIGHTS,
  30. OUTPUT_LIGHTS = BUTTON_LIGHTS + 12,
  31. NUM_LIGHTS = OUTPUT_LIGHTS + 12
  32. };
  33. SchmittTrigger button_triggers[12];
  34. bool button_states[12] = {true,true,true,true,true,true,true,true,true,true,true,true};
  35. float quantised = 0.0;
  36. bool found = false;
  37. int last_found = 0;
  38. MentalQuantiser() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  39. void step() override;
  40. json_t *toJson() override
  41. {
  42. json_t *rootJ = json_object();
  43. // button states
  44. json_t *button_statesJ = json_array();
  45. for (int i = 0; i < 12; i++)
  46. {
  47. json_t *button_stateJ = json_integer((int) button_states[i]);
  48. json_array_append_new(button_statesJ, button_stateJ);
  49. }
  50. json_object_set_new(rootJ, "buttons", button_statesJ);
  51. return rootJ;
  52. }
  53. void fromJson(json_t *rootJ) override
  54. {
  55. // button states
  56. json_t *button_statesJ = json_object_get(rootJ, "buttons");
  57. if (button_statesJ)
  58. {
  59. for (int i = 0; i < 12; i++)
  60. {
  61. json_t *button_stateJ = json_array_get(button_statesJ, i);
  62. if (button_stateJ)
  63. button_states[i] = !!json_integer_value(button_stateJ);
  64. }
  65. }
  66. }
  67. };
  68. /////////////////////////////////////////////////////
  69. void MentalQuantiser::step() {
  70. ////// handle button presses
  71. for (int i = 0 ; i < 12 ; i++)
  72. {
  73. if (button_triggers[i].process(params[BUTTON_PARAM+i].value))
  74. {
  75. button_states[i] = !button_states[i];
  76. }
  77. lights[BUTTON_LIGHTS + i ].value = (button_states[i]) ? 1.0 : 0.0;
  78. lights[OUTPUT_LIGHTS + i].value = 0.0;
  79. }
  80. // pitch offset
  81. float pitch_in = round(inputs[PITCH_INPUT].value)/12;
  82. float root_pitch = (pitch_in * (1/12.0)) + (round(params[PITCH_PARAM].value) * (1/12.0));
  83. // set reference outputs
  84. for (int i = 0 ; i < 12 ; i++)
  85. {
  86. outputs[REF_OUT + i].value = root_pitch + i * (1/12.0);
  87. }
  88. //////// quantise pitch to chromatic scale
  89. float in = inputs[INPUT].value;
  90. int octave = round(in);
  91. float octaves_removed = in - 1.0*octave;
  92. int semitone = round(octaves_removed*12);
  93. if (semitone < 0)
  94. {
  95. semitone +=12;
  96. octave -= 1;
  97. }
  98. quantised = root_pitch + 1.0 * octave + semitone/12.0;
  99. // quantise to scale selected by buttons
  100. if (button_states[semitone])
  101. {
  102. found = true;
  103. outputs[OUTPUT].value = quantised;
  104. lights[OUTPUT_LIGHTS + semitone].value = 1.0;
  105. }
  106. }
  107. //////////////////////////////////////////////////////////////////
  108. struct MentalQuantiserWidget : ModuleWidget {
  109. MentalQuantiserWidget(MentalQuantiser *module);
  110. };
  111. MentalQuantiserWidget::MentalQuantiserWidget(MentalQuantiser *module) : ModuleWidget(module)
  112. {
  113. setPanel(SVG::load(assetPlugin(plugin, "res/MentalQuantiser.svg")));
  114. int top_row = 40;
  115. int row_spacing = 25;
  116. addParam(ParamWidget::create<MedKnob>(Vec(62, 15), module, MentalQuantiser::PITCH_PARAM, -6.5, 6.5, 0.0));
  117. addInput(Port::create<CVInPort>(Vec(63, 45), Port::INPUT, module, MentalQuantiser::PITCH_INPUT));
  118. addInput(Port::create<CVInPort>(Vec(3, top_row), Port::INPUT, module, MentalQuantiser::INPUT));
  119. addOutput(Port::create<CVOutPort>(Vec(32, top_row), Port::OUTPUT, module, MentalQuantiser::OUTPUT));
  120. for (int i = 0; i < 12 ; i++)
  121. {
  122. addParam(ParamWidget::create<LEDButton>(Vec(3, top_row + 30 + row_spacing * i), module, MentalQuantiser::BUTTON_PARAM + i, 0.0, 1.0, 0.0));
  123. addChild(ModuleLightWidget::create<MedLight<BlueLED>>(Vec(3+5, top_row + 30 + row_spacing * i + 5), module, MentalQuantiser::BUTTON_LIGHTS + i));
  124. addChild(ModuleLightWidget::create<MedLight<BlueLED>>(Vec(30+5, top_row + 30 + row_spacing * i + 5), module, MentalQuantiser::OUTPUT_LIGHTS + i));
  125. addOutput(Port::create<CVOutPort>(Vec(63, top_row + 40 + row_spacing * i), Port::OUTPUT, module, MentalQuantiser::REF_OUT + i));
  126. }
  127. }
  128. } // namespace rack_plugin_mental
  129. using namespace rack_plugin_mental;
  130. RACK_PLUGIN_MODEL_INIT(mental, MentalQuantiser) {
  131. Model *modelMentalQuantiser = Model::create<MentalQuantiser, MentalQuantiserWidget>("mental", "MentalQuantiser", "Quantiser", QUANTIZER_TAG);
  132. return modelMentalQuantiser;
  133. }