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
4.4KB

  1. //***********************************************************************************************
  2. //Blank-Panel Logo for VCV Rack by Pierre Collard and Marc Boulé
  3. //
  4. //Based on code from the Fundamental plugins by Andrew Belt
  5. //See ./LICENSE.txt for all licenses
  6. //***********************************************************************************************
  7. #include "Geodesics.hpp"
  8. namespace rack_plugin_Geodesics {
  9. // From Fundamental LFO.cpp
  10. struct LowFrequencyOscillator {
  11. float phase = 0.0f;
  12. float freq = 1.0f;
  13. LowFrequencyOscillator() {}
  14. void setPitch(float pitch) {
  15. pitch = fminf(pitch, 8.0f);
  16. freq = powf(2.0f, pitch);
  17. }
  18. void step(float dt) {
  19. float deltaPhase = fminf(freq * dt, 0.5f);
  20. phase += deltaPhase;
  21. if (phase >= 1.0f)
  22. phase -= 1.0f;
  23. }
  24. float sqr() {
  25. return (phase < 0.5f) ? 2.0f : 0.0f;
  26. }
  27. };
  28. //*****************************************************************************
  29. struct BlankLogo : Module {
  30. enum ParamIds {
  31. CLK_FREQ_PARAM,
  32. NUM_PARAMS
  33. };
  34. enum InputIds {
  35. NUM_INPUTS
  36. };
  37. enum OutputIds {
  38. OUT_OUTPUT,
  39. NUM_OUTPUTS
  40. };
  41. enum LightIds {
  42. NUM_LIGHTS
  43. };
  44. int panelTheme = 0;
  45. float clkValue;
  46. int stepIndex;
  47. float song[5] = {7.0f/12.0f, 9.0f/12.0f, 5.0f/12.0f, 5.0f/12.0f - 1.0f, 0.0f/12.0f};
  48. LowFrequencyOscillator oscillatorClk;
  49. SchmittTrigger clkTrigger;
  50. BlankLogo() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  51. clkTrigger.reset();
  52. onReset();
  53. }
  54. void onReset() override {
  55. clkValue = 0.0f;
  56. stepIndex = 0;
  57. }
  58. void onRandomize() override {
  59. }
  60. json_t *toJson() override {
  61. json_t *rootJ = json_object();
  62. // panelTheme
  63. json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme));
  64. return rootJ;
  65. }
  66. void fromJson(json_t *rootJ) override {
  67. // panelTheme
  68. json_t *panelThemeJ = json_object_get(rootJ, "panelTheme");
  69. if (panelThemeJ)
  70. panelTheme = json_integer_value(panelThemeJ);
  71. }
  72. // Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate()
  73. void step() override {
  74. if (outputs[OUT_OUTPUT].active) {
  75. // CLK
  76. oscillatorClk.setPitch(params[CLK_FREQ_PARAM].value);
  77. oscillatorClk.step(engineGetSampleTime());
  78. float clkValue = oscillatorClk.sqr();
  79. if (clkTrigger.process(clkValue)) {
  80. stepIndex++;
  81. if (stepIndex >= 5)
  82. stepIndex = 0;
  83. outputs[OUT_OUTPUT].value = song[stepIndex];
  84. }
  85. }
  86. }
  87. };
  88. struct BlankLogoWidget : ModuleWidget {
  89. struct PanelThemeItem : MenuItem {
  90. BlankLogo *module;
  91. int theme;
  92. void onAction(EventAction &e) override {
  93. module->panelTheme = theme;
  94. }
  95. void step() override {
  96. rightText = (module->panelTheme == theme) ? "✔" : "";
  97. }
  98. };
  99. Menu *createContextMenu() override {
  100. Menu *menu = ModuleWidget::createContextMenu();
  101. MenuLabel *spacerLabel = new MenuLabel();
  102. menu->addChild(spacerLabel);
  103. BlankLogo *module = dynamic_cast<BlankLogo*>(this->module);
  104. assert(module);
  105. MenuLabel *themeLabel = new MenuLabel();
  106. themeLabel->text = "Panel Theme";
  107. menu->addChild(themeLabel);
  108. PanelThemeItem *lightItem = new PanelThemeItem();
  109. lightItem->text = lightPanelID;// Geodesics.hpp
  110. lightItem->module = module;
  111. lightItem->theme = 0;
  112. menu->addChild(lightItem);
  113. PanelThemeItem *darkItem = new PanelThemeItem();
  114. darkItem->text = darkPanelID;// Geodesics.hpp
  115. darkItem->module = module;
  116. darkItem->theme = 1;
  117. //menu->addChild(darkItem);
  118. return menu;
  119. }
  120. BlankLogoWidget(BlankLogo *module) : ModuleWidget(module) {
  121. // Main panel from Inkscape
  122. DynamicSVGPanel *panel = new DynamicSVGPanel();
  123. panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/BlankLogoBG-01.svg")));
  124. //panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/BlankLogo-02.svg")));// no dark pannel for now
  125. box.size = panel->box.size;
  126. panel->mode = &module->panelTheme;
  127. addChild(panel);
  128. // Screws
  129. // part of svg panel, no code required
  130. addParam(createParamCentered<BlankCKnob>(Vec(29.5f,74.2f), module, BlankLogo::CLK_FREQ_PARAM, -2.0f, 4.0f, 1.0f));// 120 BMP when default value
  131. addOutput(createOutputCentered<BlankPort>(Vec(29.5f,187.5f), module, BlankLogo::OUT_OUTPUT));
  132. }
  133. };
  134. } // namespace rack_plugin_Geodesics
  135. using namespace rack_plugin_Geodesics;
  136. RACK_PLUGIN_MODEL_INIT(Geodesics, BlankLogo) {
  137. Model *modelBlankLogo = Model::create<BlankLogo, BlankLogoWidget>("Geodesics", "Blank-Panel Logo", "MISC - Blank-Panel Logo", BLANK_TAG);
  138. return modelBlankLogo;
  139. }