#include "plugin.hpp" #include "Common.hpp" using simd::float_4; struct SlewLimiter : Module { enum ParamIds { SHAPE_PARAM, RISE_PARAM, FALL_PARAM, NUM_PARAMS }; enum InputIds { RISE_INPUT, FALL_INPUT, IN_INPUT, NUM_INPUTS }; enum OutputIds { OUT_OUTPUT, NUM_OUTPUTS }; float_4 out[4]; SlewLimiter() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); configParam(SHAPE_PARAM, 0.0, 1.0, 0.0, "Shape"); configParam(RISE_PARAM, 0.0, 1.0, 0.0, "Rise time"); configParam(FALL_PARAM, 0.0, 1.0, 0.0, "Fall time"); memset(out, 0, sizeof(out)); } void process(const ProcessArgs& args) override { float_4 in[4] = {0.f}; float_4 riseCV[4] = {0.f}; float_4 fallCV[4] = {0.f}; // this is the number of active polyphony engines, defined by the input int numPolyphonyEngines = inputs[IN_INPUT].getChannels(); // minimum and std::maximum slopes in volts per second const float slewMin = 0.1; const float slewMax = 10000.f; // Amount of extra slew per voltage difference const float shapeScale = 1 / 10.f; const float_4 shape = float_4(params[SHAPE_PARAM].getValue()); const float_4 param_rise = float_4(params[RISE_PARAM].getValue() * 10.f); const float_4 param_fall = float_4(params[FALL_PARAM].getValue() * 10.f); outputs[OUT_OUTPUT].setChannels(numPolyphonyEngines); for (int c = 0; c < numPolyphonyEngines; c += 4) { in[c / 4] = inputs[IN_INPUT].getVoltageSimd(c); if (inputs[RISE_INPUT].isConnected()) { if (inputs[RISE_INPUT].getChannels() == 1) { riseCV[c / 4] = float_4(inputs[RISE_INPUT].getVoltage()); } else { riseCV[c / 4] = inputs[RISE_INPUT].getVoltageSimd(c); } } if (inputs[FALL_INPUT].isConnected()) { if (inputs[FALL_INPUT].getChannels() == 1) { fallCV[c / 4] = float_4(inputs[FALL_INPUT].getVoltage()); } else { fallCV[c / 4] = inputs[FALL_INPUT].getVoltageSimd(c); } } riseCV[c / 4] += param_rise; fallCV[c / 4] += param_fall; float_4 delta = in[c / 4] - out[c / 4]; float_4 delta_gt_0 = delta > float_4::zero(); float_4 delta_lt_0 = delta < float_4::zero(); float_4 rateCV; rateCV = ifelse(delta_gt_0, riseCV[c / 4], float_4::zero()); rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV) * 0.1f; float_4 pm_one = simd::sgn(delta); float_4 slew = slewMax * simd::pow(float_4(slewMin / slewMax), rateCV); out[c / 4] += slew * simd::crossfade(pm_one, shapeScale * delta, shape) * args.sampleTime; out[c / 4] = ifelse(delta_gt_0 & (out[c / 4] > in[c / 4]), in[c / 4], out[c / 4]); out[c / 4] = ifelse(delta_lt_0 & (out[c / 4] < in[c / 4]), in[c / 4], out[c / 4]); out[c / 4].store(outputs[OUT_OUTPUT].getVoltages(c)); } } }; struct SlewLimiterWidget : ModuleWidget { SlewLimiterWidget(SlewLimiter* module) { setModule(module); setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/SlewLimiter.svg"))); addChild(createWidget(Vec(15, 0))); addChild(createWidget(Vec(15, 365))); addParam(createParam(Vec(27, 39), module, ::SlewLimiter::SHAPE_PARAM)); addParam(createParam(Vec(15, 102), module, ::SlewLimiter::RISE_PARAM)); addParam(createParam(Vec(60, 102), module, ::SlewLimiter::FALL_PARAM)); addInput(createInput(Vec(10, 273), module, ::SlewLimiter::RISE_INPUT)); addInput(createInput(Vec(55, 273), module, ::SlewLimiter::FALL_INPUT)); addInput(createInput(Vec(10, 323), module, ::SlewLimiter::IN_INPUT)); addOutput(createOutput(Vec(55, 323), module, ::SlewLimiter::OUT_OUTPUT)); } }; Model* modelSlewLimiter = createModel<::SlewLimiter, SlewLimiterWidget>("SlewLimiter");