#include "dsp/digital.hpp" #include "moDllz.hpp" /* * TwinGlider */ namespace rack_plugin_moDllz { struct TwinGlider : Module { enum ParamIds { RISE_PARAM, FALL_PARAM=2, LINK_PARAM=4, RISEMODE_PARAM=6, FALLMODE_PARAM=8, TRIG_PARAM=10, SMPNGLIDE_PARAM=12, NUM_PARAMS=14 }; enum InputIds { RISE_INPUT, FALL_INPUT=2, GATE_INPUT=4, CLOCK_INPUT=6, IN_INPUT=8, NUM_INPUTS=10 }; enum OutputIds { TRIGRISE_OUTPUT, TRIG_OUTPUT=2, TRIGFALL_OUTPUT=4, GATERISE_OUTPUT=6, GATEFALL_OUTPUT=8, OUT_OUTPUT=10, NUM_OUTPUTS=12 }; enum LightIds { RISING_LIGHT, FALLING_LIGHT=2, NUM_LIGHTS=4 }; struct gliderObj{ float out = 0.0f; float in = 0.0f; // float jump = 0.0f; int risemode = 0.0f; int fallmode = 0.0f; float riseval = 0.0f; float fallval = 0.0f; float prevriseval = 0.0f; float prevfallval = 0.0f; float riseramp = 0.0f; float fallramp = 0.0f; bool rising = false; bool falling = false; bool newgate = false; bool pulse = false; bool newin = false; bool trigR = false; bool trigF = false; int clocksafe = 0; // int frameTime = 0; PulseGenerator risePulse; PulseGenerator fallPulse; }; gliderObj glider[2]; const float threshold = 0.01f; // int testVal = 0; TwinGlider() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} // ~TwinGlider() { // }; void step() override; void onReset() override { for (int ix = 0; ix < 2 ; ix++){ // gliding[ix] = false; outputs[OUT_OUTPUT + ix].value = inputs[IN_INPUT + ix].value; lights[RISING_LIGHT + ix].value = 0; lights[FALLING_LIGHT + ix].value = 0; outputs[GATERISE_OUTPUT + ix].value = 0; outputs[GATEFALL_OUTPUT + ix].value = 0; } } }; /////////////////////////////////////////// ///////////////STEP ////////////////// ///////////////////////////////////////////// void TwinGlider::step() { for (int ix = 0; ix < 2; ix++){ if (inputs[IN_INPUT + ix].active) { if (std::abs(glider[ix].in - inputs[IN_INPUT + ix].value) > threshold){ glider[ix].newin = true; } if (params[SMPNGLIDE_PARAM + ix].value > 0.5f){ bool allowin = false; if (inputs[CLOCK_INPUT + ix].active){ // External clock if ((glider[ix].clocksafe > 8) && (inputs[CLOCK_INPUT + ix].value > 2.5f)){ allowin = true; glider[ix].in = inputs[IN_INPUT + ix].value; glider[ix].clocksafe = 0; }else if ((glider[ix].clocksafe <10) && (inputs[CLOCK_INPUT + ix].value < 0.01f)) glider[ix].clocksafe ++; } else allowin = ((!glider[ix].rising) && (!glider[ix].falling)); if (allowin) if (glider[ix].newin) glider[ix].in = inputs[IN_INPUT + ix].value; } else if (glider[ix].newin) glider[ix].in = inputs[IN_INPUT + ix].value; //Check for legato from Gate bool glideMe = false; if (inputs[GATE_INPUT + ix].active){ if (inputs[GATE_INPUT + ix ].value < 0.5f) { glider[ix].newgate = true; glider[ix].out = glider[ix].in; }else if (glider[ix].newgate) { glider[ix].out = glider[ix].in ; glider[ix].newgate = false; }else glideMe= true;/// GLIDE !!!!! }else glideMe = true;//// GLIDE !!!! if (glideMe) { //////////////// GLIDE FUNCTION ////////////>>>>>>>>>>>>>>>>>>>>> glider[ix].risemode = static_cast (params[RISEMODE_PARAM + ix].value); glider[ix].fallmode = static_cast (params[FALLMODE_PARAM + ix].value); if (glider[ix].in > glider[ix].out){ if (inputs[RISE_INPUT + ix].active) glider[ix].riseval = inputs[RISE_INPUT + ix].value / 10.0f * params[RISE_PARAM + ix].value; else glider[ix].riseval = params[RISE_PARAM + ix].value; if (glider[ix].riseval > 0.0f) { switch (glider[ix].risemode) { case 0: /// Hi Rate glider[ix].riseramp = 1.0f/(1.0f + glider[ix].riseval * 0.005f * engineGetSampleRate()); break; case 1: /// Rate glider[ix].riseramp = 1.0f/(1.0f + glider[ix].riseval * 2.0f * engineGetSampleRate()); break; case 2: /// Time if ((glider[ix].newin)||(glider[ix].riseval != glider[ix].prevriseval)){ glider[ix].prevriseval = glider[ix].riseval; glider[ix].newin = false; glider[ix].riseramp = (glider[ix].in - glider[ix].out) * engineGetSampleTime() /(glider[ix].riseval * glider[ix].riseval * 10.0f); if (glider[ix].riseramp< 1e-6) glider[ix].riseramp = 1e-6; } break; default: break; } glider[ix].out += glider[ix].riseramp; glider[ix].rising = true; glider[ix].falling = false; if (glider[ix].out >= glider[ix].in) {///////REACH RISE glider[ix].out = glider[ix].in; glider[ix].rising = false; glider[ix].trigR = true; } } else { glider[ix].rising = false; glider[ix].out = glider[ix].in; glider[ix].trigR = true; } } else if (glider[ix].in < glider[ix].out){ if (params[LINK_PARAM + ix].value > 0.5f) { glider[ix].fallmode = glider[ix].risemode; glider[ix].fallval = glider[ix].riseval; }else{ if (inputs[FALL_INPUT + ix].active) glider[ix].fallval = inputs[FALL_INPUT + ix].value / 10.0f * params[FALL_PARAM + ix].value; else glider[ix].fallval = params[FALL_PARAM + ix].value; } if (glider[ix].fallval > 0.0f) { switch (glider[ix].fallmode) { case 0: glider[ix].fallramp = 1.0f/(1.0f + glider[ix].fallval * 0.005f * engineGetSampleRate()); break; case 1: glider[ix].fallramp = 1.0f/(1.0f + glider[ix].fallval * 2.0f * engineGetSampleRate()); break; case 2: if ((glider[ix].newin) || (glider[ix].fallval != glider[ix].prevfallval)){ glider[ix].prevfallval = glider[ix].fallval; glider[ix].newin = false; glider[ix].fallramp = (glider[ix].out - glider[ix].in) * engineGetSampleTime() /(glider[ix].fallval * glider[ix].fallval * 10.0f); if (glider[ix].fallramp < 1e-6) glider[ix].fallramp= 1e-6; } break; default: break; } glider[ix].out -= glider[ix].fallramp; glider[ix].falling = true; glider[ix].rising = false; if (glider[ix].out <= glider[ix].in){////////REACH FALL glider[ix].out = glider[ix].in; glider[ix].falling = false; glider[ix].trigF = true; } }else{ glider[ix].falling = false; glider[ix].out = glider[ix].in; glider[ix].trigF = true; } } else { glider[ix].rising = false; glider[ix].falling = false; glider[ix].out = glider[ix].in; } }else{ glider[ix].rising = false; glider[ix].falling = false; glider[ix].out = glider[ix].in; } ///testVal = static_cast (gliding[0] * 10^7); lights[RISING_LIGHT + ix].value = glider[ix].rising? 1.0 : 0.0f; lights[FALLING_LIGHT + ix].value = glider[ix].falling? 1.0 : 0.0f; outputs[GATERISE_OUTPUT + ix].value = glider[ix].rising? 10.0 : 0.0f; outputs[GATEFALL_OUTPUT + ix].value = glider[ix].falling? 10.0 : 0.0f; outputs[OUT_OUTPUT + ix].value = glider[ix].out; // //// do triggers and reset flags if (outputs[TRIGRISE_OUTPUT + ix].active||outputs[TRIG_OUTPUT + ix].active||outputs[TRIGFALL_OUTPUT + ix].active) { if (glider[ix].trigR) { glider[ix].risePulse.trigger(1e-3f); glider[ix].trigR = false; } if (glider[ix].trigF) { glider[ix].fallPulse.trigger(1e-3f); glider[ix].trigF = false; } // bool pulseR = glider[ix].risePulse.process(1.0f / engineGetSampleRate()); outputs[TRIGRISE_OUTPUT + ix].value = pulseR ? 10.0 : 0.0; bool pulseF = glider[ix].fallPulse.process(1.0f / engineGetSampleRate()); outputs[TRIGFALL_OUTPUT + ix].value = pulseF ? 10.0 : 0.0; outputs[TRIG_OUTPUT + ix].value = (pulseR || pulseF) ? 10.0 : 0.0; // } ///else { // trigR[ix] = false; // trigF[ix] = false; // } /// else from if input ACTIVE.... }else{ //disconnected in...reset Output if connected... // outputs[OUT_OUTPUT + ix].value = 0; outputs[GATERISE_OUTPUT + ix].value = 0.0f; outputs[GATEFALL_OUTPUT + ix].value = 0.0f; lights[RISING_LIGHT + ix].value = 0.0f; lights[FALLING_LIGHT + ix].value = 0.0f; glider[ix].out = 0.0f; glider[ix].in = 0.0f; // gliding[ix] = false; glider[ix].newgate = false; // allowin[ix] = true; }/// Closing if input ACTIVE } //for loop ix }//closing STEP ///////////////////////////////////////////// ///// TEST DISPLAY ////// //struct testDisplay : TransparentWidget { // testDisplay() { // font = Font::load(FONT_FILE); // } // float mdfontSize = 16.f; // std::shared_ptr font; // std::string displayedVal; // int *valP; // int val = 0; // void draw(NVGcontext* vg) // { // val = *valP; // displayedVal = std::to_string(val); // nvgFillColor(vg, nvgRGB(0xFF,0xFF,0x00)); // nvgFontSize(vg, mdfontSize); // //nvgTextLetterSpacing(vg, 2.0f); // //nvgTextAlign(vg, NVG_ALIGN_CENTER); // nvgTextBox(vg, 0.0f, 20.0f, 165.0f, displayedVal.c_str(), NULL); // // } // //}; //////////////////////////////////////////// /////////////////////////////////////////// struct TwinGliderWidget : ModuleWidget { TwinGliderWidget(TwinGlider *module): ModuleWidget(module){ setPanel(SVG::load(assetPlugin(plugin, "res/TwinGlider.svg"))); //Screws addChild(Widget::create(Vec(0, 0))); addChild(Widget::create(Vec(box.size.x - 15, 0))); addChild(Widget::create(Vec(0, 365))); addChild(Widget::create(Vec(box.size.x - 15, 365))); float Ystep = 30.0f ; float Ypos = 0.0f; for (int i=0; i<2; i++){ Ypos = 25.0f + static_cast(i* 173); //2nd panel offset // Gliding Leds addChild(ModuleLightWidget::create>(Vec(6.75, Ypos+1), module, TwinGlider::RISING_LIGHT + i)); addChild(ModuleLightWidget::create>(Vec(154.75, Ypos+1), module, TwinGlider::FALLING_LIGHT + i)); /// Glide Knobs addParam(ParamWidget::create(Vec(19, Ypos-4), module, TwinGlider::RISE_PARAM + i, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(102, Ypos-4), module, TwinGlider::FALL_PARAM + i, 0.0, 1.0, 0.0)); Ypos += Ystep; //55 // LINK SWITCH//CKSS addParam(ParamWidget::create(Vec(73, Ypos-12), module, TwinGlider::LINK_PARAM + i, 0.0, 1.0, 1.0)); /// Glides CVs addInput(Port::create(Vec(23, Ypos+5.5), Port::INPUT, module, TwinGlider::RISE_INPUT + i)); addInput(Port::create(Vec(117.5, Ypos+5.5), Port::INPUT, module, TwinGlider::FALL_INPUT + i)); Ypos += Ystep; //85 /// Mode switches addParam(ParamWidget::create(Vec(55, Ypos-7), module, TwinGlider::RISEMODE_PARAM + i, 0.0, 2.0, 2.0)); addParam(ParamWidget::create(Vec(100, Ypos-7), module, TwinGlider::FALLMODE_PARAM + i, 0.0, 2.0, 2.0)); /// GATES OUT addOutput(Port::create(Vec(10.5, Ypos+14), Port::OUTPUT, module, TwinGlider::GATERISE_OUTPUT + i)); addOutput(Port::create(Vec(130.5, Ypos+14), Port::OUTPUT, module, TwinGlider::GATEFALL_OUTPUT + i)); Ypos += Ystep; //115 /// TRIGGERS OUT addOutput(Port::create(Vec(43, Ypos-4.5), Port::OUTPUT, module, TwinGlider::TRIGRISE_OUTPUT + i)); addOutput(Port::create(Vec(71, Ypos-4.5), Port::OUTPUT, module, TwinGlider::TRIG_OUTPUT + i)); addOutput(Port::create(Vec(98, Ypos-4.5), Port::OUTPUT, module, TwinGlider::TRIGFALL_OUTPUT + i)); Ypos += Ystep; //145 // GATE IN addInput(Port::create(Vec(44, Ypos+7), Port::INPUT, module, TwinGlider::GATE_INPUT + i)); // CLOCK IN addInput(Port::create(Vec(75, Ypos+7), Port::INPUT, module, TwinGlider::CLOCK_INPUT + i)); // Sample&Glide SWITCH addParam(ParamWidget::create(Vec(108, Ypos+19), module, TwinGlider::SMPNGLIDE_PARAM + i, 0.0, 1.0, 0.0)); // IN OUT addInput(Port::create(Vec(13.5, Ypos+6.5), Port::INPUT, module, TwinGlider::IN_INPUT + i)); addOutput(Port::create(Vec(128.5, Ypos+6.5), Port::OUTPUT, module, TwinGlider::OUT_OUTPUT + i)); } // { // testDisplay *mDisplay = new testDisplay(); // mDisplay->box.pos = Vec(0.0f, 360.0f); // mDisplay->box.size = {165.0f, 20.0f}; // mDisplay->valP = &(module->testVal); // addChild(mDisplay); // } } }; //void TwinGliderWidget::step() { // ModuleWidget::step(); //} // Specify the Module and ModuleWidget subclass, human-readable // manufacturer name for categorization, module slug (should never // change), human-readable module name, and any number of tags // (found in `include/tags.hpp`) separated by commas. } // namespace rack_plugin_moDllz using namespace rack_plugin_moDllz; RACK_PLUGIN_MODEL_INIT(moDllz, TwinGlider) { Model *modelTwinGlider = Model::create("moDllz", "TwinGlider", "TwinGlider Dual Portamento", SLEW_LIMITER_TAG, DUAL_TAG, ENVELOPE_FOLLOWER_TAG); return modelTwinGlider; }