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.

167 lines
4.8KB

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