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.

196 lines
5.4KB

  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. int lastOctave = 0;
  20. Octave() {
  21. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  22. configParam(OCTAVE_PARAM, -4.f, 4.f, 0.f, "Shift", " oct");
  23. getParamQuantity(OCTAVE_PARAM)->snapEnabled = true;
  24. configInput(PITCH_INPUT, "1V/octave pitch");
  25. configInput(OCTAVE_INPUT, "Octave shift CV");
  26. configOutput(PITCH_OUTPUT, "Pitch");
  27. configBypass(PITCH_INPUT, PITCH_OUTPUT);
  28. }
  29. void process(const ProcessArgs& args) override {
  30. int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
  31. int octaveParam = std::round(params[OCTAVE_PARAM].getValue());
  32. for (int c = 0; c < channels; c++) {
  33. int octave = octaveParam + std::round(inputs[OCTAVE_INPUT].getPolyVoltage(c));
  34. float pitch = inputs[PITCH_INPUT].getVoltage(c);
  35. pitch += octave;
  36. outputs[PITCH_OUTPUT].setVoltage(pitch, c);
  37. if (c == 0)
  38. lastOctave = octave;
  39. }
  40. outputs[PITCH_OUTPUT].setChannels(channels);
  41. }
  42. void dataFromJson(json_t* rootJ) override {
  43. // In Fundamental 1.1.1 and earlier, the octave param was internal data.
  44. json_t* octaveJ = json_object_get(rootJ, "octave");
  45. if (octaveJ) {
  46. params[OCTAVE_PARAM].setValue(json_integer_value(octaveJ));
  47. }
  48. }
  49. };
  50. struct OctaveButton : Widget {
  51. int octave;
  52. void drawLayer(const DrawArgs& args, int layer) override {
  53. if (layer != 1)
  54. return;
  55. Vec c = box.size.div(2);
  56. int activeOctave = 0;
  57. int lastOctave = 0;
  58. ParamWidget* paramWidget = getAncestorOfType<ParamWidget>();
  59. assert(paramWidget);
  60. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  61. if (pq) {
  62. activeOctave = std::round(pq->getValue());
  63. Octave* module = dynamic_cast<Octave*>(pq->module);
  64. if (module)
  65. lastOctave = module->lastOctave;
  66. }
  67. if (activeOctave == octave) {
  68. // Enabled
  69. nvgBeginPath(args.vg);
  70. nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
  71. if (octave == 0)
  72. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  73. else
  74. nvgFillColor(args.vg, SCHEME_YELLOW);
  75. nvgFill(args.vg);
  76. }
  77. else if (lastOctave == octave) {
  78. // Disabled but enabled by CV
  79. nvgBeginPath(args.vg);
  80. nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
  81. if (octave == 0)
  82. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.5 * 0.33));
  83. else
  84. nvgFillColor(args.vg, color::alpha(SCHEME_YELLOW, 0.5));
  85. nvgFill(args.vg);
  86. }
  87. else {
  88. // Disabled
  89. nvgBeginPath(args.vg);
  90. nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
  91. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  92. nvgFill(args.vg);
  93. nvgBeginPath(args.vg);
  94. nvgCircle(args.vg, c.x, c.y, mm2px(3.0 / 2));
  95. nvgFillColor(args.vg, nvgRGB(0x12, 0x12, 0x12));
  96. nvgFill(args.vg);
  97. if (octave == 0) {
  98. nvgBeginPath(args.vg);
  99. nvgCircle(args.vg, c.x, c.y, mm2px(1.0 / 2));
  100. nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
  101. nvgFill(args.vg);
  102. }
  103. }
  104. }
  105. void onDragHover(const event::DragHover& e) override {
  106. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  107. e.consume(this);
  108. }
  109. Widget::onDragHover(e);
  110. }
  111. void onDragEnter(const event::DragEnter& e) override;
  112. };
  113. struct OctaveParam : ParamWidget {
  114. OctaveParam() {
  115. box.size = mm2px(Vec(15.263, 55.88));
  116. const int octaves = 9;
  117. const float margin = mm2px(2.0);
  118. float height = box.size.y - 2 * margin;
  119. for (int i = 0; i < octaves; i++) {
  120. OctaveButton* octaveButton = new OctaveButton();
  121. octaveButton->box.pos = Vec(0, height / octaves * i + margin);
  122. octaveButton->box.size = Vec(box.size.x, height / octaves);
  123. octaveButton->octave = 4 - i;
  124. addChild(octaveButton);
  125. }
  126. }
  127. };
  128. inline void OctaveButton::onDragEnter(const event::DragEnter& e) {
  129. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  130. OctaveParam* origin = dynamic_cast<OctaveParam*>(e.origin);
  131. if (origin) {
  132. ParamWidget* paramWidget = getAncestorOfType<ParamWidget>();
  133. assert(paramWidget);
  134. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  135. if (pq) {
  136. pq->setValue(octave);
  137. }
  138. }
  139. }
  140. Widget::onDragEnter(e);
  141. }
  142. struct OctaveDisplay : LedDisplay {
  143. void setModule(Octave* module) {
  144. addChild(createParam<OctaveParam>(mm2px(Vec(0.0, 0.0)), module, Octave::OCTAVE_PARAM));
  145. }
  146. };
  147. struct OctaveWidget : ModuleWidget {
  148. OctaveWidget(Octave* module) {
  149. setModule(module);
  150. setPanel(createPanel(asset::plugin(pluginInstance, "res/Octave.svg"), asset::plugin(pluginInstance, "res/Octave-dark.svg")));
  151. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, 0)));
  152. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  153. addChild(createWidget<ThemedScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  154. addChild(createWidget<ThemedScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  155. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(7.62, 80.573)), module, Octave::OCTAVE_INPUT));
  156. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(7.62, 96.859)), module, Octave::PITCH_INPUT));
  157. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(7.62, 113.115)), module, Octave::PITCH_OUTPUT));
  158. OctaveDisplay* display = createWidget<OctaveDisplay>(mm2px(Vec(0.0, 13.039)));
  159. display->box.size = mm2px(Vec(15.263, 55.88));
  160. display->setModule(module);
  161. addChild(display);
  162. }
  163. };
  164. Model* modelOctave = createModel<Octave, OctaveWidget>("Octave");