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.

224 lines
6.2KB

  1. #include "plugin.hpp"
  2. namespace rack {
  3. namespace core {
  4. struct GateMidiOutput : midi::Output {
  5. int vels[128];
  6. bool lastGates[128];
  7. GateMidiOutput() {
  8. reset();
  9. }
  10. void reset() {
  11. for (int note = 0; note < 128; note++) {
  12. vels[note] = 100;
  13. lastGates[note] = false;
  14. }
  15. }
  16. void panic() {
  17. reset();
  18. // Send all note off commands
  19. for (int note = 0; note < 128; note++) {
  20. // Note off
  21. midi::Message m;
  22. m.setStatus(0x8);
  23. m.setNote(note);
  24. m.setValue(0);
  25. sendMessage(m);
  26. }
  27. }
  28. void setVelocity(int vel, int note) {
  29. vels[note] = vel;
  30. }
  31. void setGate(bool gate, int note) {
  32. if (gate && !lastGates[note]) {
  33. // Note on
  34. midi::Message m;
  35. m.setStatus(0x9);
  36. m.setNote(note);
  37. m.setValue(vels[note]);
  38. sendMessage(m);
  39. }
  40. else if (!gate && lastGates[note]) {
  41. // Note off
  42. midi::Message m;
  43. m.setStatus(0x8);
  44. m.setNote(note);
  45. m.setValue(vels[note]);
  46. sendMessage(m);
  47. }
  48. lastGates[note] = gate;
  49. }
  50. };
  51. struct CV_Gate : Module {
  52. enum ParamIds {
  53. NUM_PARAMS
  54. };
  55. enum InputIds {
  56. ENUMS(GATE_INPUTS, 16),
  57. NUM_INPUTS
  58. };
  59. enum OutputIds {
  60. NUM_OUTPUTS
  61. };
  62. enum LightIds {
  63. NUM_LIGHTS
  64. };
  65. GateMidiOutput midiOutput;
  66. bool velocityMode = false;
  67. int learningId = -1;
  68. uint8_t learnedNotes[16] = {};
  69. CV_Gate() {
  70. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  71. onReset();
  72. }
  73. void onReset() override {
  74. for (int y = 0; y < 4; y++) {
  75. for (int x = 0; x < 4; x++) {
  76. learnedNotes[4 * y + x] = 36 + 4 * (3 - y) + x;
  77. }
  78. }
  79. learningId = -1;
  80. midiOutput.reset();
  81. midiOutput.midi::Output::reset();
  82. }
  83. void process(const ProcessArgs& args) override {
  84. for (int i = 0; i < 16; i++) {
  85. int note = learnedNotes[i];
  86. if (velocityMode) {
  87. int vel = (int) std::round(inputs[GATE_INPUTS + i].getVoltage() / 10.f * 127);
  88. vel = clamp(vel, 0, 127);
  89. midiOutput.setVelocity(vel, note);
  90. midiOutput.setGate(vel > 0, note);
  91. }
  92. else {
  93. bool gate = inputs[GATE_INPUTS + i].getVoltage() >= 1.f;
  94. midiOutput.setVelocity(100, note);
  95. midiOutput.setGate(gate, note);
  96. }
  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, "velocity", json_boolean(velocityMode));
  108. json_object_set_new(rootJ, "midi", midiOutput.toJson());
  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* velocityJ = json_object_get(rootJ, "velocity");
  121. if (velocityJ)
  122. velocityMode = json_boolean_value(velocityJ);
  123. json_t* midiJ = json_object_get(rootJ, "midi");
  124. if (midiJ)
  125. midiOutput.fromJson(midiJ);
  126. }
  127. };
  128. struct CV_GateVelocityItem : MenuItem {
  129. CV_Gate* module;
  130. void onAction(const event::Action& e) override {
  131. module->velocityMode ^= true;
  132. }
  133. };
  134. struct CV_GatePanicItem : MenuItem {
  135. CV_Gate* module;
  136. void onAction(const event::Action& e) override {
  137. module->midiOutput.panic();
  138. }
  139. };
  140. struct CV_GateWidget : ModuleWidget {
  141. CV_GateWidget(CV_Gate* module) {
  142. setModule(module);
  143. setPanel(APP->window->loadSvg(asset::system("res/Core/CV-Gate.svg")));
  144. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  145. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  146. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  147. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  148. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 77)), module, CV_Gate::GATE_INPUTS + 0));
  149. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 77)), module, CV_Gate::GATE_INPUTS + 1));
  150. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 77)), module, CV_Gate::GATE_INPUTS + 2));
  151. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 77)), module, CV_Gate::GATE_INPUTS + 3));
  152. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 89)), module, CV_Gate::GATE_INPUTS + 4));
  153. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 89)), module, CV_Gate::GATE_INPUTS + 5));
  154. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 89)), module, CV_Gate::GATE_INPUTS + 6));
  155. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 89)), module, CV_Gate::GATE_INPUTS + 7));
  156. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 101)), module, CV_Gate::GATE_INPUTS + 8));
  157. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 101)), module, CV_Gate::GATE_INPUTS + 9));
  158. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 101)), module, CV_Gate::GATE_INPUTS + 10));
  159. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 101)), module, CV_Gate::GATE_INPUTS + 11));
  160. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 112)), module, CV_Gate::GATE_INPUTS + 12));
  161. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 112)), module, CV_Gate::GATE_INPUTS + 13));
  162. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 112)), module, CV_Gate::GATE_INPUTS + 14));
  163. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 112)), module, CV_Gate::GATE_INPUTS + 15));
  164. typedef Grid16MidiWidget<NoteChoice<CV_Gate>> TMidiWidget;
  165. TMidiWidget* midiWidget = createWidget<TMidiWidget>(mm2px(Vec(3.399621, 14.837339)));
  166. midiWidget->box.size = mm2px(Vec(44, 54.667));
  167. midiWidget->setMidiPort(module ? &module->midiOutput : NULL);
  168. midiWidget->setModule(module);
  169. addChild(midiWidget);
  170. }
  171. void appendContextMenu(Menu* menu) override {
  172. CV_Gate* module = dynamic_cast<CV_Gate*>(this->module);
  173. menu->addChild(new MenuEntry);
  174. CV_GateVelocityItem* velocityItem = createMenuItem<CV_GateVelocityItem>("Velocity mode", CHECKMARK(module->velocityMode));
  175. velocityItem->module = module;
  176. menu->addChild(velocityItem);
  177. CV_GatePanicItem* panicItem = new CV_GatePanicItem;
  178. panicItem->text = "Panic";
  179. panicItem->module = module;
  180. menu->addChild(panicItem);
  181. }
  182. };
  183. Model* modelCV_Gate = createModel<CV_Gate, CV_GateWidget>("CV-Gate");
  184. } // namespace core
  185. } // namespace rack