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.

263 lines
7.5KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. struct MIDICCToCVInterface : Module {
  4. enum ParamIds {
  5. NUM_PARAMS
  6. };
  7. enum InputIds {
  8. NUM_INPUTS
  9. };
  10. enum OutputIds {
  11. ENUMS(CC_OUTPUT, 16),
  12. NUM_OUTPUTS
  13. };
  14. enum LightIds {
  15. NUM_LIGHTS
  16. };
  17. midi::InputQueue midiInput;
  18. int8_t values[128];
  19. dsp::ExponentialFilter valueFilters[16];
  20. int learningId = -1;
  21. int ccs[16] = {};
  22. bool jump[16] = {};
  23. MIDICCToCVInterface() {
  24. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  25. onReset();
  26. }
  27. void onReset() override {
  28. for (int i = 0; i < 128; i++) {
  29. values[i] = 0;
  30. }
  31. for (int i = 0; i < 16; i++) {
  32. ccs[i] = i;
  33. }
  34. learningId = -1;
  35. }
  36. void step() override {
  37. midi::Message msg;
  38. while (midiInput.shift(&msg)) {
  39. processMessage(msg);
  40. }
  41. float lambda = app()->engine->getSampleTime() * 100.f;
  42. for (int i = 0; i < 16; i++) {
  43. int learnedCc = ccs[i];
  44. float value = rescale(values[learnedCc], 0, 127, 0.f, 10.f);
  45. valueFilters[i].lambda = lambda;
  46. // Smooth value unless we're jumping there
  47. if (jump[i]) {
  48. valueFilters[i].out = value;
  49. jump[i] = false;
  50. }
  51. else {
  52. valueFilters[i].process(value);
  53. }
  54. outputs[CC_OUTPUT + i].setVoltage(valueFilters[i].out);
  55. }
  56. }
  57. void processMessage(midi::Message msg) {
  58. switch (msg.status()) {
  59. // cc
  60. case 0xb: {
  61. uint8_t cc = msg.note();
  62. // Learn
  63. if (learningId >= 0 && values[cc] != msg.data2) {
  64. ccs[learningId] = cc;
  65. learningId = -1;
  66. }
  67. int8_t oldValue = values[cc];
  68. // Allow CC to be negative if the 8th bit is set.
  69. // The gamepad driver abuses this, for example.
  70. int8_t value = msg.data2;
  71. // Detect behavior from MIDI buttons.
  72. // Don't run these through a smoothing filter.
  73. if ((oldValue == 0 && value == 127) || (oldValue == 127 && value == 0))
  74. jump[cc] = true;
  75. values[cc] = value;
  76. } break;
  77. default: break;
  78. }
  79. }
  80. json_t *dataToJson() override {
  81. json_t *rootJ = json_object();
  82. json_t *ccsJ = json_array();
  83. for (int i = 0; i < 16; i++) {
  84. json_array_append_new(ccsJ, json_integer(ccs[i]));
  85. }
  86. json_object_set_new(rootJ, "ccs", ccsJ);
  87. // Remember values so users don't have to touch MIDI controller knobs when restarting Rack
  88. json_t *valuesJ = json_array();
  89. for (int i = 0; i < 128; i++) {
  90. json_array_append_new(valuesJ, json_integer(values[i]));
  91. }
  92. json_object_set_new(rootJ, "values", valuesJ);
  93. json_object_set_new(rootJ, "midi", midiInput.toJson());
  94. return rootJ;
  95. }
  96. void dataFromJson(json_t *rootJ) override {
  97. json_t *ccsJ = json_object_get(rootJ, "ccs");
  98. if (ccsJ) {
  99. for (int i = 0; i < 16; i++) {
  100. json_t *ccJ = json_array_get(ccsJ, i);
  101. if (ccJ)
  102. ccs[i] = json_integer_value(ccJ);
  103. }
  104. }
  105. json_t *valuesJ = json_object_get(rootJ, "values");
  106. if (valuesJ) {
  107. for (int i = 0; i < 128; i++) {
  108. json_t *valueJ = json_array_get(valuesJ, i);
  109. if (valueJ) {
  110. values[i] = json_integer_value(valueJ);
  111. }
  112. }
  113. // Jump all CCs
  114. for (int i = 0; i < 16; i++) {
  115. jump[i] = true;
  116. }
  117. }
  118. json_t *midiJ = json_object_get(rootJ, "midi");
  119. if (midiJ)
  120. midiInput.fromJson(midiJ);
  121. }
  122. };
  123. struct MidiCcChoice : GridChoice {
  124. MIDICCToCVInterface *module;
  125. int id;
  126. int focusCc;
  127. MidiCcChoice() {
  128. box.size.y = mm2px(6.666);
  129. textOffset.y -= 4;
  130. }
  131. void setId(int id) override {
  132. this->id = id;
  133. }
  134. void step() override {
  135. if (!module) {
  136. text = "";
  137. return;
  138. }
  139. if (module->learningId == id) {
  140. if (0 <= focusCc)
  141. text = string::f("%d", focusCc);
  142. else
  143. text = "LRN";
  144. color.a = 0.5;
  145. }
  146. else {
  147. text = string::f("%d", module->ccs[id]);
  148. color.a = 1.0;
  149. if (app()->event->selectedWidget == this)
  150. app()->event->selectedWidget = NULL;
  151. }
  152. }
  153. void onSelect(const event::Select &e) override {
  154. e.consume(this);
  155. if (!module)
  156. return;
  157. module->learningId = id;
  158. focusCc = -1;
  159. }
  160. void onDeselect(const event::Deselect &e) override {
  161. if (!module)
  162. return;
  163. if (0 <= focusCc && focusCc < 128) {
  164. module->ccs[id] = focusCc;
  165. }
  166. module->learningId = -1;
  167. }
  168. void onSelectText(const event::SelectText &e) override {
  169. char c = e.codepoint;
  170. if ('0' <= c && c <= '9') {
  171. if (focusCc < 0)
  172. focusCc = 0;
  173. focusCc = focusCc * 10 + (c - '0');
  174. }
  175. e.consume(this);
  176. }
  177. void onSelectKey(const event::SelectKey &e) override {
  178. if (app()->event->selectedWidget == this) {
  179. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  180. event::Deselect eDeselect;
  181. onDeselect(eDeselect);
  182. app()->event->selectedWidget = NULL;
  183. e.consume(this);
  184. }
  185. }
  186. }
  187. };
  188. struct MidiCcWidget : Grid16MidiWidget {
  189. MIDICCToCVInterface *module;
  190. GridChoice *createGridChoice() override {
  191. MidiCcChoice *gridChoice = new MidiCcChoice;
  192. gridChoice->module = module;
  193. return gridChoice;
  194. }
  195. };
  196. struct MIDICCToCVInterfaceWidget : ModuleWidget {
  197. MIDICCToCVInterfaceWidget(MIDICCToCVInterface *module) : ModuleWidget(module) {
  198. setPanel(SVG::load(asset::system("res/Core/MIDICCToCVInterface.svg")));
  199. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  200. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  201. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  202. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  203. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 0));
  204. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 1));
  205. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 2));
  206. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDICCToCVInterface::CC_OUTPUT + 3));
  207. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 4));
  208. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 5));
  209. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 6));
  210. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDICCToCVInterface::CC_OUTPUT + 7));
  211. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 8));
  212. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 9));
  213. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 10));
  214. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDICCToCVInterface::CC_OUTPUT + 11));
  215. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 12));
  216. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 13));
  217. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 14));
  218. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDICCToCVInterface::CC_OUTPUT + 15));
  219. MidiCcWidget *midiWidget = createWidget<MidiCcWidget>(mm2px(Vec(3.399621, 14.837339)));
  220. midiWidget->module = module;
  221. midiWidget->box.size = mm2px(Vec(44, 54.667));
  222. if (module)
  223. midiWidget->midiIO = &module->midiInput;
  224. midiWidget->createGridChoices();
  225. addChild(midiWidget);
  226. }
  227. };
  228. Model *modelMIDICCToCVInterface = createModel<MIDICCToCVInterface, MIDICCToCVInterfaceWidget>("MIDICCToCVInterface");