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.

190 lines
5.9KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include "dsp/filter.hpp"
  4. struct MIDICCToCVInterface : Module {
  5. enum ParamIds {
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. NUM_INPUTS
  10. };
  11. enum OutputIds {
  12. ENUMS(CC_OUTPUT, 16),
  13. NUM_OUTPUTS
  14. };
  15. enum LightIds {
  16. NUM_LIGHTS
  17. };
  18. MidiInputQueue midiInput;
  19. int8_t ccs[128];
  20. ExponentialFilter ccFilters[16];
  21. int learningId = -1;
  22. int learnedCcs[16] = {};
  23. MIDICCToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  24. onReset();
  25. }
  26. void onReset() override {
  27. for (int i = 0; i < 128; i++) {
  28. ccs[i] = 0;
  29. }
  30. for (int i = 0; i < 16; i++) {
  31. learnedCcs[i] = i;
  32. }
  33. learningId = -1;
  34. }
  35. void step() override {
  36. MidiMessage msg;
  37. while (midiInput.shift(&msg)) {
  38. processMessage(msg);
  39. }
  40. float lambda = 100.f * engineGetSampleTime();
  41. for (int i = 0; i < 16; i++) {
  42. int learnedCc = learnedCcs[i];
  43. float value = rescale(clamp(ccs[learnedCc], -127, 127), 0, 127, 0.f, 10.f);
  44. ccFilters[i].lambda = lambda;
  45. outputs[CC_OUTPUT + i].value = ccFilters[i].process(value);
  46. }
  47. }
  48. void processMessage(MidiMessage msg) {
  49. switch (msg.status()) {
  50. // cc
  51. case 0xb: {
  52. uint8_t cc = msg.note();
  53. // Learn
  54. if (learningId >= 0 && ccs[cc] != msg.data2) {
  55. learnedCcs[learningId] = cc;
  56. learningId = -1;
  57. }
  58. // Set CV
  59. // Allow CC to be negative if the 8th bit is set
  60. ccs[cc] = msg.data2;
  61. } break;
  62. default: break;
  63. }
  64. }
  65. json_t *toJson() override {
  66. json_t *rootJ = json_object();
  67. json_t *ccsJ = json_array();
  68. for (int i = 0; i < 16; i++) {
  69. json_t *ccJ = json_integer(learnedCcs[i]);
  70. json_array_append_new(ccsJ, ccJ);
  71. }
  72. json_object_set_new(rootJ, "ccs", ccsJ);
  73. json_object_set_new(rootJ, "midi", midiInput.toJson());
  74. return rootJ;
  75. }
  76. void fromJson(json_t *rootJ) override {
  77. json_t *ccsJ = json_object_get(rootJ, "ccs");
  78. if (ccsJ) {
  79. for (int i = 0; i < 16; i++) {
  80. json_t *ccJ = json_array_get(ccsJ, i);
  81. if (ccJ)
  82. learnedCcs[i] = json_integer_value(ccJ);
  83. }
  84. }
  85. json_t *midiJ = json_object_get(rootJ, "midi");
  86. if (midiJ)
  87. midiInput.fromJson(midiJ);
  88. }
  89. };
  90. struct MidiCcChoice : GridChoice {
  91. MIDICCToCVInterface *module;
  92. int id;
  93. MidiCcChoice() {
  94. box.size.y = mm2px(6.666);
  95. textOffset.y -= 4;
  96. }
  97. void setId(int id) override {
  98. this->id = id;
  99. }
  100. void step() override {
  101. if (module->learningId == id) {
  102. text = "LRN";
  103. color.a = 0.5;
  104. }
  105. else {
  106. text = stringf("%d", module->learnedCcs[id]);
  107. color.a = 1.0;
  108. if (gFocusedWidget == this)
  109. gFocusedWidget = NULL;
  110. }
  111. }
  112. void onFocus(EventFocus &e) override {
  113. e.consumed = true;
  114. module->learningId = id;
  115. }
  116. void onDefocus(EventDefocus &e) override {
  117. module->learningId = -1;
  118. }
  119. };
  120. struct MidiCcWidget : Grid16MidiWidget {
  121. MIDICCToCVInterface *module;
  122. GridChoice *createGridChoice() override {
  123. MidiCcChoice *gridChoice = new MidiCcChoice();
  124. gridChoice->module = module;
  125. return gridChoice;
  126. }
  127. };
  128. struct MIDICCToCVInterfaceWidget : ModuleWidget {
  129. MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) {
  130. setPanel(SVG::load(assetGlobal("res/Core/MIDICCToCVInterface.svg")));
  131. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  132. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  133. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  134. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  135. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 0));
  136. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 1));
  137. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 2));
  138. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 3));
  139. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 4));
  140. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 5));
  141. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 6));
  142. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 7));
  143. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 8));
  144. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 9));
  145. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 10));
  146. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 11));
  147. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 12));
  148. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 13));
  149. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14));
  150. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15));
  151. MidiCcWidget *midiWidget = Widget::create<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339)));
  152. midiWidget->module = module;
  153. midiWidget->box.size = mm2px(Vec(44, 54.667));
  154. midiWidget->midiIO = &module->midiInput;
  155. midiWidget->createGridChoices();
  156. addChild(midiWidget);
  157. }
  158. };
  159. Model *modelMIDICCToCVInterface = Model::create<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG);