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.

179 lines
5.2KB

  1. #pragma once
  2. #include <algorithm>
  3. #include <random>
  4. #include <string>
  5. #include <utility>
  6. #include <app.hpp>
  7. #include <util/math.hpp>
  8. #include "dhe-modules.h"
  9. namespace DHE {
  10. inline void moveTo(rack::Rect &box, rack::Vec center) {
  11. box.pos = center.minus(box.size.mult(0.5f));
  12. }
  13. inline void moveTo(float x, float y, rack::Widget *widget) {
  14. moveTo(widget->box, rack::mm2px({x, y}));
  15. }
  16. template <typename P> class Jack : public rack::SVGPort {
  17. public:
  18. Jack() {
  19. background->svg = P::svg("port");
  20. background->wrap();
  21. box.size = background->box.size;
  22. }
  23. };
  24. template <typename P> class InputJack : public Jack<P> {};
  25. template <typename P> class OutputJack : public Jack<P> {};
  26. static auto plugin_asset_dir() -> std::string {
  27. #ifdef USE_VST2
  28. static const auto dir = rack::assetPlugin("DHE-Modules"/*plugin*/, "svg/");
  29. #else
  30. static const auto dir = rack::assetPlugin(plugin, std::string("svg/"));
  31. #endif
  32. return dir;
  33. }
  34. template <typename P> class Panel : public rack::ModuleWidget {
  35. public:
  36. Panel(rack::Module *module, int widget_hp) : rack::ModuleWidget{module} {
  37. box.size =
  38. rack::Vec{widget_hp * rack::RACK_GRID_WIDTH, rack::RACK_GRID_HEIGHT};
  39. auto panel = new rack::SVGPanel();
  40. panel->box.size = box.size;
  41. panel->setBackground(panel_svg());
  42. addChild(panel);
  43. install_screws();
  44. }
  45. void fromJson(json_t *patch) override {
  46. // If there's no data, we're loading from a legacy patch. Add empty data to
  47. // the incoming patch so that ModuleWidget::fromJson will call
  48. // Module::fromJson, which will configure the module with appropriate legacy
  49. // behavior.
  50. if (!json_object_get(patch, "data")) {
  51. json_object_set_new(patch, "data", json_object());
  52. }
  53. rack::ModuleWidget::fromJson(patch);
  54. }
  55. static auto svg(const std::string &filename) -> std::shared_ptr<rack::SVG> {
  56. static const auto module_asset_dir =
  57. plugin_asset_dir() + P::module_slug + "/";
  58. return rack::SVG::load(module_asset_dir + filename + ".svg");
  59. }
  60. static auto panel_svg() -> std::shared_ptr<rack::SVG> {
  61. return rack::SVG::load(plugin_asset_dir() + P::module_slug + ".svg");
  62. }
  63. protected:
  64. auto height() const -> float { return box.size.y * MM_PER_IN / SVG_DPI; }
  65. auto width() const -> float { return box.size.x * MM_PER_IN / SVG_DPI; }
  66. void install(float x, float y, InputJack<P> *jack) {
  67. moveTo(x, y, jack);
  68. addInput(jack);
  69. }
  70. void install(float x, float y, OutputJack<P> *jack) {
  71. moveTo(x, y, jack);
  72. addOutput(jack);
  73. }
  74. void install(float x, float y, rack::ParamWidget *param) {
  75. moveTo(x, y, param);
  76. addParam(param);
  77. }
  78. void install(float x, float y, rack::Widget *widget) {
  79. moveTo(x, y, widget);
  80. addChild(widget);
  81. }
  82. template <template <typename> class K>
  83. auto knob(int index, float initial = 0.5f,
  84. const std::function<void(float)> &on_change = [](float) {}) const
  85. -> K<P> * {
  86. return param<K<P>>(index, 1, initial, on_change);
  87. }
  88. template <template <typename> class B = Button>
  89. auto button(int index, const std::function<void(bool)> &on_change = [](bool) {
  90. }) const -> B<P> * {
  91. return param<B<P>>(index, 1, 0, on_change);
  92. }
  93. template <template <typename> class C>
  94. auto toggle(int index, int initial,
  95. const std::function<void(int)> &on_change = [](int) {}) const
  96. -> C<P> * {
  97. return param<C<P>>(index, C<P>::size - 1, initial, on_change);
  98. }
  99. template <int N>
  100. auto toggle(int index, int initial,
  101. const std::function<void(int)> &on_change = [](int) {}) const
  102. -> Toggle<P, N> * {
  103. return param<Toggle<P, N>>(index, N - 1, initial, on_change);
  104. }
  105. auto input(int index) const -> InputJack<P> * {
  106. return rack::Port::create<InputJack<P>>({0, 0}, rack::Port::PortType::INPUT,
  107. module, index);
  108. }
  109. auto output(int index) const -> OutputJack<P> * {
  110. return rack::Port::create<OutputJack<P>>(
  111. {0, 0}, rack::Port::PortType::OUTPUT, module, index);
  112. }
  113. private:
  114. template <typename T>
  115. auto param(int index, float max, float initial,
  116. const std::function<void(float)> &on_change) const -> T * {
  117. auto widget =
  118. rack::ParamWidget::create<T>({0, 0}, module, index, 0, max, initial);
  119. widget->notify = on_change;
  120. widget->notify(widget->value);
  121. return widget;
  122. }
  123. void install_screws() {
  124. auto screw_diameter = rack::RACK_GRID_WIDTH * MM_PER_IN / SVG_DPI;
  125. auto screw_radius = screw_diameter / 2.f;
  126. auto top = screw_radius;
  127. auto bottom = height() - top;
  128. auto max_screw_inset = screw_diameter * 1.5f;
  129. auto left = std::min(width() / 4.f, max_screw_inset);
  130. auto right = width() - left;
  131. auto screw_positions = std::vector<rack::Vec>{
  132. {left, top}, {left, bottom}, {right, top}, {right, bottom}};
  133. std::shuffle(screw_positions.begin(), screw_positions.end(),
  134. std::mt19937(std::random_device()()));
  135. auto p_special = screw_positions.back();
  136. install(p_special.x, p_special.y, rack::Widget::create<rack::ScrewBlack>());
  137. screw_positions.pop_back();
  138. for (auto p : screw_positions) {
  139. install(p.x, p.y, rack::Widget::create<rack::ScrewSilver>());
  140. }
  141. }
  142. };
  143. } // namespace DHE