| @@ -3,7 +3,7 @@ | |||
| using simd::float_4; | |||
| template <typename T> | |||
| 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<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 { | |||
| 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 { | |||
| 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); | |||
| } | |||
| }; | |||