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.

234 lines
6.6KB

  1. #include "plugin.hpp"
  2. namespace rack {
  3. namespace core {
  4. struct GateMidiOutput : midi::Output {
  5. uint8_t vels[128];
  6. bool lastGates[128];
  7. int64_t frame = -1;
  8. GateMidiOutput() {
  9. reset();
  10. }
  11. void reset() {
  12. for (uint8_t note = 0; note < 128; note++) {
  13. vels[note] = 100;
  14. lastGates[note] = false;
  15. }
  16. Output::reset();
  17. }
  18. void panic() {
  19. // Send all note off commands
  20. for (uint8_t note = 0; note < 128; note++) {
  21. // Note off
  22. midi::Message m;
  23. m.setStatus(0x8);
  24. m.setNote(note);
  25. m.setValue(0);
  26. m.setFrame(frame);
  27. sendMessage(m);
  28. lastGates[note] = false;
  29. }
  30. }
  31. void setVelocity(uint8_t note, uint8_t vel) {
  32. vels[note] = vel;
  33. }
  34. void setGate(uint8_t note, bool gate) {
  35. if (gate && !lastGates[note]) {
  36. // Note on
  37. midi::Message m;
  38. m.setStatus(0x9);
  39. m.setNote(note);
  40. m.setValue(vels[note]);
  41. m.setFrame(frame);
  42. sendMessage(m);
  43. }
  44. else if (!gate && lastGates[note]) {
  45. // Note off
  46. midi::Message m;
  47. m.setStatus(0x8);
  48. m.setNote(note);
  49. m.setValue(vels[note]);
  50. m.setFrame(frame);
  51. sendMessage(m);
  52. }
  53. lastGates[note] = gate;
  54. }
  55. void setFrame(int64_t frame) {
  56. this->frame = frame;
  57. }
  58. };
  59. struct Gate_MIDI : Module {
  60. enum ParamIds {
  61. NUM_PARAMS
  62. };
  63. enum InputIds {
  64. ENUMS(GATE_INPUTS, 16),
  65. NUM_INPUTS
  66. };
  67. enum OutputIds {
  68. NUM_OUTPUTS
  69. };
  70. enum LightIds {
  71. NUM_LIGHTS
  72. };
  73. GateMidiOutput midiOutput;
  74. bool velocityMode = false;
  75. int learningId = -1;
  76. int8_t learnedNotes[16] = {};
  77. dsp::SchmittTrigger cellTriggers[16];
  78. Gate_MIDI() {
  79. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  80. for (int id = 0; id < 16; id++)
  81. configInput(GATE_INPUTS + id, string::f("Cell %d", id + 1));
  82. onReset();
  83. }
  84. void onReset() override {
  85. for (int y = 0; y < 4; y++) {
  86. for (int x = 0; x < 4; x++) {
  87. learnedNotes[4 * y + x] = 36 + 4 * (3 - y) + x;
  88. }
  89. }
  90. learningId = -1;
  91. midiOutput.reset();
  92. midiOutput.midi::Output::reset();
  93. }
  94. void process(const ProcessArgs& args) override {
  95. midiOutput.setFrame(args.frame);
  96. for (int id = 0; id < 16; id++) {
  97. int8_t note = learnedNotes[id];
  98. if (note < 0)
  99. continue;
  100. if (velocityMode) {
  101. int8_t vel = (int8_t) clamp(std::round(inputs[GATE_INPUTS + id].getVoltage() / 10.f * 127), 0.f, 127.f);
  102. vel = clamp(vel, 0, 127);
  103. midiOutput.setVelocity(note, vel);
  104. midiOutput.setGate(note, vel > 0);
  105. }
  106. else {
  107. cellTriggers[id].process(inputs[GATE_INPUTS + id].getVoltage(), 0.1f, 2.f);
  108. midiOutput.setVelocity(note, 100);
  109. midiOutput.setGate(note, cellTriggers[id].isHigh());
  110. }
  111. }
  112. }
  113. void setLearnedNote(int id, int8_t note) {
  114. // Unset IDs of similar note
  115. if (note >= 0) {
  116. for (int id = 0; id < 16; id++) {
  117. if (learnedNotes[id] == note)
  118. learnedNotes[id] = -1;
  119. }
  120. }
  121. learnedNotes[id] = note;
  122. }
  123. json_t* dataToJson() override {
  124. json_t* rootJ = json_object();
  125. json_t* notesJ = json_array();
  126. for (int id = 0; id < 16; id++) {
  127. json_t* noteJ = json_integer(learnedNotes[id]);
  128. json_array_append_new(notesJ, noteJ);
  129. }
  130. json_object_set_new(rootJ, "notes", notesJ);
  131. json_object_set_new(rootJ, "velocity", json_boolean(velocityMode));
  132. json_object_set_new(rootJ, "midi", midiOutput.toJson());
  133. return rootJ;
  134. }
  135. void dataFromJson(json_t* rootJ) override {
  136. json_t* notesJ = json_object_get(rootJ, "notes");
  137. if (notesJ) {
  138. for (int id = 0; id < 16; id++) {
  139. json_t* noteJ = json_array_get(notesJ, id);
  140. if (noteJ)
  141. setLearnedNote(id, json_integer_value(noteJ));
  142. }
  143. }
  144. json_t* velocityJ = json_object_get(rootJ, "velocity");
  145. if (velocityJ)
  146. velocityMode = json_boolean_value(velocityJ);
  147. json_t* midiJ = json_object_get(rootJ, "midi");
  148. if (midiJ)
  149. midiOutput.fromJson(midiJ);
  150. }
  151. };
  152. struct Gate_MIDIWidget : ModuleWidget {
  153. Gate_MIDIWidget(Gate_MIDI* module) {
  154. setModule(module);
  155. setPanel(Svg::load(asset::system("res/Core/Gate_MIDI.svg")));
  156. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  157. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  158. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  159. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  160. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.189, 78.431)), module, Gate_MIDI::GATE_INPUTS + 0));
  161. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.739, 78.431)), module, Gate_MIDI::GATE_INPUTS + 1));
  162. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31.289, 78.431)), module, Gate_MIDI::GATE_INPUTS + 2));
  163. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.838, 78.431)), module, Gate_MIDI::GATE_INPUTS + 3));
  164. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.189, 89.946)), module, Gate_MIDI::GATE_INPUTS + 4));
  165. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.739, 89.946)), module, Gate_MIDI::GATE_INPUTS + 5));
  166. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31.289, 89.946)), module, Gate_MIDI::GATE_INPUTS + 6));
  167. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.838, 89.946)), module, Gate_MIDI::GATE_INPUTS + 7));
  168. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.189, 101.466)), module, Gate_MIDI::GATE_INPUTS + 8));
  169. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.739, 101.466)), module, Gate_MIDI::GATE_INPUTS + 9));
  170. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31.289, 101.466)), module, Gate_MIDI::GATE_INPUTS + 10));
  171. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.838, 101.466)), module, Gate_MIDI::GATE_INPUTS + 11));
  172. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8.189, 112.998)), module, Gate_MIDI::GATE_INPUTS + 12));
  173. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(19.739, 112.984)), module, Gate_MIDI::GATE_INPUTS + 13));
  174. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31.289, 112.984)), module, Gate_MIDI::GATE_INPUTS + 14));
  175. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(42.838, 112.984)), module, Gate_MIDI::GATE_INPUTS + 15));
  176. typedef Grid16MidiDisplay<NoteChoice<Gate_MIDI>> TMidiDisplay;
  177. TMidiDisplay* display = createWidget<TMidiDisplay>(mm2px(Vec(0.0, 13.039)));
  178. display->box.size = mm2px(Vec(50.8, 55.88));
  179. display->setMidiPort(module ? &module->midiOutput : NULL);
  180. display->setModule(module);
  181. addChild(display);
  182. }
  183. void appendContextMenu(Menu* menu) override {
  184. Gate_MIDI* module = dynamic_cast<Gate_MIDI*>(this->module);
  185. menu->addChild(new MenuSeparator);
  186. menu->addChild(createBoolPtrMenuItem("Velocity mode", "", &module->velocityMode));
  187. menu->addChild(createMenuItem("Panic", "",
  188. [=]() {module->midiOutput.panic();}
  189. ));
  190. }
  191. };
  192. Model* modelGate_MIDI = createModel<Gate_MIDI, Gate_MIDIWidget>("CV-Gate");
  193. } // namespace core
  194. } // namespace rack