| @@ -3,7 +3,7 @@ | |||||
| using simd::float_4; | using simd::float_4; | ||||
| template <typename T> | template <typename T> | ||||
| static T clip4(T x) { | |||||
| static T clip(T x) { | |||||
| // Pade approximant of x/(1 + x^12)^(1/12) | // Pade approximant of x/(1 + x^12)^(1/12) | ||||
| const T limit = 1.16691853009184f; | const T limit = 1.16691853009184f; | ||||
| x = clamp(x * 0.1f, -limit, limit); | 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"); | 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<float_4>(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<float_4>(c); | |||||
| float_4 inB = inputs[inputB].getNormalPolyVoltageSimd<float_4>(5.f, c) * gainB; | |||||
| float_4 inC = inputs[inputC].getNormalPolyVoltageSimd<float_4>(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<float_4>(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 { | 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<float_4>(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 { | 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()) { | 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); | |||||
| } | } | ||||
| }; | }; | ||||