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.

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