| @@ -1,12 +1,7 @@ | |||
| #include "plugin.hpp" | |||
| using namespace simd; | |||
| const float MIN_TIME = 1e-3f; | |||
| const float MAX_TIME = 10.f; | |||
| const float LAMBDA_BASE = MAX_TIME / MIN_TIME; | |||
| using simd::float_4; | |||
| struct ADSR : Module { | |||
| @@ -15,6 +10,12 @@ struct ADSR : Module { | |||
| DECAY_PARAM, | |||
| SUSTAIN_PARAM, | |||
| RELEASE_PARAM, | |||
| // added in 2.0 | |||
| ATTACK_CV_PARAM, | |||
| DECAY_CV_PARAM, | |||
| SUSTAIN_CV_PARAM, | |||
| RELEASE_CV_PARAM, | |||
| PUSH_PARAM, | |||
| NUM_PARAMS | |||
| }; | |||
| enum InputIds { | |||
| @@ -23,7 +24,7 @@ struct ADSR : Module { | |||
| SUSTAIN_INPUT, | |||
| RELEASE_INPUT, | |||
| GATE_INPUT, | |||
| TRIG_INPUT, | |||
| RETRIG_INPUT, | |||
| NUM_INPUTS | |||
| }; | |||
| enum OutputIds { | |||
| @@ -35,17 +36,22 @@ struct ADSR : Module { | |||
| DECAY_LIGHT, | |||
| SUSTAIN_LIGHT, | |||
| RELEASE_LIGHT, | |||
| PUSH_LIGHT, | |||
| NUM_LIGHTS | |||
| }; | |||
| float_4 attacking[4] = {float_4::zero()}; | |||
| float_4 env[4] = {0.f}; | |||
| static constexpr float MIN_TIME = 1e-3f; | |||
| static constexpr float MAX_TIME = 10.f; | |||
| static constexpr float LAMBDA_BASE = MAX_TIME / MIN_TIME; | |||
| float_4 attacking[4] = {}; | |||
| float_4 env[4] = {}; | |||
| dsp::TSchmittTrigger<float_4> trigger[4]; | |||
| dsp::ClockDivider cvDivider; | |||
| float_4 attackLambda[4] = {0.f}; | |||
| float_4 decayLambda[4] = {0.f}; | |||
| float_4 releaseLambda[4] = {0.f}; | |||
| float_4 sustain[4] = {0.f}; | |||
| float_4 attackLambda[4] = {}; | |||
| float_4 decayLambda[4] = {}; | |||
| float_4 releaseLambda[4] = {}; | |||
| float_4 sustain[4] = {}; | |||
| dsp::ClockDivider lightDivider; | |||
| ADSR() { | |||
| @@ -54,12 +60,21 @@ struct ADSR : Module { | |||
| configParam(DECAY_PARAM, 0.f, 1.f, 0.5f, "Decay", " ms", LAMBDA_BASE, MIN_TIME * 1000); | |||
| configParam(SUSTAIN_PARAM, 0.f, 1.f, 0.5f, "Sustain", "%", 0, 100); | |||
| configParam(RELEASE_PARAM, 0.f, 1.f, 0.5f, "Release", " ms", LAMBDA_BASE, MIN_TIME * 1000); | |||
| configParam(ATTACK_CV_PARAM, -1.f, 1.f, 0.f, "Attack CV", "%", 0, 100); | |||
| configParam(DECAY_CV_PARAM, -1.f, 1.f, 0.f, "Decay CV", "%", 0, 100); | |||
| configParam(SUSTAIN_CV_PARAM, -1.f, 1.f, 0.f, "Sustain CV", "%", 0, 100); | |||
| configParam(RELEASE_CV_PARAM, -1.f, 1.f, 0.f, "Release CV", "%", 0, 100); | |||
| configButton(PUSH_PARAM, "Push"); | |||
| configInput(ATTACK_INPUT, "Attack"); | |||
| configInput(DECAY_INPUT, "Decay"); | |||
| configInput(SUSTAIN_INPUT, "Sustain"); | |||
| configInput(RELEASE_INPUT, "Release"); | |||
| configInput(GATE_INPUT, "Gate"); | |||
| configInput(TRIG_INPUT, "Retrigger"); | |||
| configInput(RETRIG_INPUT, "Retrigger"); | |||
| configOutput(ENVELOPE_OUTPUT, "Envelope"); | |||
| cvDivider.setDivision(16); | |||
| @@ -71,7 +86,7 @@ struct ADSR : Module { | |||
| // 0.23 us serial with all lambdas computed | |||
| // 0.15-0.18 us serial with all lambdas computed with SSE | |||
| int channels = inputs[GATE_INPUT].getChannels(); | |||
| int channels = std::max(1, inputs[GATE_INPUT].getChannels()); | |||
| // Compute lambdas | |||
| if (cvDivider.process()) { | |||
| @@ -80,12 +95,17 @@ struct ADSR : Module { | |||
| float sustainParam = params[SUSTAIN_PARAM].getValue(); | |||
| float releaseParam = params[RELEASE_PARAM].getValue(); | |||
| float attackCvParam = params[ATTACK_CV_PARAM].getValue(); | |||
| float decayCvParam = params[DECAY_CV_PARAM].getValue(); | |||
| float sustainCvParam = params[SUSTAIN_CV_PARAM].getValue(); | |||
| float releaseCvParam = params[RELEASE_CV_PARAM].getValue(); | |||
| for (int c = 0; c < channels; c += 4) { | |||
| // CV | |||
| float_4 attack = attackParam + inputs[ATTACK_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f; | |||
| float_4 decay = decayParam + inputs[DECAY_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f; | |||
| float_4 sustain = sustainParam + inputs[SUSTAIN_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f; | |||
| float_4 release = releaseParam + inputs[RELEASE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f; | |||
| float_4 attack = attackParam + inputs[ATTACK_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * attackCvParam; | |||
| float_4 decay = decayParam + inputs[DECAY_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * decayCvParam; | |||
| float_4 sustain = sustainParam + inputs[SUSTAIN_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * sustainCvParam; | |||
| float_4 release = releaseParam + inputs[RELEASE_INPUT].getPolyVoltageSimd<float_4>(c) / 10.f * releaseCvParam; | |||
| attack = simd::clamp(attack, 0.f, 1.f); | |||
| decay = simd::clamp(decay, 0.f, 1.f); | |||
| @@ -99,14 +119,19 @@ struct ADSR : Module { | |||
| } | |||
| } | |||
| float_4 gate[4]; | |||
| float_4 gate[4] = {}; | |||
| bool push = params[PUSH_PARAM].getValue(); | |||
| for (int c = 0; c < channels; c += 4) { | |||
| // Gate | |||
| gate[c / 4] = inputs[GATE_INPUT].getVoltageSimd<float_4>(c) >= 1.f; | |||
| if (push) { | |||
| gate[c / 4] = float_4::mask(); | |||
| } | |||
| // Retrigger | |||
| float_4 triggered = trigger[c / 4].process(inputs[TRIG_INPUT].getPolyVoltageSimd<float_4>(c)); | |||
| float_4 triggered = trigger[c / 4].process(inputs[RETRIG_INPUT].getPolyVoltageSimd<float_4>(c)); | |||
| attacking[c / 4] = simd::ifelse(triggered, float_4::mask(), attacking[c / 4]); | |||
| // Get target and lambda for exponential decay | |||
| @@ -150,7 +175,41 @@ struct ADSR : Module { | |||
| if (simd::movemask(~gate[c / 4] & ~resting)) | |||
| lights[RELEASE_LIGHT].setBrightness(1); | |||
| } | |||
| // Push button light | |||
| bool anyGate = false; | |||
| for (int c = 0; c < channels; c += 4) | |||
| anyGate = anyGate || simd::movemask(gate[c / 4]); | |||
| lights[PUSH_LIGHT].setBrightness(anyGate); | |||
| } | |||
| } | |||
| }; | |||
| struct ADSRDisplay : LedDisplay { | |||
| ADSR* module; | |||
| void drawLayer(const DrawArgs& args, int layer) override { | |||
| if (layer == 1) { | |||
| Rect r = box.zeroPos().shrink(Vec(2, 4)); | |||
| Vec s = r.getTopLeft(); | |||
| Vec e = r.getBottomRight(); | |||
| float decayX = 20.0; | |||
| float releaseX = 80.0; | |||
| float releaseY = 40.0; | |||
| nvgBeginPath(args.vg); | |||
| nvgMoveTo(args.vg, s.x, e.y); | |||
| nvgBezierTo(args.vg, s.x, s.y, decayX, s.y, decayX, s.y); | |||
| nvgBezierTo(args.vg, decayX, releaseY, releaseX, releaseY, releaseX, releaseY); | |||
| nvgBezierTo(args.vg, releaseX, e.y, e.x, e.y, e.x, e.y); | |||
| nvgLineCap(args.vg, NVG_ROUND); | |||
| nvgMiterLimit(args.vg, 2.f); | |||
| nvgStrokeWidth(args.vg, 1.5f); | |||
| nvgStrokeColor(args.vg, SCHEME_YELLOW); | |||
| nvgStroke(args.vg); | |||
| } | |||
| LedDisplay::drawLayer(args, layer); | |||
| } | |||
| }; | |||
| @@ -165,27 +224,29 @@ struct ADSRWidget : ModuleWidget { | |||
| addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
| addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); | |||
| addParam(createParamCentered<LEDSlider>(mm2px(Vec(6.604, 55.454)), module, ADSR::ATTACK_PARAM)); | |||
| addParam(createParamCentered<LEDSlider>(mm2px(Vec(17.441, 55.454)), module, ADSR::DECAY_PARAM)); | |||
| addParam(createParamCentered<LEDSlider>(mm2px(Vec(28.279, 55.454)), module, ADSR::SUSTAIN_PARAM)); | |||
| addParam(createParamCentered<LEDSlider>(mm2px(Vec(39.116, 55.454)), module, ADSR::RELEASE_PARAM)); | |||
| // addParam(createParamCentered<Trimpot>(mm2px(Vec(6.604, 80.603)), module, ADSR::ATTCV_PARAM)); | |||
| // addParam(createParamCentered<Trimpot>(mm2px(Vec(17.441, 80.63)), module, ADSR::DECCV_PARAM)); | |||
| // addParam(createParamCentered<Trimpot>(mm2px(Vec(28.279, 80.603)), module, ADSR::SUSCV_PARAM)); | |||
| // addParam(createParamCentered<Trimpot>(mm2px(Vec(39.119, 80.603)), module, ADSR::RELCV_PARAM)); | |||
| // addParam(createParamCentered<LEDLightBezel>(mm2px(Vec(6.604, 113.115)), module, ADSR::PUSH_PARAM)); | |||
| addParam(createLightParamCentered<LEDLightSlider<YellowLight>>(mm2px(Vec(6.604, 55.454)), module, ADSR::ATTACK_PARAM, ADSR::ATTACK_LIGHT)); | |||
| addParam(createLightParamCentered<LEDLightSlider<YellowLight>>(mm2px(Vec(17.441, 55.454)), module, ADSR::DECAY_PARAM, ADSR::DECAY_LIGHT)); | |||
| addParam(createLightParamCentered<LEDLightSlider<YellowLight>>(mm2px(Vec(28.279, 55.454)), module, ADSR::SUSTAIN_PARAM, ADSR::SUSTAIN_LIGHT)); | |||
| addParam(createLightParamCentered<LEDLightSlider<YellowLight>>(mm2px(Vec(39.116, 55.454)), module, ADSR::RELEASE_PARAM, ADSR::RELEASE_LIGHT)); | |||
| addParam(createParamCentered<Trimpot>(mm2px(Vec(6.604, 80.603)), module, ADSR::ATTACK_CV_PARAM)); | |||
| addParam(createParamCentered<Trimpot>(mm2px(Vec(17.441, 80.63)), module, ADSR::DECAY_CV_PARAM)); | |||
| addParam(createParamCentered<Trimpot>(mm2px(Vec(28.279, 80.603)), module, ADSR::SUSTAIN_CV_PARAM)); | |||
| addParam(createParamCentered<Trimpot>(mm2px(Vec(39.119, 80.603)), module, ADSR::RELEASE_CV_PARAM)); | |||
| addParam(createLightParamCentered<LEDLightBezel<>>(mm2px(Vec(6.604, 113.115)), module, ADSR::PUSH_PARAM, ADSR::PUSH_LIGHT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(6.604, 96.882)), module, ADSR::ATTACK_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.441, 96.859)), module, ADSR::DECAY_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.279, 96.886)), module, ADSR::SUSTAIN_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(39.119, 96.89)), module, ADSR::RELEASE_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(17.441, 113.115)), module, ADSR::GATE_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.279, 113.115)), module, ADSR::TRIG_INPUT)); | |||
| addInput(createInputCentered<PJ301MPort>(mm2px(Vec(28.279, 113.115)), module, ADSR::RETRIG_INPUT)); | |||
| addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(39.119, 113.115)), module, ADSR::ENVELOPE_OUTPUT)); | |||
| // mm2px(Vec(45.72, 21.219)) | |||
| // addChild(createWidget<Widget>(mm2px(Vec(0.0, 13.039)))); | |||
| ADSRDisplay* display = createWidget<ADSRDisplay>(mm2px(Vec(0.0, 13.039))); | |||
| display->box.size = mm2px(Vec(45.72, 21.219)); | |||
| display->module = module; | |||
| addChild(display); | |||
| } | |||
| }; | |||