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.

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