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.

255 lines
7.5KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include "event.hpp"
  4. struct MIDITriggerToCVInterface : Module {
  5. enum ParamIds {
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. NUM_INPUTS
  10. };
  11. enum OutputIds {
  12. ENUMS(TRIG_OUTPUT, 16),
  13. NUM_OUTPUTS
  14. };
  15. enum LightIds {
  16. NUM_LIGHTS
  17. };
  18. midi::InputQueue midiInput;
  19. bool gates[16];
  20. float gateTimes[16];
  21. uint8_t velocities[16];
  22. int learningId = -1;
  23. uint8_t learnedNotes[16] = {};
  24. bool velocity = false;
  25. MIDITriggerToCVInterface() {
  26. setup(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  27. onReset();
  28. }
  29. void onReset() override {
  30. for (int i = 0; i < 16; i++) {
  31. gates[i] = false;
  32. gateTimes[i] = 0.f;
  33. learnedNotes[i] = i + 36;
  34. }
  35. learningId = -1;
  36. }
  37. void pressNote(uint8_t note, uint8_t vel) {
  38. // Learn
  39. if (learningId >= 0) {
  40. learnedNotes[learningId] = note;
  41. learningId = -1;
  42. }
  43. // Find id
  44. for (int i = 0; i < 16; i++) {
  45. if (learnedNotes[i] == note) {
  46. gates[i] = true;
  47. gateTimes[i] = 1e-3f;
  48. velocities[i] = vel;
  49. }
  50. }
  51. }
  52. void releaseNote(uint8_t note) {
  53. // Find id
  54. for (int i = 0; i < 16; i++) {
  55. if (learnedNotes[i] == note) {
  56. gates[i] = false;
  57. }
  58. }
  59. }
  60. void step() override {
  61. midi::Message msg;
  62. while (midiInput.shift(&msg)) {
  63. processMessage(msg);
  64. }
  65. float deltaTime = context()->engine->getSampleTime();
  66. for (int i = 0; i < 16; i++) {
  67. if (gateTimes[i] > 0.f) {
  68. outputs[TRIG_OUTPUT + i].value = velocity ? rescale(velocities[i], 0, 127, 0.f, 10.f) : 10.f;
  69. // If the gate is off, wait 1 ms before turning the pulse off.
  70. // This avoids drum controllers sending a pulse with 0 ms duration.
  71. if (!gates[i]) {
  72. gateTimes[i] -= deltaTime;
  73. }
  74. }
  75. else {
  76. outputs[TRIG_OUTPUT + i].value = 0.f;
  77. }
  78. }
  79. }
  80. void processMessage(midi::Message msg) {
  81. switch (msg.status()) {
  82. // note off
  83. case 0x8: {
  84. releaseNote(msg.note());
  85. } break;
  86. // note on
  87. case 0x9: {
  88. if (msg.value() > 0) {
  89. pressNote(msg.note(), msg.value());
  90. }
  91. else {
  92. // Many stupid keyboards send a "note on" command with 0 velocity to mean "note release"
  93. releaseNote(msg.note());
  94. }
  95. } break;
  96. default: break;
  97. }
  98. }
  99. json_t *dataToJson() override {
  100. json_t *rootJ = json_object();
  101. json_t *notesJ = json_array();
  102. for (int i = 0; i < 16; i++) {
  103. json_t *noteJ = json_integer(learnedNotes[i]);
  104. json_array_append_new(notesJ, noteJ);
  105. }
  106. json_object_set_new(rootJ, "notes", notesJ);
  107. json_object_set_new(rootJ, "midi", midiInput.toJson());
  108. json_object_set_new(rootJ, "velocity", json_boolean(velocity));
  109. return rootJ;
  110. }
  111. void dataFromJson(json_t *rootJ) override {
  112. json_t *notesJ = json_object_get(rootJ, "notes");
  113. if (notesJ) {
  114. for (int i = 0; i < 16; i++) {
  115. json_t *noteJ = json_array_get(notesJ, i);
  116. if (noteJ)
  117. learnedNotes[i] = json_integer_value(noteJ);
  118. }
  119. }
  120. json_t *midiJ = json_object_get(rootJ, "midi");
  121. if (midiJ)
  122. midiInput.fromJson(midiJ);
  123. json_t *velocityJ = json_object_get(rootJ, "velocity");
  124. if (velocityJ)
  125. velocity = json_boolean_value(velocityJ);
  126. }
  127. };
  128. struct MidiTrigChoice : GridChoice {
  129. MIDITriggerToCVInterface *module;
  130. int id;
  131. MidiTrigChoice() {
  132. box.size.y = mm2px(6.666);
  133. textOffset.y -= 4;
  134. textOffset.x -= 4;
  135. }
  136. void setId(int id) override {
  137. this->id = id;
  138. }
  139. void step() override {
  140. if (module->learningId == id) {
  141. text = "LRN";
  142. color.a = 0.5;
  143. }
  144. else {
  145. uint8_t note = module->learnedNotes[id];
  146. static const char *noteNames[] = {
  147. "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
  148. };
  149. int oct = note / 12 - 1;
  150. int semi = note % 12;
  151. text = string::f("%s%d", noteNames[semi], oct);
  152. color.a = 1.0;
  153. if (context()->event->selectedWidget == this)
  154. context()->event->selectedWidget = NULL;
  155. }
  156. }
  157. void onSelect(const event::Select &e) override {
  158. e.consume(this);
  159. module->learningId = id;
  160. }
  161. void onDeselect(const event::Deselect &e) override {
  162. module->learningId = -1;
  163. }
  164. };
  165. struct MidiTrigWidget : Grid16MidiWidget {
  166. MIDITriggerToCVInterface *module;
  167. GridChoice *createGridChoice() override {
  168. MidiTrigChoice *gridChoice = new MidiTrigChoice;
  169. gridChoice->module = module;
  170. return gridChoice;
  171. }
  172. };
  173. struct MIDITriggerToCVInterfaceWidget : ModuleWidget {
  174. MIDITriggerToCVInterfaceWidget(MIDITriggerToCVInterface *module) : ModuleWidget(module) {
  175. setPanel(SVG::load(asset::system("res/Core/MIDITriggerToCVInterface.svg")));
  176. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  177. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  178. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  179. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  180. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 0));
  181. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 1));
  182. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 2));
  183. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 73.344704)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 3));
  184. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943355, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 4));
  185. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 5));
  186. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.094982, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 6));
  187. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 84.945023)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 7));
  188. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.8943343, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 8));
  189. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.494659, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 9));
  190. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 10));
  191. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 96.543976)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 11));
  192. addOutput(createOutput<PJ301MPort>(mm2px(Vec(3.894335, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 12));
  193. addOutput(createOutput<PJ301MPort>(mm2px(Vec(15.49466, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 13));
  194. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.09498, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 14));
  195. addOutput(createOutput<PJ301MPort>(mm2px(Vec(38.693932, 108.14429)), module, MIDITriggerToCVInterface::TRIG_OUTPUT + 15));
  196. MidiTrigWidget *midiWidget = createWidget<MidiTrigWidget>(mm2px(Vec(3.399621, 14.837339)));
  197. midiWidget->module = module;
  198. midiWidget->box.size = mm2px(Vec(44, 54.667));
  199. midiWidget->midiIO = &module->midiInput;
  200. midiWidget->createGridChoices();
  201. addChild(midiWidget);
  202. }
  203. void appendContextMenu(Menu *menu) override {
  204. MIDITriggerToCVInterface *module = dynamic_cast<MIDITriggerToCVInterface*>(this->module);
  205. struct VelocityItem : MenuItem {
  206. MIDITriggerToCVInterface *module;
  207. void onAction(const event::Action &e) override {
  208. module->velocity ^= true;
  209. }
  210. };
  211. menu->addChild(new MenuEntry);
  212. VelocityItem *velocityItem = createMenuItem<VelocityItem>("Velocity", CHECKMARK(module->velocity));
  213. velocityItem->module = module;
  214. menu->addChild(velocityItem);
  215. }
  216. };
  217. Model *modelMIDITriggerToCVInterface = createModel<MIDITriggerToCVInterface, MIDITriggerToCVInterfaceWidget>("MIDITriggerToCVInterface");