#include "Gratrix.hpp" #include "dsp/digital.hpp" namespace rack_plugin_Gratrix { //============================================================================================================ struct ADSR : MicroModule { enum ParamIds { ATTACK_PARAM, DECAY_PARAM, SUSTAIN_PARAM, RELEASE_PARAM, NUM_PARAMS }; enum InputIds { ATTACK_INPUT, // 1 DECAY_INPUT, // 1 SUSTAIN_INPUT, // 1 RELEASE_INPUT, // 1 GATE_INPUT, // N+1 TRIG_INPUT, // N+1 NUM_INPUTS, OFF_INPUTS = GATE_INPUT }; enum OutputIds { ENVELOPE_OUTPUT, // N INVERTED_OUTPUT, // N NUM_OUTPUTS, OFF_OUTPUTS = ENVELOPE_OUTPUT }; enum LightIds { NUM_LIGHTS }; bool decaying = false; float env = 0.0f; SchmittTrigger trigger; ADSR() : MicroModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step(); }; //============================================================================================================ void ADSR::step() { float attack = clamp(params[ATTACK_INPUT].value + inputs[ATTACK_INPUT].value / 10.0f, 0.0f, 1.0f); float decay = clamp(params[DECAY_PARAM].value + inputs[DECAY_INPUT].value / 10.0f, 0.0f, 1.0f); float sustain = clamp(params[SUSTAIN_PARAM].value + inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f); float release = clamp(params[RELEASE_PARAM].value + inputs[RELEASE_PARAM].value / 10.0f, 0.0f, 1.0f); // Gate and trigger bool gated = inputs[GATE_INPUT].value >= 1.0f; if (trigger.process(inputs[TRIG_INPUT].value)) decaying = false; const float base = 20000.0f; const float maxTime = 10.0f; if (gated) { if (decaying) { // Decay if (decay < 1e-4) { env = sustain; } else { env += powf(base, 1 - decay) / maxTime * (sustain - env) * engineGetSampleTime(); } } else { // Attack // Skip ahead if attack is all the way down (infinitely fast) if (attack < 1e-4) { env = 1.0f; } else { env += powf(base, 1 - attack) / maxTime * (1.01f - env) * engineGetSampleTime(); } if (env >= 1.0f) { env = 1.0f; decaying = true; } } } else { // Release if (release < 1e-4) { env = 0.0f; } else { env += powf(base, 1 - release) / maxTime * (0.0f - env) * engineGetSampleTime(); } decaying = false; } outputs[ENVELOPE_OUTPUT].value = 10.0 * env; outputs[INVERTED_OUTPUT].value = 10.0 * (1.0 - env); } //============================================================================================================ struct GtxModule_ADSR : Module { std::array inst; GtxModule_ADSR() : Module(ADSR::NUM_PARAMS, (GTX__N+1) * (ADSR::NUM_INPUTS - ADSR::OFF_INPUTS ) + ADSR::OFF_INPUTS, (GTX__N ) * (ADSR::NUM_OUTPUTS - ADSR::OFF_OUTPUTS) + ADSR::OFF_OUTPUTS) {} static constexpr std::size_t imap(std::size_t port, std::size_t bank) { return (port < ADSR::OFF_INPUTS) ? port : port + bank * (ADSR::NUM_INPUTS - ADSR::OFF_INPUTS); } static constexpr std::size_t omap(std::size_t port, std::size_t bank) { // return (port < ADSR::OFF_OUTPUTS) ? port : port + bank * (ADSR::NUM_OUTPUTS - ADSR::OFF_OUTPUTS); return port + bank * ADSR::NUM_OUTPUTS; } void step() override { for (std::size_t i=0; i(Vec(15, 0))); addChild(Widget::create(Vec(box.size.x-30, 0))); addChild(Widget::create(Vec(15, 365))); addChild(Widget::create(Vec(box.size.x-30, 365))); addParam(createParamGTX(Vec(fx(0+0.18), fy(-0.28)), module, ADSR::ATTACK_PARAM, 0.0, 1.0, 0.5)); addParam(createParamGTX(Vec(fx(1+0.18), fy(-0.28)), module, ADSR::DECAY_PARAM, 0.0, 1.0, 0.5)); addParam(createParamGTX(Vec(fx(0+0.18), fy(+0.28)), module, ADSR::SUSTAIN_PARAM, 0.0, 1.0, 0.5)); addParam(createParamGTX(Vec(fx(1+0.18), fy(+0.28)), module, ADSR::RELEASE_PARAM, 0.0, 1.0, 0.5)); addInput(createInputGTX(Vec(fx(0-0.28), fy(-0.28)), module, ADSR::ATTACK_INPUT)); addInput(createInputGTX(Vec(fx(1-0.28), fy(-0.28)), module, ADSR::DECAY_INPUT)); addInput(createInputGTX(Vec(fx(0-0.28), fy(+0.28)), module, ADSR::SUSTAIN_INPUT)); addInput(createInputGTX(Vec(fx(1-0.28), fy(+0.28)), module, ADSR::RELEASE_INPUT)); for (std::size_t i=0; i(Vec(px(0, i), py(1, i)), module, GtxModule_ADSR::imap(ADSR::GATE_INPUT, i))); addInput(createInputGTX(Vec(px(0, i), py(2, i)), module, GtxModule_ADSR::imap(ADSR::TRIG_INPUT, i))); addOutput(createOutputGTX(Vec(px(1, i), py(1, i)), module, GtxModule_ADSR::omap(ADSR::ENVELOPE_OUTPUT, i))); addOutput(createOutputGTX(Vec(px(1, i), py(2, i)), module, GtxModule_ADSR::omap(ADSR::INVERTED_OUTPUT, i))); } addInput(createInputGTX(Vec(gx(0), gy(1)), module, GtxModule_ADSR::imap(ADSR::GATE_INPUT, GTX__N))); addInput(createInputGTX(Vec(gx(0), gy(2)), module, GtxModule_ADSR::imap(ADSR::TRIG_INPUT, GTX__N))); } }; } // namespace rack_plugin_Gratrix using namespace rack_plugin_Gratrix; RACK_PLUGIN_MODEL_INIT(Gratrix, ADSR_F1) { Model *model = Model::create("Gratrix", "ADSR-F1", "ADSR-F1", ENVELOPE_GENERATOR_TAG); return model; }