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.

220 lines
6.6KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include "dsp/filter.hpp"
  4. #include "window.hpp"
  5. struct MIDICCToCVInterface : Module {
  6. enum ParamIds {
  7. NUM_PARAMS
  8. };
  9. enum InputIds {
  10. NUM_INPUTS
  11. };
  12. enum OutputIds {
  13. ENUMS(CC_OUTPUT, 16),
  14. NUM_OUTPUTS
  15. };
  16. enum LightIds {
  17. NUM_LIGHTS
  18. };
  19. MidiInputQueue midiInput;
  20. int8_t ccs[128];
  21. ExponentialFilter ccFilters[16];
  22. int learningId = -1;
  23. int learnedCcs[16] = {};
  24. MIDICCToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  25. onReset();
  26. }
  27. void onReset() override {
  28. for (int i = 0; i < 128; i++) {
  29. ccs[i] = 0;
  30. }
  31. for (int i = 0; i < 16; i++) {
  32. learnedCcs[i] = i;
  33. }
  34. learningId = -1;
  35. }
  36. void step() override {
  37. MidiMessage msg;
  38. while (midiInput.shift(&msg)) {
  39. processMessage(msg);
  40. }
  41. float lambda = 100.f * engineGetSampleTime();
  42. for (int i = 0; i < 16; i++) {
  43. int learnedCc = learnedCcs[i];
  44. float value = rescale(clamp(ccs[learnedCc], -127, 127), 0, 127, 0.f, 10.f);
  45. ccFilters[i].lambda = lambda;
  46. outputs[CC_OUTPUT + i].value = ccFilters[i].process(value);
  47. }
  48. }
  49. void processMessage(MidiMessage msg) {
  50. switch (msg.status()) {
  51. // cc
  52. case 0xb: {
  53. uint8_t cc = msg.note();
  54. // Learn
  55. if (learningId >= 0 && ccs[cc] != msg.data2) {
  56. learnedCcs[learningId] = cc;
  57. learningId = -1;
  58. }
  59. // Set CV
  60. // Allow CC to be negative if the 8th bit is set
  61. ccs[cc] = msg.data2;
  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. int focusCc;
  95. MidiCcChoice() {
  96. box.size.y = mm2px(6.666);
  97. textOffset.y -= 4;
  98. }
  99. void setId(int id) override {
  100. this->id = id;
  101. }
  102. void step() override {
  103. if (module->learningId == id) {
  104. if (0 <= focusCc)
  105. text = string::stringf("%d", focusCc);
  106. else
  107. text = "LRN";
  108. color.a = 0.5;
  109. }
  110. else {
  111. text = string::stringf("%d", module->learnedCcs[id]);
  112. color.a = 1.0;
  113. if (gFocusedWidget == this)
  114. gFocusedWidget = NULL;
  115. }
  116. }
  117. void onFocus(EventFocus &e) override {
  118. e.consumed = true;
  119. module->learningId = id;
  120. focusCc = -1;
  121. }
  122. void onDefocus(EventDefocus &e) override {
  123. if (0 <= focusCc && focusCc < 128) {
  124. module->learnedCcs[id] = focusCc;
  125. }
  126. module->learningId = -1;
  127. }
  128. void onText(EventText &e) override {
  129. char c = e.codepoint;
  130. if ('0' <= c && c <= '9') {
  131. if (focusCc < 0)
  132. focusCc = 0;
  133. focusCc = focusCc * 10 + (c - '0');
  134. }
  135. e.consumed = true;
  136. }
  137. void onKey(EventKey &e) override {
  138. if (gFocusedWidget == this) {
  139. if (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) {
  140. EventDefocus eDefocus;
  141. onDefocus(eDefocus);
  142. gFocusedWidget = NULL;
  143. e.consumed = true;
  144. }
  145. }
  146. }
  147. };
  148. struct MidiCcWidget : Grid16MidiWidget {
  149. MIDICCToCVInterface *module;
  150. GridChoice *createGridChoice() override {
  151. MidiCcChoice *gridChoice = new MidiCcChoice();
  152. gridChoice->module = module;
  153. return gridChoice;
  154. }
  155. };
  156. struct MIDICCToCVInterfaceWidget : ModuleWidget {
  157. MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) {
  158. setPanel(SVG::load(assetGlobal("res/Core/MIDICCToCVInterface.svg")));
  159. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  160. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  161. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  162. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  163. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 0));
  164. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 1));
  165. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 2));
  166. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 3));
  167. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 4));
  168. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 5));
  169. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 6));
  170. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 7));
  171. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 8));
  172. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 9));
  173. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 10));
  174. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 11));
  175. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 12));
  176. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 13));
  177. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 14));
  178. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), Port::OUTPUT, module, MIDICCToCVInterface::CC_OUTPUT + 15));
  179. MidiCcWidget *midiWidget = Widget::create<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339)));
  180. midiWidget->module = module;
  181. midiWidget->box.size = mm2px(Vec(44, 54.667));
  182. midiWidget->midiIO = &module->midiInput;
  183. midiWidget->createGridChoices();
  184. addChild(midiWidget);
  185. }
  186. };
  187. Model *modelMIDICCToCVInterface = Model::create<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG);