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.

172 lines
4.1KB

  1. #include "plugin.hpp"
  2. struct Octave : Module {
  3. enum ParamIds {
  4. NUM_PARAMS
  5. };
  6. enum InputIds {
  7. CV_INPUT,
  8. NUM_INPUTS
  9. };
  10. enum OutputIds {
  11. CV_OUTPUT,
  12. NUM_OUTPUTS
  13. };
  14. enum LightIds {
  15. NUM_LIGHTS
  16. };
  17. int octave = 0;
  18. Octave() {
  19. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  20. }
  21. void onReset() override {
  22. octave = 0;
  23. }
  24. void onRandomize() override {
  25. octave = (random::u32() % 9) - 4;
  26. }
  27. void process(const ProcessArgs &args) override {
  28. int channels = std::max(inputs[CV_INPUT].getChannels(), 1);
  29. for (int c = 0; c < channels; c++) {
  30. float cv = inputs[CV_INPUT].getVoltage(c);
  31. cv += octave;
  32. outputs[CV_OUTPUT].setVoltage(cv, c);
  33. }
  34. outputs[CV_OUTPUT].setChannels(channels);
  35. }
  36. json_t *dataToJson() override {
  37. json_t *rootJ = json_object();
  38. json_object_set_new(rootJ, "octave", json_integer(octave));
  39. return rootJ;
  40. }
  41. void dataFromJson(json_t *rootJ) override {
  42. json_t *octaveJ = json_object_get(rootJ, "octave");
  43. if (octaveJ)
  44. octave = json_integer_value(octaveJ);
  45. }
  46. };
  47. struct OctaveButton : OpaqueWidget {
  48. Octave *module;
  49. int octave;
  50. void draw(const DrawArgs &args) override {
  51. Vec c = box.size.div(2);
  52. if ((module && module->octave == octave) || octave == 0) {
  53. // Enabled
  54. nvgBeginPath(args.vg);
  55. nvgCircle(args.vg, c.x, c.y, mm2px(4.0/2));
  56. if (octave < 0)
  57. nvgFillColor(args.vg, color::RED);
  58. else if (octave > 0)
  59. nvgFillColor(args.vg, color::GREEN);
  60. else
  61. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  62. nvgFill(args.vg);
  63. }
  64. else {
  65. // Disabled
  66. nvgBeginPath(args.vg);
  67. nvgCircle(args.vg, c.x, c.y, mm2px(4.0/2));
  68. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  69. nvgFill(args.vg);
  70. nvgBeginPath(args.vg);
  71. nvgCircle(args.vg, c.x, c.y, mm2px(3.0/2));
  72. nvgFillColor(args.vg, color::BLACK);
  73. nvgFill(args.vg);
  74. if (octave == 0) {
  75. nvgBeginPath(args.vg);
  76. nvgCircle(args.vg, c.x, c.y, mm2px(1.0/2));
  77. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  78. nvgFill(args.vg);
  79. }
  80. }
  81. }
  82. void onButton(const event::Button &e) override {
  83. if (e.button == GLFW_MOUSE_BUTTON_RIGHT && e.action == GLFW_PRESS) {
  84. module->octave = 0;
  85. e.consume(this);
  86. return;
  87. }
  88. OpaqueWidget::onButton(e);
  89. }
  90. void onDragEnter(const event::DragEnter &e) override {
  91. OctaveButton *w = dynamic_cast<OctaveButton*>(e.origin);
  92. if (w) {
  93. module->octave = octave;
  94. }
  95. }
  96. };
  97. struct OctaveDisplay : OpaqueWidget {
  98. OctaveDisplay() {
  99. box.size = mm2px(Vec(15.240, 72.000));
  100. }
  101. void setModule(Octave *module) {
  102. clearChildren();
  103. const int octaves = 9;
  104. const float margin = mm2px(2.0);
  105. float height = box.size.y - 2*margin;
  106. for (int i = 0; i < octaves; i++) {
  107. OctaveButton *octaveButton = new OctaveButton();
  108. octaveButton->box.pos = Vec(0, height / octaves * i + margin);
  109. octaveButton->box.size = Vec(box.size.x, height / octaves);
  110. octaveButton->module = module;
  111. octaveButton->octave = 4 - i;
  112. addChild(octaveButton);
  113. }
  114. }
  115. void draw(const DrawArgs &args) override {
  116. // Background
  117. nvgBeginPath(args.vg);
  118. nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
  119. nvgFillColor(args.vg, nvgRGB(0, 0, 0));
  120. nvgFill(args.vg);
  121. Widget::draw(args);
  122. }
  123. };
  124. struct OctaveWidget : ModuleWidget {
  125. OctaveWidget(Octave *module) {
  126. setModule(module);
  127. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Octave.svg")));
  128. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  129. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  130. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  131. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  132. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.62, 97.253)), module, Octave::CV_INPUT));
  133. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.62, 112.253)), module, Octave::CV_OUTPUT));
  134. OctaveDisplay *octaveDisplay = new OctaveDisplay();
  135. octaveDisplay->box.pos = mm2px(Vec(0.0, 14.584));
  136. octaveDisplay->setModule(module);
  137. addChild(octaveDisplay);
  138. }
  139. };
  140. Model *modelOctave = createModel<Octave, OctaveWidget>("Octave");