diff --git a/src/ABC.cpp b/src/ABC.cpp index 91afc15..3ef6ecf 100644 --- a/src/ABC.cpp +++ b/src/ABC.cpp @@ -3,7 +3,7 @@ using simd::float_4; template -static T clip4(T x) { +static T clip(T x) { // Pade approximant of x/(1 + x^12)^(1/12) const T limit = 1.16691853009184f; x = clamp(x * 0.1f, -limit, limit); @@ -54,115 +54,66 @@ struct ABC : Module { configParam(C2_LEVEL_PARAM, -1.0, 1.0, 0.0, "C2 Level"); } - int processSection(simd::float_4* out, InputIds inputA, InputIds inputB, InputIds inputC, ParamIds levelB, ParamIds levelC) { - - float_4 inA[4] = {}; - float_4 inB[4] = {}; - float_4 inC[4] = {}; - - int channelsA = inputs[inputA].getChannels(); - int channelsB = inputs[inputB].getChannels(); - int channelsC = inputs[inputC].getChannels(); - - // this sets the number of active engines (according to polyphony standard) - // NOTE: A*B + C has the number of active engines set by any one of the three inputs - int activeEngines = std::max(1, channelsA); - activeEngines = std::max(activeEngines, channelsB); - activeEngines = std::max(activeEngines, channelsC); - - float mult_B = (2.f / 5.f) * exponentialBipolar80Pade_5_4(params[levelB].getValue()); - float mult_C = exponentialBipolar80Pade_5_4(params[levelC].getValue()); - - if (inputs[inputA].isConnected()) { - for (int c = 0; c < activeEngines; c += 4) - inA[c / 4] = inputs[inputA].getPolyVoltageSimd(c); + void processSection(const ProcessArgs& args, int& lastChannels, float_4* lastOut, ParamIds levelB, ParamIds levelC, InputIds inputA, InputIds inputB, InputIds inputC, OutputIds output, LightIds outLight) { + // Compute polyphony channels + int channels = std::max(lastChannels, inputs[inputA].getChannels()); + channels = std::max(channels, inputs[inputB].getChannels()); + channels = std::max(channels, inputs[inputC].getChannels()); + lastChannels = channels; + + // Get param levels + float gainB = 2.f * exponentialBipolar80Pade_5_4(params[levelB].getValue()); + float gainC = exponentialBipolar80Pade_5_4(params[levelC].getValue()); + + for (int c = 0; c < channels; c += 4) { + // Get inputs + float_4 inA = inputs[inputA].getPolyVoltageSimd(c); + float_4 inB = inputs[inputB].getNormalPolyVoltageSimd(5.f, c) * gainB; + float_4 inC = inputs[inputC].getNormalPolyVoltageSimd(10.f, c) * gainC; + + // Compute and set output + float_4 out = inA * inB / 5.f + inC; + lastOut[c / 4] += out; + if (outputs[output].isConnected()) { + outputs[output].setChannels(channels); + outputs[output].setVoltageSimd(clip(lastOut[c / 4]), c); + } } - if (inputs[inputB].isConnected()) { - for (int c = 0; c < activeEngines; c += 4) - inB[c / 4] = inputs[inputB].getPolyVoltageSimd(c) * mult_B; + // Set lights + if (channels == 1) { + float b = lastOut[0][0]; + lights[outLight + 0].setSmoothBrightness(b / 5.f, args.sampleTime); + lights[outLight + 1].setSmoothBrightness(-b / 5.f, args.sampleTime); + lights[outLight + 2].setBrightness(0.f); } else { - for (int c = 0; c < activeEngines; c += 4) - inB[c / 4] = 5.f * mult_B; - } - - if (inputs[inputC].isConnected()) { - for (int c = 0; c < activeEngines; c += 4) - inC[c / 4] = inputs[inputC].getPolyVoltageSimd(c) * mult_C; + // RMS of output + float b = 0.f; + for (int c = 0; c < channels; c++) + b += std::pow(lastOut[c / 4][c % 4], 2); + b = std::sqrt(b); + lights[outLight + 0].setBrightness(0.0f); + lights[outLight + 1].setBrightness(0.0f); + lights[outLight + 2].setBrightness(b); } - else { - for (int c = 0; c < activeEngines; c += 4) - inC[c / 4] = 10.f * mult_C; - } - - for (int c = 0; c < activeEngines; c += 4) - out[c / 4] = clip4(inA[c / 4] * inB[c / 4] + inC[c / 4]); - - return activeEngines; } void process(const ProcessArgs& args) override { + int channels = 1; + float_4 out[4] = {}; - // process upper section - float_4 out1[4] = {}; - int activeEngines1 = 1; - if (outputs[OUT1_OUTPUT].isConnected() || outputs[OUT2_OUTPUT].isConnected()) { - activeEngines1 = processSection(out1, A1_INPUT, B1_INPUT, C1_INPUT, B1_LEVEL_PARAM, C1_LEVEL_PARAM); - } - - float_4 out2[4] = {}; - int activeEngines2 = 1; - // process lower section - if (outputs[OUT2_OUTPUT].isConnected()) { - activeEngines2 = processSection(out2, A2_INPUT, B2_INPUT, C2_INPUT, B2_LEVEL_PARAM, C2_LEVEL_PARAM); - } + // Section A + processSection(args, channels, out, B1_LEVEL_PARAM, C1_LEVEL_PARAM, A1_INPUT, B1_INPUT, C1_INPUT, OUT1_OUTPUT, OUT1_LIGHT); - // Set outputs + // Break summing if output A is patched if (outputs[OUT1_OUTPUT].isConnected()) { - outputs[OUT1_OUTPUT].setChannels(activeEngines1); - for (int c = 0; c < activeEngines1; c += 4) - outputs[OUT1_OUTPUT].setVoltageSimd(out1[c / 4], c); + channels = 1; + std::memset(out, 0, sizeof(out)); } - else if (outputs[OUT2_OUTPUT].isConnected()) { - - for (int c = 0; c < activeEngines1; c += 4) - out2[c / 4] += out1[c / 4]; - activeEngines2 = std::max(activeEngines1, activeEngines2); - - outputs[OUT2_OUTPUT].setChannels(activeEngines2); - for (int c = 0; c < activeEngines2; c += 4) - outputs[OUT2_OUTPUT].setVoltageSimd(out2[c / 4], c); - } - - // Lights - - if (activeEngines1 == 1) { - float b = out1[0].s[0]; - lights[OUT1_LIGHT + 0].setSmoothBrightness(b / 5.f, args.sampleTime); - lights[OUT1_LIGHT + 1].setSmoothBrightness(-b / 5.f, args.sampleTime); - lights[OUT1_LIGHT + 2].setBrightness(0.f); - } - else { - float b = 10.f; - lights[OUT1_LIGHT + 0].setBrightness(0.0f); - lights[OUT1_LIGHT + 1].setBrightness(0.0f); - lights[OUT1_LIGHT + 2].setBrightness(b); - } - - if (activeEngines2 == 1) { - float b = out2[0].s[0]; - lights[OUT2_LIGHT + 0].setSmoothBrightness(b / 5.f, args.sampleTime); - lights[OUT2_LIGHT + 1].setSmoothBrightness(-b / 5.f, args.sampleTime); - lights[OUT2_LIGHT + 2].setBrightness(0.f); - } - else { - float b = 10.f; - lights[OUT2_LIGHT + 0].setBrightness(0.0f); - lights[OUT2_LIGHT + 1].setBrightness(0.0f); - lights[OUT2_LIGHT + 2].setBrightness(b); - } + // Section B + processSection(args, channels, out, B2_LEVEL_PARAM, C2_LEVEL_PARAM, A2_INPUT, B2_INPUT, C2_INPUT, OUT2_OUTPUT, OUT2_LIGHT); } };