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.

226 lines
6.3KB

  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. for (int i = 0; i < 16; i++)
  72. configInput(GATE_INPUTS + i, string::f("Cell %d", i + 1));
  73. onReset();
  74. }
  75. void onReset() override {
  76. for (int y = 0; y < 4; y++) {
  77. for (int x = 0; x < 4; x++) {
  78. learnedNotes[4 * y + x] = 36 + 4 * (3 - y) + x;
  79. }
  80. }
  81. learningId = -1;
  82. midiOutput.reset();
  83. midiOutput.midi::Output::reset();
  84. }
  85. void process(const ProcessArgs& args) override {
  86. for (int i = 0; i < 16; i++) {
  87. int note = learnedNotes[i];
  88. if (velocityMode) {
  89. int vel = (int) std::round(inputs[GATE_INPUTS + i].getVoltage() / 10.f * 127);
  90. vel = clamp(vel, 0, 127);
  91. midiOutput.setVelocity(vel, note);
  92. midiOutput.setGate(vel > 0, note);
  93. }
  94. else {
  95. bool gate = inputs[GATE_INPUTS + i].getVoltage() >= 1.f;
  96. midiOutput.setVelocity(100, note);
  97. midiOutput.setGate(gate, note);
  98. }
  99. }
  100. }
  101. json_t* dataToJson() override {
  102. json_t* rootJ = json_object();
  103. json_t* notesJ = json_array();
  104. for (int i = 0; i < 16; i++) {
  105. json_t* noteJ = json_integer(learnedNotes[i]);
  106. json_array_append_new(notesJ, noteJ);
  107. }
  108. json_object_set_new(rootJ, "notes", notesJ);
  109. json_object_set_new(rootJ, "velocity", json_boolean(velocityMode));
  110. json_object_set_new(rootJ, "midi", midiOutput.toJson());
  111. return rootJ;
  112. }
  113. void dataFromJson(json_t* rootJ) override {
  114. json_t* notesJ = json_object_get(rootJ, "notes");
  115. if (notesJ) {
  116. for (int i = 0; i < 16; i++) {
  117. json_t* noteJ = json_array_get(notesJ, i);
  118. if (noteJ)
  119. learnedNotes[i] = json_integer_value(noteJ);
  120. }
  121. }
  122. json_t* velocityJ = json_object_get(rootJ, "velocity");
  123. if (velocityJ)
  124. velocityMode = json_boolean_value(velocityJ);
  125. json_t* midiJ = json_object_get(rootJ, "midi");
  126. if (midiJ)
  127. midiOutput.fromJson(midiJ);
  128. }
  129. };
  130. struct CV_GateVelocityItem : MenuItem {
  131. CV_Gate* module;
  132. void onAction(const event::Action& e) override {
  133. module->velocityMode ^= true;
  134. }
  135. };
  136. struct CV_GatePanicItem : MenuItem {
  137. CV_Gate* module;
  138. void onAction(const event::Action& e) override {
  139. module->midiOutput.panic();
  140. }
  141. };
  142. struct CV_GateWidget : ModuleWidget {
  143. CV_GateWidget(CV_Gate* module) {
  144. setModule(module);
  145. setPanel(APP->window->loadSvg(asset::system("res/Core/CV-Gate.svg")));
  146. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  147. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  148. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  149. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  150. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 77)), module, CV_Gate::GATE_INPUTS + 0));
  151. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 77)), module, CV_Gate::GATE_INPUTS + 1));
  152. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 77)), module, CV_Gate::GATE_INPUTS + 2));
  153. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 77)), module, CV_Gate::GATE_INPUTS + 3));
  154. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 89)), module, CV_Gate::GATE_INPUTS + 4));
  155. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 89)), module, CV_Gate::GATE_INPUTS + 5));
  156. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 89)), module, CV_Gate::GATE_INPUTS + 6));
  157. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 89)), module, CV_Gate::GATE_INPUTS + 7));
  158. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 101)), module, CV_Gate::GATE_INPUTS + 8));
  159. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 101)), module, CV_Gate::GATE_INPUTS + 9));
  160. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 101)), module, CV_Gate::GATE_INPUTS + 10));
  161. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 101)), module, CV_Gate::GATE_INPUTS + 11));
  162. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 112)), module, CV_Gate::GATE_INPUTS + 12));
  163. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 112)), module, CV_Gate::GATE_INPUTS + 13));
  164. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 112)), module, CV_Gate::GATE_INPUTS + 14));
  165. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 112)), module, CV_Gate::GATE_INPUTS + 15));
  166. typedef Grid16MidiWidget<NoteChoice<CV_Gate>> TMidiWidget;
  167. TMidiWidget* midiWidget = createWidget<TMidiWidget>(mm2px(Vec(3.399621, 14.837339)));
  168. midiWidget->box.size = mm2px(Vec(44, 54.667));
  169. midiWidget->setMidiPort(module ? &module->midiOutput : NULL);
  170. midiWidget->setModule(module);
  171. addChild(midiWidget);
  172. }
  173. void appendContextMenu(Menu* menu) override {
  174. CV_Gate* module = dynamic_cast<CV_Gate*>(this->module);
  175. menu->addChild(new MenuEntry);
  176. CV_GateVelocityItem* velocityItem = createMenuItem<CV_GateVelocityItem>("Velocity mode", CHECKMARK(module->velocityMode));
  177. velocityItem->module = module;
  178. menu->addChild(velocityItem);
  179. CV_GatePanicItem* panicItem = new CV_GatePanicItem;
  180. panicItem->text = "Panic";
  181. panicItem->module = module;
  182. menu->addChild(panicItem);
  183. }
  184. };
  185. Model* modelCV_Gate = createModel<CV_Gate, CV_GateWidget>("CV-Gate");
  186. } // namespace core
  187. } // namespace rack