|
- #include "modular80.hpp"
-
- #include "dsp/digital.hpp"
-
- namespace rack_plugin_modular80 {
-
- struct Logistiker : Module {
- enum ParamIds {
- RATE_PARAM,
- R_PARAM,
- X_PARAM,
- RESET_PARAM,
- NUM_PARAMS
- };
- enum InputIds {
- CLK_INPUT,
- RST_INPUT,
- R_INPUT,
- NUM_INPUTS
- };
- enum OutputIds {
- X_OUTPUT,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- Logistiker() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS),
- x(0.0f),
- phase(0.0f)
- {
- }
-
- void step() override;
- void reset() override;
- void onReset() override;
-
- private:
- float logistic(const float x, const float r);
-
- SchmittTrigger rstButtonTrigger;
- SchmittTrigger rstInputTrigger;
- SchmittTrigger clkTrigger;
-
- float x;
- float phase;
- };
-
- void Logistiker::reset() {
- onReset();
- }
-
- void Logistiker::onReset() {
- x = 0.0f;
- phase = 0.0f;
- }
-
- float Logistiker::logistic(const float x, const float r) {
- return(r * x * (1.0f - x));
- }
-
- void Logistiker::step() {
- if (!outputs[X_OUTPUT].active) {
- return;
- }
-
- static bool doReset(false);
-
- if (rstButtonTrigger.process(params[RESET_PARAM].value) ||
- (inputs[RST_INPUT].active && rstInputTrigger.process(inputs[RST_INPUT].value)))
- {
- doReset = true;
- }
-
- bool doStep(false);
-
- // External clock
- if (inputs[CLK_INPUT].active) {
- if (clkTrigger.process(inputs[CLK_INPUT].value)) {
- phase = 0.0f;
- doStep = true;
- }
- }
- else {
- // Internal clock
- phase += pow(2.0f, params[RATE_PARAM].value)/engineGetSampleRate();
- if (phase >= 1.0f) {
- phase = 0.0f;
- doStep = true;
- }
- }
-
- if (doStep) {
-
- // Synchronize resetting x with steps.
- if (doReset) {
- x = params[X_PARAM].value;
- doReset = false;
- }
-
- const float r = clamp(params[R_PARAM].value + inputs[R_INPUT].value, 0.0f, 8.0f);
-
- // Don't let population die!
- x = clamp(logistic(x, r), 0.00001f, 1.0f);
- }
-
- outputs[X_OUTPUT].value = clamp(x * 10.0f, -10.0f, 10.0f);
- }
-
- struct LogistikerWidget : ModuleWidget {
- LogistikerWidget(Logistiker *module);
- };
-
- LogistikerWidget::LogistikerWidget(Logistiker *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/Logistiker.svg")));
-
- addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
- addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
-
- addParam(ParamWidget::create<Davies1900hLargeBlackKnob>(Vec(18, 62), module, Logistiker::RATE_PARAM, -2.0f, 6.0f, 2.0f)); // 0.25..64
- addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(49, 140), module, Logistiker::R_PARAM, 0.0f, 8.0f, 3.56995f)); // default = onset of chaos
- addParam(ParamWidget::create<Davies1900hBlackKnob>(Vec(49, 206), module, Logistiker::X_PARAM, 0.0f, 0.5f, 0.0f));
-
- addInput(Port::create<PJ301MPort>(Vec(11, 146), Port::INPUT, module, Logistiker::R_INPUT));
-
- addParam(ParamWidget::create<TL1105>(Vec(15, 217), module, Logistiker::RESET_PARAM, 0, 1, 0));
-
- addInput(Port::create<PJ301MPort>(Vec(54, 276), Port::INPUT, module, Logistiker::CLK_INPUT));
- addInput(Port::create<PJ301MPort>(Vec(11, 276), Port::INPUT, module, Logistiker::RST_INPUT));
-
- addOutput(Port::create<PJ301MPort>(Vec(33, 319), Port::OUTPUT, module, Logistiker::X_OUTPUT));
-
- addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
- addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
- }
-
- } // namespace rack_plugin_modular80
-
- using namespace rack_plugin_modular80;
-
- RACK_PLUGIN_MODEL_INIT(modular80, Logistiker) {
- Model *modelLogistiker = Model::create<Logistiker, LogistikerWidget>("modular80", "Logistiker", "Logistiker", RANDOM_TAG);
- return modelLogistiker;
- }
|