//*********************************************************************************************** //Atomic Duophonic Voltage Sequencer module for VCV Rack by Pierre Collard and Marc Boulé // //Based on code from the Fundamental plugins by Andrew Belt and graphics // from the Component Library by Wes Milholen. //See ./LICENSE.txt for all licenses //See ./res/fonts/ for font licenses // //*********************************************************************************************** #include "Geodesics.hpp" namespace rack_plugin_Geodesics { struct Ions : Module { enum ParamIds { RUN_PARAM, RESET_PARAM, ENUMS(CV_PARAMS, 15),// 0 is center, move conter clockwise top atom, then clockwise bot atom PROB_PARAM, ENUMS(OCT_PARAMS, 2), LEAP_PARAM, ENUMS(STATE_PARAMS, 2),// 3 states : global, local, global+local PLANK_PARAM, uncertainty_PARAM, RESETONRUN_PARAM, STEPCLOCKS_PARAM, NUM_PARAMS }; enum InputIds { CLK_INPUT, ENUMS(CLK_INPUTS, 2), RUN_INPUT, RESET_INPUT, PROB_INPUT,// CV_value/10 is added to PROB_PARAM, which is a 0 to 1 knob ENUMS(OCTCV_INPUTS, 2), ENUMS(STATECV_INPUTS, 2), NUM_INPUTS }; enum OutputIds { ENUMS(SEQ_OUTPUTS, 2), ENUMS(JUMP_OUTPUTS, 2), NUM_OUTPUTS }; enum LightIds { ENUMS(BLUE_LIGHTS, 16), ENUMS(YELLOW_LIGHTS, 16), RUN_LIGHT, RESET_LIGHT, ENUMS(GLOBAL_LIGHTS, 2),// 0 is top atom, 1 is bottom atom ENUMS(LOCAL_LIGHTS, 2), LEAP_LIGHT, ENUMS(OCTA_LIGHTS, 3),// 0 is center, 1 is inside mirrors, 2 is outside mirrors ENUMS(OCTB_LIGHTS, 3), ENUMS(PLANK_LIGHT, 3),// room for blue, yellow, white uncertainty_LIGHT, ENUMS(JUMP_LIGHTS, 2), RESETONRUN_LIGHT, STEPCLOCKS_LIGHT, NUM_LIGHTS }; // Constants static constexpr float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays) const int cvMap[2][16] = {{0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14}, {0, 8, 9 ,10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7}};// map each of the 16 steps of a sequence step to a CV knob index (0-14) // Need to save, with reset bool running; bool resetOnRun; int quantize;// a.k.a. plank constant, 0 = none, 1 = blue, 2 = yellow, 3 = white (both) //bool symmetry; bool uncertainty; int stepIndexes[2];// position of electrons (sequencers) int states[2];// which clocks to use (0 = global, 1 = local, 2 = both) int ranges[2];// [0; 2], number of extra octaves to span each side of central octave (which is C4: 0 - 1V) bool leap; // Need to save, no reset int panelTheme; // No need to save, with reset long clockIgnoreOnReset; float resetLight; bool rangeInc[2];// true when 1-3-5 increasing, false when 5-3-1 decreasing // No need to save, no reset SchmittTrigger runningTrigger; SchmittTrigger clockTrigger; SchmittTrigger clocksTriggers[2]; SchmittTrigger resetTrigger; SchmittTrigger stateTriggers[2]; SchmittTrigger octTriggers[2]; SchmittTrigger stateCVTriggers[2]; SchmittTrigger leapTrigger; SchmittTrigger plankTrigger; SchmittTrigger uncertaintyTrigger; SchmittTrigger resetOnRunTrigger; SchmittTrigger stepClocksTrigger; PulseGenerator jumpPulses[2]; float jumpLights[2]; float stepClocksLight; inline float quantizeCV(float cv) {return roundf(cv * 12.0f) / 12.0f;} inline bool jumpRandom() {return (randomUniform() < (params[PROB_PARAM].value + inputs[PROB_INPUT].value / 10.0f));}// randomUniform is [0.0, 1.0), see include/util/common.hpp Ions() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { // Need to save, no reset panelTheme = 0; // No need to save, no reset runningTrigger.reset(); clockTrigger.reset(); for (int i = 0; i < 2; i++) { clocksTriggers[i].reset(); stateTriggers[i].reset(); octTriggers[i].reset(); stateCVTriggers[i].reset(); jumpPulses[i].reset(); jumpLights[i] = 0.0f; } stepClocksLight = 0.0f; resetTrigger.reset(); leapTrigger.reset(); plankTrigger.reset(); uncertaintyTrigger.reset(); resetOnRunTrigger.reset(); stepClocksTrigger.reset(); onReset(); } // widgets are not yet created when module is created // even if widgets not created yet, can use params[] and should handle 0.0f value since step may call // this before widget creation anyways // called from the main thread if by constructor, called by engine thread if right-click initialization // when called by constructor, module is created before the first step() is called void onReset() override { // Need to save, with reset running = false; resetOnRun = false; quantize = 3; //symmetry = false; uncertainty = false; for (int i = 0; i < 2; i++) { states[i] = 0; ranges[i] = 1; } leap = false; initRun(true, false); // No need to save, with reset for (int i = 0; i < 2; i++) { rangeInc[i] = true; } } // widgets randomized before onRandomize() is called // called by engine thread if right-click randomize void onRandomize() override { // Need to save, with reset running = false; resetOnRun = false; quantize = randomu32() % 4; //symmetry = false; uncertainty = false; for (int i = 0; i < 2; i++) { states[i] = randomu32() % 3; ranges[i] = randomu32() % 3; } leap = (randomu32() % 2) > 0; initRun(true, true); // No need to save, with reset for (int i = 0; i < 2; i++) { rangeInc[i] = true; } } void initRun(bool hard, bool randomize) {// run button activated or run edge in run input jack if (hard) { if (randomize) { stepIndexes[0] = randomu32() % 16; stepIndexes[1] = randomu32() % 16; } else { stepIndexes[0] = 0; stepIndexes[1] = 0; } } clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate()); resetLight = 0.0f; } // called by main thread json_t *toJson() override { json_t *rootJ = json_object(); // Need to save (reset or not) // panelTheme json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme)); // resetOnRun json_object_set_new(rootJ, "resetOnRun", json_boolean(resetOnRun)); // quantize json_object_set_new(rootJ, "quantize", json_integer(quantize)); // symmetry //json_object_set_new(rootJ, "symmetry", json_boolean(symmetry)); // uncertainty json_object_set_new(rootJ, "uncertainty", json_boolean(uncertainty)); // running json_object_set_new(rootJ, "running", json_boolean(running)); // stepIndexes json_object_set_new(rootJ, "stepIndexes0", json_integer(stepIndexes[0])); json_object_set_new(rootJ, "stepIndexes1", json_integer(stepIndexes[1])); // states json_object_set_new(rootJ, "states0", json_integer(states[0])); json_object_set_new(rootJ, "states1", json_integer(states[1])); // ranges json_object_set_new(rootJ, "ranges0", json_integer(ranges[0])); json_object_set_new(rootJ, "ranges1", json_integer(ranges[1])); // leap json_object_set_new(rootJ, "leap", json_boolean(leap)); return rootJ; } // widgets have their fromJson() called before this fromJson() is called // called by main thread void fromJson(json_t *rootJ) override { // Need to save (reset or not) // panelTheme json_t *panelThemeJ = json_object_get(rootJ, "panelTheme"); if (panelThemeJ) panelTheme = json_integer_value(panelThemeJ); // resetOnRun json_t *resetOnRunJ = json_object_get(rootJ, "resetOnRun"); if (resetOnRunJ) resetOnRun = json_is_true(resetOnRunJ); // quantize json_t *quantizeJ = json_object_get(rootJ, "quantize"); if (quantizeJ) quantize = json_integer_value(quantizeJ); // symmetry //json_t *symmetryJ = json_object_get(rootJ, "symmetry"); //if (symmetryJ) //symmetry = json_is_true(symmetryJ); // uncertainty json_t *uncertaintyJ = json_object_get(rootJ, "uncertainty"); if (uncertaintyJ) uncertainty = json_is_true(uncertaintyJ); // running json_t *runningJ = json_object_get(rootJ, "running"); if (runningJ) running = json_is_true(runningJ); // stepIndexes json_t *stepIndexes0J = json_object_get(rootJ, "stepIndexes0"); if (stepIndexes0J) stepIndexes[0] = json_integer_value(stepIndexes0J); json_t *stepIndexes1J = json_object_get(rootJ, "stepIndexes1"); if (stepIndexes1J) stepIndexes[1] = json_integer_value(stepIndexes1J); // states json_t *states0J = json_object_get(rootJ, "states0"); if (states0J) states[0] = json_integer_value(states0J); json_t *states1J = json_object_get(rootJ, "states1"); if (states1J) states[1] = json_integer_value(states1J); // ranges json_t *ranges0J = json_object_get(rootJ, "ranges0"); if (ranges0J) ranges[0] = json_integer_value(ranges0J); json_t *ranges1J = json_object_get(rootJ, "ranges1"); if (ranges1J) ranges[1] = json_integer_value(ranges1J); // leap json_t *leapJ = json_object_get(rootJ, "leap"); if (leapJ) leap = json_is_true(leapJ); // No need to save, with reset initRun(true, false); rangeInc[0] = true; rangeInc[1] = true; } // Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate() void step() override { float sampleTime = engineGetSampleTime(); //********** Buttons, knobs, switches and inputs ********** // Run button if (runningTrigger.process(params[RUN_PARAM].value + inputs[RUN_INPUT].value)) { running = !running; if (running) initRun(resetOnRun, false); } // Leap button if (leapTrigger.process(params[LEAP_PARAM].value)) { leap = !leap; } // Plank button (quatize) if (plankTrigger.process(params[PLANK_PARAM].value)) { quantize++; if (quantize >= 4) quantize = 0; } // uncertainty button if (uncertaintyTrigger.process(params[uncertainty_PARAM].value)) { uncertainty = !uncertainty; } // Reset on Run button if (resetOnRunTrigger.process(params[RESETONRUN_PARAM].value)) { resetOnRun = !resetOnRun; } // State buttons and CV inputs (state: 0 = global, 1 = local, 2 = both) for (int i = 0; i < 2; i++) { int stateTrig = stateTriggers[i].process(params[STATE_PARAMS + i].value); if (inputs[STATECV_INPUTS + i].active) { if (inputs[STATECV_INPUTS + i].value <= -1.0f) states[i] = 1; else if (inputs[STATECV_INPUTS + i].value < 1.0f) states[i] = 2; else states[i] = 0; } else if (stateTrig) { states[i]++; if (states[i] >= 3) states[i] = 0; } } // Range buttons and CV inputs for (int i = 0; i < 2; i++) { int rangeTrig = octTriggers[i].process(params[OCT_PARAMS + i].value); if (inputs[OCTCV_INPUTS + i].active) { if (inputs[OCTCV_INPUTS + i].value <= -1.0f) ranges[i] = 0; else if (inputs[OCTCV_INPUTS + i].value < 1.0f) ranges[i] = 1; else ranges[i] = 2; } else if (rangeTrig) { if (rangeInc[i]) { ranges[i]++; if (ranges[i] >= 3) { ranges[i] = 1; rangeInc[i] = false; } } else { ranges[i]--; if (ranges[i] < 0) { ranges[i] = 1; rangeInc[i] = true; } } } } //********** Clock and reset ********** // Clocks bool globalClockTrig = clockTrigger.process(inputs[CLK_INPUT].value); bool stepClocksTrig = stepClocksTrigger.process(params[STEPCLOCKS_PARAM].value); for (int i = 0; i < 2; i++) { int jumpCount = 0; if (running && clockIgnoreOnReset == 0l) { // Local clocks and uncertainty bool localClockTrig = clocksTriggers[i].process(inputs[CLK_INPUTS + i].value); localClockTrig &= (states[i] >= 1); if (localClockTrig) { if (uncertainty) {// local clock modified by uncertainty int numSteps = 8; int prob = randomu32() % 1000; if (prob < 175) numSteps = 1; else if (prob < 330) // 175 + 155 numSteps = 2; else if (prob < 475) // 175 + 155 + 145 numSteps = 3; else if (prob < 610) // 175 + 155 + 145 + 135 numSteps = 4; else if (prob < 725) // 175 + 155 + 145 + 135 + 115 numSteps = 5; else if (prob < 830) // 175 + 155 + 145 + 135 + 115 + 105 numSteps = 6; else if (prob < 925) // 175 + 155 + 145 + 135 + 115 + 105 + 95 numSteps = 7; for (int n = 0; n < numSteps; n++) jumpCount += stepElectron(i, leap); } else jumpCount += stepElectron(i, leap);// normal local clock } // Global clock if (globalClockTrig && ((states[i] & 0x1) == 0) && !localClockTrig) { jumpCount += stepElectron(i, leap); } } // Magnetic clock (step clock) if (stepClocksTrig) jumpCount += stepElectron(i, leap); // Jump occurred feedback if ((jumpCount & 0x1) != 0) { jumpPulses[i].trigger(0.001f); jumpLights[i] = 1.0f; } } //if (symmetry) //stepIndexes[1] = stepIndexes[0]; // Reset if (resetTrigger.process(inputs[RESET_INPUT].value + params[RESET_PARAM].value)) { initRun(true, uncertainty); resetLight = 1.0f; clockTrigger.reset(); } else resetLight -= (resetLight / lightLambda) * sampleTime; //********** Outputs and lights ********** // Outputs for (int i = 0; i < 2; i++) { float knobVal = params[CV_PARAMS + cvMap[i][stepIndexes[i]]].value; float cv = 0.0f; int range = ranges[i]; if ( (i == 0 && (quantize & 0x1) != 0) || (i == 1 && (quantize > 1)) ) { cv = (knobVal * (float)(range * 2 + 1) - (float)range); cv = quantizeCV(cv); } else { int maxCV = (range == 0 ? 1 : (range * 5));// maxCV is [1, 5, 10] cv = knobVal * (float)(maxCV * 2) - (float)maxCV; } outputs[SEQ_OUTPUTS + i].value = cv; outputs[JUMP_OUTPUTS + i].value = jumpPulses[i].process((float)sampleTime); } // Blue and Yellow lights for (int i = 0; i < 16; i++) { lights[BLUE_LIGHTS + i].value = (stepIndexes[0] == i ? 1.0f : 0.0f); lights[YELLOW_LIGHTS + i].value = (stepIndexes[1] == i ? 1.0f : 0.0f); } // Reset light lights[RESET_LIGHT].value = resetLight; // Run light lights[RUN_LIGHT].value = running ? 1.0f : 0.0f; // State lights for (int i = 0; i < 2; i++) { lights[GLOBAL_LIGHTS + i].value = (states[i] & 0x1) == 0 ? 0.5f : 0.0f; lights[LOCAL_LIGHTS + i].value = states[i] >= 1 ? 0.5f : 0.0f; } // Leap, Plank, uncertainty and ResetOnRun lights lights[LEAP_LIGHT].value = leap ? 1.0f : 0.0f; lights[PLANK_LIGHT + 0].value = (quantize == 1) ? 1.0f : 0.0f;// Blue lights[PLANK_LIGHT + 1].value = (quantize == 2) ? 1.0f : 0.0f;// Yellow lights[PLANK_LIGHT + 2].value = (quantize == 3) ? 1.0f : 0.0f;// White lights[uncertainty_LIGHT].value = uncertainty ? 1.0f : 0.0f; lights[RESETONRUN_LIGHT].value = resetOnRun ? 1.0f : 0.0f; // Range lights for (int i = 0; i < 3; i++) { lights[OCTA_LIGHTS + i].value = (i <= ranges[0] ? 1.0f : 0.0f); lights[OCTB_LIGHTS + i].value = (i <= ranges[1] ? 1.0f : 0.0f); } // Jump lights for (int i = 0; i < 2; i++) { lights[JUMP_LIGHTS + i].value = jumpLights[i]; jumpLights[i] -= (jumpLights[i] / lightLambda) * sampleTime; } // Step clocks light if (stepClocksTrig) stepClocksLight = 1.0f; else stepClocksLight -= (stepClocksLight / lightLambda) * sampleTime; lights[STEPCLOCKS_LIGHT].value = stepClocksLight; if (clockIgnoreOnReset > 0l) clockIgnoreOnReset--; }// step() int stepElectron(int i, bool leap) { int jumped = 0; int base = stepIndexes[i] & 0x8;// 0 or 8 int step8 = stepIndexes[i] & 0x7;// 0 to 7 if ( (step8 == 7 || leap) && jumpRandom() ) { jumped = 1; base = 8 - base;// change atom } step8++; if (step8 > 7) step8 = 0; stepIndexes[i] = base | step8; return jumped; } }; struct IonsWidget : ModuleWidget { struct PanelThemeItem : MenuItem { Ions *module; int theme; void onAction(EventAction &e) override { module->panelTheme = theme; } void step() override { rightText = (module->panelTheme == theme) ? "✔" : ""; } }; Menu *createContextMenu() override { Menu *menu = ModuleWidget::createContextMenu(); MenuLabel *spacerLabel = new MenuLabel(); menu->addChild(spacerLabel); Ions *module = dynamic_cast(this->module); assert(module); MenuLabel *themeLabel = new MenuLabel(); themeLabel->text = "Panel Theme"; menu->addChild(themeLabel); PanelThemeItem *lightItem = new PanelThemeItem(); lightItem->text = lightPanelID;// Geodesics.hpp lightItem->module = module; lightItem->theme = 0; menu->addChild(lightItem); PanelThemeItem *darkItem = new PanelThemeItem(); darkItem->text = darkPanelID;// Geodesics.hpp darkItem->module = module; darkItem->theme = 1; //menu->addChild(darkItem); return menu; } IonsWidget(Ions *module) : ModuleWidget(module) { // Main panel from Inkscape DynamicSVGPanel *panel = new DynamicSVGPanel(); panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/IonsBG-01.svg"))); //panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/IonsBG-02.svg")));// no dark pannel for now box.size = panel->box.size; panel->mode = &module->panelTheme; addChild(panel); // Screws // part of svg panel, no code required float colRulerCenter = box.size.x / 2.0f; static constexpr float rowRulerAtomA = 125.5; static constexpr float rowRulerAtomB = 251.5f; static constexpr float radius1 = 21.0f; static constexpr float offset1 = 14.0f; static constexpr float radius2 = 35.0f; static constexpr float offset2 = 25.0f; static constexpr float radius3 = 61.0f; static constexpr float offset3 = 43.0f; // Outputs addOutput(createDynamicPort(Vec(colRulerCenter, rowRulerAtomA), Port::OUTPUT, module, Ions::SEQ_OUTPUTS + 0, &module->panelTheme)); addOutput(createDynamicPort(Vec(colRulerCenter, rowRulerAtomB), Port::OUTPUT, module, Ions::SEQ_OUTPUTS + 1, &module->panelTheme)); // CV knobs addParam(createDynamicParam(Vec(colRulerCenter, rowRulerAtomA + radius3 + 2.0f), module, Ions::CV_PARAMS + 0, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter + offset3, rowRulerAtomA + offset3), module, Ions::CV_PARAMS + 1, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter + radius3, rowRulerAtomA), module, Ions::CV_PARAMS + 2, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter + offset3, rowRulerAtomA - offset3), module, Ions::CV_PARAMS + 3, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter, rowRulerAtomA - radius3), module, Ions::CV_PARAMS + 4, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - offset3, rowRulerAtomA - offset3), module, Ions::CV_PARAMS + 5, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - radius3, rowRulerAtomA), module, Ions::CV_PARAMS + 6, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - offset3, rowRulerAtomA + offset3), module, Ions::CV_PARAMS + 7, 0.0f, 1.0f, 0.5f, &module->panelTheme)); // addParam(createDynamicParam(Vec(colRulerCenter + offset3, rowRulerAtomB - offset3), module, Ions::CV_PARAMS + 8, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter + radius3, rowRulerAtomB), module, Ions::CV_PARAMS + 9, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter + offset3, rowRulerAtomB + offset3), module, Ions::CV_PARAMS + 10, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter, rowRulerAtomB + radius3), module, Ions::CV_PARAMS + 11, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - offset3, rowRulerAtomB + offset3), module, Ions::CV_PARAMS + 12, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - radius3, rowRulerAtomB), module, Ions::CV_PARAMS + 13, 0.0f, 1.0f, 0.5f, &module->panelTheme)); addParam(createDynamicParam(Vec(colRulerCenter - offset3, rowRulerAtomB - offset3), module, Ions::CV_PARAMS + 14, 0.0f, 1.0f, 0.5f, &module->panelTheme)); // Prob knob and CV inuput float probX = colRulerCenter + 2.0f * offset3; float probY = rowRulerAtomA + radius3 + 2.0f; addParam(createDynamicParam(Vec(probX, probY), module, Ions::PROB_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); addInput(createDynamicPort(Vec(probX + 32.0f, probY), Port::INPUT, module, Ions::PROB_INPUT, &module->panelTheme)); // Jump pulses addOutput(createDynamicPort(Vec(probX + 18.0f, probY - 37.0f), Port::OUTPUT, module, Ions::JUMP_OUTPUTS + 0, &module->panelTheme)); addOutput(createDynamicPort(Vec(probX + 18.0f, probY + 37.0f), Port::OUTPUT, module, Ions::JUMP_OUTPUTS + 1, &module->panelTheme)); // Jump lights addChild(createLightCentered>(Vec(probX, probY - 46.0f), module, Ions::JUMP_LIGHTS + 0)); addChild(createLightCentered>(Vec(probX, probY + 46.0f), module, Ions::JUMP_LIGHTS + 1)); // Leap light and button addChild(createLightCentered>(Vec(colRulerCenter - 86.5f, 62.5f), module, Ions::LEAP_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter - 77.5f, 50.5f), module, Ions::LEAP_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // Plank light and button addChild(createLightCentered>(Vec(colRulerCenter + 86.5f, 62.5f), module, Ions::PLANK_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter + 77.5f, 50.5f), module, Ions::PLANK_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // Octave buttons and lights float octX = colRulerCenter + 107.0f; float octOffsetY = 10.0f; float octYA = rowRulerAtomA - octOffsetY; float octYB = rowRulerAtomB + octOffsetY; // top: addParam(createDynamicParam(Vec(octX, octYA), module, Ions::OCT_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme)); addChild(createLightCentered>(Vec(octX - 15.0f, octYA + 2.5f), module, Ions::OCTA_LIGHTS + 0)); addChild(createLightCentered>(Vec(octX - 12.0f, octYA - 8.0f), module, Ions::OCTA_LIGHTS + 1)); addChild(createLightCentered>(Vec(octX - 10.0f, octYA + 11.5f), module, Ions::OCTA_LIGHTS + 1)); addChild(createLightCentered>(Vec(octX - 3.0f, octYA - 13.5f), module, Ions::OCTA_LIGHTS + 2)); addChild(createLightCentered>(Vec(octX + 0.0f, octYA + 15.0f), module, Ions::OCTA_LIGHTS + 2)); // bottom: addParam(createDynamicParam(Vec(octX, octYB), module, Ions::OCT_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme)); addChild(createLightCentered>(Vec(octX - 15.0f, octYB - 2.5f), module, Ions::OCTB_LIGHTS + 0)); addChild(createLightCentered>(Vec(octX - 12.0f, octYB + 8.0f), module, Ions::OCTB_LIGHTS + 1)); addChild(createLightCentered>(Vec(octX - 10.0f, octYB - 11.5f), module, Ions::OCTB_LIGHTS + 1)); addChild(createLightCentered>(Vec(octX - 3.0f, octYB + 13.5f), module, Ions::OCTB_LIGHTS + 2)); addChild(createLightCentered>(Vec(octX + 0.0f, octYB - 15.0f), module, Ions::OCTB_LIGHTS + 2)); // Oct CV inputs addInput(createDynamicPort(Vec(octX - 9.0f, octYA - 34.5), Port::INPUT, module, Ions::OCTCV_INPUTS + 0, &module->panelTheme)); addInput(createDynamicPort(Vec(octX - 9.0f, octYB + 34.5), Port::INPUT, module, Ions::OCTCV_INPUTS + 1, &module->panelTheme)); // Blue electron lights // top blue addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomA + radius2), module, Ions::BLUE_LIGHTS + 0)); addChild(createLightCentered>(Vec(colRulerCenter + offset2, rowRulerAtomA + offset2), module, Ions::BLUE_LIGHTS + 1)); addChild(createLightCentered>(Vec(colRulerCenter + radius2, rowRulerAtomA), module, Ions::BLUE_LIGHTS + 2)); addChild(createLightCentered>(Vec(colRulerCenter + offset2, rowRulerAtomA - offset2), module, Ions::BLUE_LIGHTS + 3)); addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomA - radius2), module, Ions::BLUE_LIGHTS + 4)); addChild(createLightCentered>(Vec(colRulerCenter - offset2, rowRulerAtomA - offset2), module, Ions::BLUE_LIGHTS + 5)); addChild(createLightCentered>(Vec(colRulerCenter - radius2, rowRulerAtomA), module, Ions::BLUE_LIGHTS + 6)); addChild(createLightCentered>(Vec(colRulerCenter - offset2, rowRulerAtomA + offset2), module, Ions::BLUE_LIGHTS + 7)); // bottom blue addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomB - radius1), module, Ions::BLUE_LIGHTS + 8)); addChild(createLightCentered>(Vec(colRulerCenter + offset1, rowRulerAtomB - offset1), module, Ions::BLUE_LIGHTS + 9)); addChild(createLightCentered>(Vec(colRulerCenter + radius1, rowRulerAtomB), module, Ions::BLUE_LIGHTS + 10)); addChild(createLightCentered>(Vec(colRulerCenter + offset1, rowRulerAtomB + offset1), module, Ions::BLUE_LIGHTS + 11)); addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomB + radius1), module, Ions::BLUE_LIGHTS + 12)); addChild(createLightCentered>(Vec(colRulerCenter - offset1, rowRulerAtomB + offset1), module, Ions::BLUE_LIGHTS + 13)); addChild(createLightCentered>(Vec(colRulerCenter - radius1, rowRulerAtomB), module, Ions::BLUE_LIGHTS + 14)); addChild(createLightCentered>(Vec(colRulerCenter - offset1, rowRulerAtomB - offset1), module, Ions::BLUE_LIGHTS + 15)); // Yellow electron lights // bottom yellow addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomB - radius2), module, Ions::YELLOW_LIGHTS + 0)); addChild(createLightCentered>(Vec(colRulerCenter + offset2, rowRulerAtomB - offset2), module, Ions::YELLOW_LIGHTS + 1)); addChild(createLightCentered>(Vec(colRulerCenter + radius2, rowRulerAtomB), module, Ions::YELLOW_LIGHTS + 2)); addChild(createLightCentered>(Vec(colRulerCenter + offset2, rowRulerAtomB + offset2), module, Ions::YELLOW_LIGHTS + 3)); addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomB + radius2), module, Ions::YELLOW_LIGHTS + 4)); addChild(createLightCentered>(Vec(colRulerCenter - offset2, rowRulerAtomB + offset2), module, Ions::YELLOW_LIGHTS + 5)); addChild(createLightCentered>(Vec(colRulerCenter - radius2, rowRulerAtomB), module, Ions::YELLOW_LIGHTS + 6)); addChild(createLightCentered>(Vec(colRulerCenter - offset2, rowRulerAtomB - offset2), module, Ions::YELLOW_LIGHTS + 7)); // top yellow addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomA + radius1), module, Ions::YELLOW_LIGHTS + 8)); addChild(createLightCentered>(Vec(colRulerCenter + offset1, rowRulerAtomA + offset1), module, Ions::YELLOW_LIGHTS + 9)); addChild(createLightCentered>(Vec(colRulerCenter + radius1, rowRulerAtomA), module, Ions::YELLOW_LIGHTS + 10)); addChild(createLightCentered>(Vec(colRulerCenter + offset1, rowRulerAtomA - offset1), module, Ions::YELLOW_LIGHTS + 11)); addChild(createLightCentered>(Vec(colRulerCenter, rowRulerAtomA - radius1), module, Ions::YELLOW_LIGHTS + 12)); addChild(createLightCentered>(Vec(colRulerCenter - offset1, rowRulerAtomA - offset1), module, Ions::YELLOW_LIGHTS + 13)); addChild(createLightCentered>(Vec(colRulerCenter - radius1, rowRulerAtomA), module, Ions::YELLOW_LIGHTS + 14)); addChild(createLightCentered>(Vec(colRulerCenter - offset1, rowRulerAtomA + offset1), module, Ions::YELLOW_LIGHTS + 15)); // Run jack, light and button static constexpr float rowRulerRunJack = 344.5f; static constexpr float offsetRunJackX = 119.5f; addInput(createDynamicPort(Vec(colRulerCenter - offsetRunJackX, rowRulerRunJack), Port::INPUT, module, Ions::RUN_INPUT, &module->panelTheme)); addChild(createLightCentered>(Vec(colRulerCenter - offsetRunJackX + 18.0f, rowRulerRunJack), module, Ions::RUN_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter - offsetRunJackX + 33.0f, rowRulerRunJack), module, Ions::RUN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // Reset jack, light and button addInput(createDynamicPort(Vec(colRulerCenter + offsetRunJackX, rowRulerRunJack), Port::INPUT, module, Ions::RESET_INPUT, &module->panelTheme)); addChild(createLightCentered>(Vec(colRulerCenter + offsetRunJackX - 18.0f, rowRulerRunJack), module, Ions::RESET_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter + offsetRunJackX - 33.0f, rowRulerRunJack), module, Ions::RESET_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); static constexpr float offsetMagneticButton = 42.5f; // Magnetic clock (step clocks) addChild(createLightCentered>(Vec(colRulerCenter - offsetMagneticButton - 15.0f, rowRulerRunJack), module, Ions::STEPCLOCKS_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter - offsetMagneticButton, rowRulerRunJack), module, Ions::STEPCLOCKS_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // Reset on Run light and button addChild(createLightCentered>(Vec(colRulerCenter + offsetMagneticButton + 15.0f, rowRulerRunJack), module, Ions::RESETONRUN_LIGHT)); addParam(createDynamicParam(Vec(colRulerCenter + offsetMagneticButton, rowRulerRunJack), module, Ions::RESETONRUN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // Globak clock float gclkX = colRulerCenter - 2.0f * offset3; float gclkY = rowRulerAtomA + radius3 + 2.0f; addInput(createDynamicPort(Vec(gclkX, gclkY), Port::INPUT, module, Ions::CLK_INPUT, &module->panelTheme)); // global lights addChild(createLightCentered>(Vec(gclkX - 12.0f, gclkY - 20.0f), module, Ions::GLOBAL_LIGHTS + 0)); addChild(createLightCentered>(Vec(gclkX - 12.0f, gclkY + 20.0f), module, Ions::GLOBAL_LIGHTS + 1)); // state buttons addParam(createDynamicParam(Vec(gclkX - 17.0f, gclkY - 34.0f), module, Ions::STATE_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme)); addParam(createDynamicParam(Vec(gclkX - 17.0f, gclkY + 34.0f), module, Ions::STATE_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme)); // local lights addChild(createLightCentered>(Vec(gclkX - 20.0f, gclkY - 48.5f), module, Ions::LOCAL_LIGHTS + 0)); addChild(createLightCentered>(Vec(gclkX - 20.0f, gclkY + 48.5f), module, Ions::LOCAL_LIGHTS + 1)); // local inputs addInput(createDynamicPort(Vec(gclkX - 21.0f, gclkY - 72.0f), Port::INPUT, module, Ions::CLK_INPUTS + 0, &module->panelTheme)); addInput(createDynamicPort(Vec(gclkX - 21.0f, gclkY + 72.0f), Port::INPUT, module, Ions::CLK_INPUTS + 1, &module->panelTheme)); // state inputs addInput(createDynamicPort(Vec(gclkX - 11.0f, gclkY - 107.0f), Port::INPUT, module, Ions::STATECV_INPUTS + 0, &module->panelTheme)); addInput(createDynamicPort(Vec(gclkX - 11.0f, gclkY + 107.0f), Port::INPUT, module, Ions::STATECV_INPUTS + 1, &module->panelTheme)); // uncertainty light and button addChild(createLightCentered>(Vec(gclkX - 20.0f, gclkY), module, Ions::uncertainty_LIGHT)); addParam(createDynamicParam(Vec(gclkX - 34.0f, gclkY), module, Ions::uncertainty_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme)); } }; } // namespace rack_plugin_Geodesics using namespace rack_plugin_Geodesics; RACK_PLUGIN_MODEL_INIT(Geodesics, Ions) { Model *modelIons = Model::create("Geodesics", "Ions", "Ions", SEQUENCER_TAG); return modelIons; } /*CHANGE LOG 0.6.1: Ions reloaded (many changes) 0.6.0: created */