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.

181 lines
5.2KB

  1. #include "plugin.hpp"
  2. namespace rack {
  3. namespace core {
  4. struct MidiOutput : dsp::MidiGenerator<PORT_MAX_CHANNELS>, midi::Output {
  5. void onMessage(midi::Message message) override {
  6. midi::Output::sendMessage(message);
  7. }
  8. void reset() {
  9. Output::reset();
  10. MidiGenerator::reset();
  11. }
  12. };
  13. struct CV_MIDI : Module {
  14. enum ParamIds {
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. PITCH_INPUT,
  19. GATE_INPUT,
  20. VEL_INPUT,
  21. AFT_INPUT,
  22. PW_INPUT,
  23. MW_INPUT,
  24. CLK_INPUT,
  25. VOL_INPUT,
  26. PAN_INPUT,
  27. START_INPUT,
  28. STOP_INPUT,
  29. CONTINUE_INPUT,
  30. NUM_INPUTS
  31. };
  32. enum OutputIds {
  33. NUM_OUTPUTS
  34. };
  35. enum LightIds {
  36. NUM_LIGHTS
  37. };
  38. MidiOutput midiOutput;
  39. float rateLimiterPhase = 0.f;
  40. CV_MIDI() {
  41. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  42. onReset();
  43. }
  44. void onReset() override {
  45. midiOutput.reset();
  46. }
  47. void process(const ProcessArgs& args) override {
  48. const float rateLimiterPeriod = 0.005f;
  49. rateLimiterPhase += args.sampleTime / rateLimiterPeriod;
  50. if (rateLimiterPhase >= 1.f) {
  51. rateLimiterPhase -= 1.f;
  52. }
  53. else {
  54. return;
  55. }
  56. for (int c = 0; c < inputs[PITCH_INPUT].getChannels(); c++) {
  57. int vel = (int) std::round(inputs[VEL_INPUT].getNormalPolyVoltage(10.f * 100 / 127, c) / 10.f * 127);
  58. vel = clamp(vel, 0, 127);
  59. midiOutput.setVelocity(vel, c);
  60. int note = (int) std::round(inputs[PITCH_INPUT].getVoltage(c) * 12.f + 60.f);
  61. note = clamp(note, 0, 127);
  62. bool gate = inputs[GATE_INPUT].getPolyVoltage(c) >= 1.f;
  63. midiOutput.setNoteGate(note, gate, c);
  64. int aft = (int) std::round(inputs[AFT_INPUT].getPolyVoltage(c) / 10.f * 127);
  65. aft = clamp(aft, 0, 127);
  66. midiOutput.setKeyPressure(aft, c);
  67. }
  68. int pw = (int) std::round((inputs[PW_INPUT].getVoltage() + 5.f) / 10.f * 0x4000);
  69. pw = clamp(pw, 0, 0x3fff);
  70. midiOutput.setPitchWheel(pw);
  71. int mw = (int) std::round(inputs[MW_INPUT].getVoltage() / 10.f * 127);
  72. mw = clamp(mw, 0, 127);
  73. midiOutput.setModWheel(mw);
  74. int vol = (int) std::round(inputs[VOL_INPUT].getNormalVoltage(10.f) / 10.f * 127);
  75. vol = clamp(vol, 0, 127);
  76. midiOutput.setVolume(vol);
  77. int pan = (int) std::round((inputs[PAN_INPUT].getVoltage() + 5.f) / 10.f * 127);
  78. pan = clamp(pan, 0, 127);
  79. midiOutput.setPan(pan);
  80. bool clk = inputs[CLK_INPUT].getVoltage() >= 1.f;
  81. midiOutput.setClock(clk);
  82. bool start = inputs[START_INPUT].getVoltage() >= 1.f;
  83. midiOutput.setStart(start);
  84. bool stop = inputs[STOP_INPUT].getVoltage() >= 1.f;
  85. midiOutput.setStop(stop);
  86. bool cont = inputs[CONTINUE_INPUT].getVoltage() >= 1.f;
  87. midiOutput.setContinue(cont);
  88. }
  89. json_t* dataToJson() override {
  90. json_t* rootJ = json_object();
  91. json_object_set_new(rootJ, "midi", midiOutput.toJson());
  92. return rootJ;
  93. }
  94. void dataFromJson(json_t* rootJ) override {
  95. json_t* midiJ = json_object_get(rootJ, "midi");
  96. if (midiJ)
  97. midiOutput.fromJson(midiJ);
  98. }
  99. };
  100. struct CV_MIDIPanicItem : MenuItem {
  101. CV_MIDI* module;
  102. void onAction(const event::Action& e) override {
  103. module->midiOutput.panic();
  104. }
  105. };
  106. struct CV_MIDIWidget : ModuleWidget {
  107. CV_MIDIWidget(CV_MIDI* module) {
  108. setModule(module);
  109. setPanel(APP->window->loadSvg(asset::system("res/Core/CV-MIDI.svg")));
  110. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  111. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  112. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  113. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  114. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9, 64)), module, CV_MIDI::PITCH_INPUT));
  115. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 64)), module, CV_MIDI::GATE_INPUT));
  116. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(32, 64)), module, CV_MIDI::VEL_INPUT));
  117. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9, 80)), module, CV_MIDI::AFT_INPUT));
  118. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 80)), module, CV_MIDI::PW_INPUT));
  119. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(32, 80)), module, CV_MIDI::MW_INPUT));
  120. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9, 96)), module, CV_MIDI::CLK_INPUT));
  121. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 96)), module, CV_MIDI::VOL_INPUT));
  122. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(32, 96)), module, CV_MIDI::PAN_INPUT));
  123. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(9, 112)), module, CV_MIDI::START_INPUT));
  124. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 112)), module, CV_MIDI::STOP_INPUT));
  125. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(32, 112)), module, CV_MIDI::CONTINUE_INPUT));
  126. MidiWidget* midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
  127. midiWidget->box.size = mm2px(Vec(33.840, 28));
  128. midiWidget->setMidiPort(module ? &module->midiOutput : NULL);
  129. addChild(midiWidget);
  130. }
  131. void appendContextMenu(Menu* menu) override {
  132. CV_MIDI* module = dynamic_cast<CV_MIDI*>(this->module);
  133. menu->addChild(new MenuEntry);
  134. CV_MIDIPanicItem* panicItem = new CV_MIDIPanicItem;
  135. panicItem->text = "Panic";
  136. panicItem->module = module;
  137. menu->addChild(panicItem);
  138. }
  139. };
  140. Model* modelCV_MIDI = createModel<CV_MIDI, CV_MIDIWidget>("CV-MIDI");
  141. } // namespace core
  142. } // namespace rack