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.

168 lines
4.3KB

  1. #include "plugin.hpp"
  2. struct Octave : Module {
  3. enum ParamIds {
  4. OCTAVE_PARAM,
  5. NUM_PARAMS
  6. };
  7. enum InputIds {
  8. PITCH_INPUT,
  9. OCTAVE_INPUT,
  10. NUM_INPUTS
  11. };
  12. enum OutputIds {
  13. PITCH_OUTPUT,
  14. NUM_OUTPUTS
  15. };
  16. enum LightIds {
  17. NUM_LIGHTS
  18. };
  19. Octave() {
  20. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  21. configParam(OCTAVE_PARAM, -4.f, 4.f, 0.f, "Octave shift");
  22. }
  23. void process(const ProcessArgs &args) override {
  24. int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
  25. float octaveParam = params[OCTAVE_PARAM].getValue();
  26. for (int c = 0; c < channels; c++) {
  27. float octave = octaveParam + inputs[OCTAVE_INPUT].getPolyVoltage(c);
  28. octave = std::round(octave);
  29. float pitch = inputs[PITCH_INPUT].getVoltage(c);
  30. pitch += octave;
  31. outputs[PITCH_OUTPUT].setVoltage(pitch, c);
  32. }
  33. outputs[PITCH_OUTPUT].setChannels(channels);
  34. }
  35. void dataFromJson(json_t *rootJ) override {
  36. // In Fundamental 1.1.1 and earlier, the octave param was internal data.
  37. json_t *octaveJ = json_object_get(rootJ, "octave");
  38. if (octaveJ) {
  39. params[OCTAVE_PARAM].setValue(json_integer_value(octaveJ));
  40. }
  41. }
  42. };
  43. struct OctaveButton : Widget {
  44. int octave;
  45. void draw(const DrawArgs &args) override {
  46. Vec c = box.size.div(2);
  47. int activeOctave = 0;
  48. ParamWidget *paramWidget = getAncestorOfType<ParamWidget>();
  49. if (paramWidget && paramWidget->paramQuantity) {
  50. activeOctave = std::round(paramWidget->paramQuantity->getValue());
  51. }
  52. if (activeOctave == octave) {
  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 onDragHover(const event::DragHover &e) override {
  83. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  84. e.consume(this);
  85. }
  86. Widget::onDragHover(e);
  87. }
  88. void onDragEnter(const event::DragEnter &e) override;
  89. };
  90. struct OctaveParam : ParamWidget {
  91. OctaveParam() {
  92. box.size = mm2px(Vec(15.24, 63.0));
  93. const int octaves = 9;
  94. const float margin = mm2px(2.0);
  95. float height = box.size.y - 2*margin;
  96. for (int i = 0; i < octaves; i++) {
  97. OctaveButton *octaveButton = new OctaveButton();
  98. octaveButton->box.pos = Vec(0, height / octaves * i + margin);
  99. octaveButton->box.size = Vec(box.size.x, height / octaves);
  100. octaveButton->octave = 4 - i;
  101. addChild(octaveButton);
  102. }
  103. }
  104. void draw(const DrawArgs &args) override {
  105. // Background
  106. nvgBeginPath(args.vg);
  107. nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
  108. nvgFillColor(args.vg, nvgRGB(0, 0, 0));
  109. nvgFill(args.vg);
  110. ParamWidget::draw(args);
  111. }
  112. };
  113. inline void OctaveButton::onDragEnter(const event::DragEnter &e) {
  114. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  115. OctaveParam *origin = dynamic_cast<OctaveParam*>(e.origin);
  116. if (origin) {
  117. ParamWidget *paramWidget = getAncestorOfType<ParamWidget>();
  118. if (paramWidget && paramWidget->paramQuantity) {
  119. paramWidget->paramQuantity->setValue(octave);
  120. }
  121. }
  122. }
  123. Widget::onDragEnter(e);
  124. }
  125. struct OctaveWidget : ModuleWidget {
  126. OctaveWidget(Octave *module) {
  127. setModule(module);
  128. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Octave.svg")));
  129. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  130. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  131. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.62, 82.753)), module, Octave::OCTAVE_INPUT));
  132. addInput(createInputCentered<PJ301MPort>(mm2px(Vec(7.62, 97.253)), module, Octave::PITCH_INPUT));
  133. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.62, 112.253)), module, Octave::PITCH_OUTPUT));
  134. addParam(createParam<OctaveParam>(mm2px(Vec(0.0, 12.817)), module, Octave::OCTAVE_PARAM));
  135. }
  136. };
  137. Model *modelOctave = createModel<Octave, OctaveWidget>("Octave");