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.

158 lines
4.5KB

  1. #include "plugin.hpp"
  2. namespace rack {
  3. namespace core {
  4. struct CCMidiOutput : midi::Output {
  5. int lastValues[128];
  6. CCMidiOutput() {
  7. reset();
  8. }
  9. void reset() {
  10. for (int n = 0; n < 128; n++) {
  11. lastValues[n] = -1;
  12. }
  13. }
  14. void setValue(int value, int cc) {
  15. if (value == lastValues[cc])
  16. return;
  17. lastValues[cc] = value;
  18. // CC
  19. midi::Message m;
  20. m.setStatus(0xb);
  21. m.setNote(cc);
  22. m.setValue(value);
  23. sendMessage(m);
  24. }
  25. };
  26. struct CV_CC : Module {
  27. enum ParamIds {
  28. NUM_PARAMS
  29. };
  30. enum InputIds {
  31. ENUMS(CC_INPUTS, 16),
  32. NUM_INPUTS
  33. };
  34. enum OutputIds {
  35. NUM_OUTPUTS
  36. };
  37. enum LightIds {
  38. NUM_LIGHTS
  39. };
  40. CCMidiOutput midiOutput;
  41. float rateLimiterPhase = 0.f;
  42. int learningId = -1;
  43. int learnedCcs[16] = {};
  44. CV_CC() {
  45. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  46. onReset();
  47. }
  48. void onReset() override {
  49. for (int i = 0; i < 16; i++) {
  50. learnedCcs[i] = i;
  51. }
  52. learningId = -1;
  53. midiOutput.reset();
  54. midiOutput.midi::Output::reset();
  55. }
  56. void process(const ProcessArgs& args) override {
  57. const float rateLimiterPeriod = 0.010f;
  58. rateLimiterPhase += args.sampleTime / rateLimiterPeriod;
  59. if (rateLimiterPhase >= 1.f) {
  60. rateLimiterPhase -= 1.f;
  61. }
  62. else {
  63. return;
  64. }
  65. for (int i = 0; i < 16; i++) {
  66. int value = (int) std::round(inputs[CC_INPUTS + i].getVoltage() / 10.f * 127);
  67. value = clamp(value, 0, 127);
  68. midiOutput.setValue(value, learnedCcs[i]);
  69. }
  70. }
  71. json_t* dataToJson() override {
  72. json_t* rootJ = json_object();
  73. json_t* ccsJ = json_array();
  74. for (int i = 0; i < 16; i++) {
  75. json_array_append_new(ccsJ, json_integer(learnedCcs[i]));
  76. }
  77. json_object_set_new(rootJ, "ccs", ccsJ);
  78. json_object_set_new(rootJ, "midi", midiOutput.toJson());
  79. return rootJ;
  80. }
  81. void dataFromJson(json_t* rootJ) override {
  82. json_t* ccsJ = json_object_get(rootJ, "ccs");
  83. if (ccsJ) {
  84. for (int i = 0; i < 16; i++) {
  85. json_t* ccJ = json_array_get(ccsJ, i);
  86. if (ccJ)
  87. learnedCcs[i] = json_integer_value(ccJ);
  88. }
  89. }
  90. json_t* midiJ = json_object_get(rootJ, "midi");
  91. if (midiJ)
  92. midiOutput.fromJson(midiJ);
  93. }
  94. };
  95. struct CV_CCWidget : ModuleWidget {
  96. CV_CCWidget(CV_CC* module) {
  97. setModule(module);
  98. setPanel(APP->window->loadSvg(asset::system("res/Core/CV-CC.svg")));
  99. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  100. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  101. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  102. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  103. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 77)), module, CV_CC::CC_INPUTS + 0));
  104. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 77)), module, CV_CC::CC_INPUTS + 1));
  105. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 77)), module, CV_CC::CC_INPUTS + 2));
  106. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 77)), module, CV_CC::CC_INPUTS + 3));
  107. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 89)), module, CV_CC::CC_INPUTS + 4));
  108. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 89)), module, CV_CC::CC_INPUTS + 5));
  109. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 89)), module, CV_CC::CC_INPUTS + 6));
  110. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 89)), module, CV_CC::CC_INPUTS + 7));
  111. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 101)), module, CV_CC::CC_INPUTS + 8));
  112. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 101)), module, CV_CC::CC_INPUTS + 9));
  113. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 101)), module, CV_CC::CC_INPUTS + 10));
  114. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 101)), module, CV_CC::CC_INPUTS + 11));
  115. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(8, 112)), module, CV_CC::CC_INPUTS + 12));
  116. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(20, 112)), module, CV_CC::CC_INPUTS + 13));
  117. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(31, 112)), module, CV_CC::CC_INPUTS + 14));
  118. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(43, 112)), module, CV_CC::CC_INPUTS + 15));
  119. typedef Grid16MidiWidget<CcChoice<CV_CC>> TMidiWidget;
  120. TMidiWidget* midiWidget = createWidget<TMidiWidget>(mm2px(Vec(3.399621, 14.837339)));
  121. midiWidget->box.size = mm2px(Vec(44, 54.667));
  122. midiWidget->setMidiPort(module ? &module->midiOutput : NULL);
  123. midiWidget->setModule(module);
  124. addChild(midiWidget);
  125. }
  126. };
  127. Model* modelCV_CC = createModel<CV_CC, CV_CCWidget>("CV-CC");
  128. } // namespace core
  129. } // namespace rack