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.

146 lines
3.5KB

  1. #include "modular80.hpp"
  2. #include "dsp/digital.hpp"
  3. namespace rack_plugin_modular80 {
  4. struct Logistiker : Module {
  5. enum ParamIds {
  6. RATE_PARAM,
  7. R_PARAM,
  8. X_PARAM,
  9. RESET_PARAM,
  10. NUM_PARAMS
  11. };
  12. enum InputIds {
  13. CLK_INPUT,
  14. RST_INPUT,
  15. R_INPUT,
  16. NUM_INPUTS
  17. };
  18. enum OutputIds {
  19. X_OUTPUT,
  20. NUM_OUTPUTS
  21. };
  22. enum LightIds {
  23. NUM_LIGHTS
  24. };
  25. Logistiker() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS),
  26. x(0.0f),
  27. phase(0.0f)
  28. {
  29. }
  30. void step() override;
  31. void reset() override;
  32. void onReset() override;
  33. private:
  34. float logistic(const float x, const float r);
  35. SchmittTrigger rstButtonTrigger;
  36. SchmittTrigger rstInputTrigger;
  37. SchmittTrigger clkTrigger;
  38. float x;
  39. float phase;
  40. };
  41. void Logistiker::reset() {
  42. onReset();
  43. }
  44. void Logistiker::onReset() {
  45. x = 0.0f;
  46. phase = 0.0f;
  47. }
  48. float Logistiker::logistic(const float x, const float r) {
  49. return(r * x * (1.0f - x));
  50. }
  51. void Logistiker::step() {
  52. if (!outputs[X_OUTPUT].active) {
  53. return;
  54. }
  55. static bool doReset(false);
  56. if (rstButtonTrigger.process(params[RESET_PARAM].value) ||
  57. (inputs[RST_INPUT].active && rstInputTrigger.process(inputs[RST_INPUT].value)))
  58. {
  59. doReset = true;
  60. }
  61. bool doStep(false);
  62. // External clock
  63. if (inputs[CLK_INPUT].active) {
  64. if (clkTrigger.process(inputs[CLK_INPUT].value)) {
  65. phase = 0.0f;
  66. doStep = true;
  67. }
  68. }
  69. else {
  70. // Internal clock
  71. phase += pow(2.0f, params[RATE_PARAM].value)/engineGetSampleRate();
  72. if (phase >= 1.0f) {
  73. phase = 0.0f;
  74. doStep = true;
  75. }
  76. }
  77. if (doStep) {
  78. // Synchronize resetting x with steps.
  79. if (doReset) {
  80. x = params[X_PARAM].value;
  81. doReset = false;
  82. }
  83. const float r = clamp(params[R_PARAM].value + inputs[R_INPUT].value, 0.0f, 8.0f);
  84. // Don't let population die!
  85. x = clamp(logistic(x, r), 0.00001f, 1.0f);
  86. }
  87. outputs[X_OUTPUT].value = clamp(x * 10.0f, -10.0f, 10.0f);
  88. }
  89. struct LogistikerWidget : ModuleWidget {
  90. LogistikerWidget(Logistiker *module);
  91. };
  92. LogistikerWidget::LogistikerWidget(Logistiker *module) : ModuleWidget(module) {
  93. setPanel(SVG::load(assetPlugin(plugin, "res/Logistiker.svg")));
  94. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  95. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  96. addParam(ParamWidget::create<Davies1900hLargeBlackKnob>(Vec(18, 62), module, Logistiker::RATE_PARAM, -2.0f, 6.0f, 2.0f)); // 0.25..64
  97. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(49, 140), module, Logistiker::R_PARAM, 0.0f, 8.0f, 3.56995f)); // default = onset of chaos
  98. addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(49, 206), module, Logistiker::X_PARAM, 0.0f, 0.5f, 0.0f));
  99. addInput(Port::create<PJ301MPort>(Vec(11, 146), Port::INPUT, module, Logistiker::R_INPUT));
  100. addParam(ParamWidget::create<TL1105>(Vec(15, 217), module, Logistiker::RESET_PARAM, 0, 1, 0));
  101. addInput(Port::create<PJ301MPort>(Vec(54, 276), Port::INPUT, module, Logistiker::CLK_INPUT));
  102. addInput(Port::create<PJ301MPort>(Vec(11, 276), Port::INPUT, module, Logistiker::RST_INPUT));
  103. addOutput(Port::create<PJ301MPort>(Vec(33, 319), Port::OUTPUT, module, Logistiker::X_OUTPUT));
  104. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  105. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  106. }
  107. } // namespace rack_plugin_modular80
  108. using namespace rack_plugin_modular80;
  109. RACK_PLUGIN_MODEL_INIT(modular80, Logistiker) {
  110. Model *modelLogistiker = Model::create<Logistiker, LogistikerWidget>("modular80", "Logistiker", "Logistiker", RANDOM_TAG);
  111. return modelLogistiker;
  112. }