diff --git a/plugin.json b/plugin.json index 4d817b3..4e5439b 100644 --- a/plugin.json +++ b/plugin.json @@ -16,7 +16,8 @@ "modularGridUrl": "https://www.modulargrid.net/e/befaco-even-vco-", "tags": [ "VCO", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -31,7 +32,8 @@ "Slew Limiter", "Envelope Follower", "Dual", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -44,7 +46,8 @@ "Ring Modulator", "Attenuator", "Dual", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -64,7 +67,8 @@ "modularGridUrl": "https://www.modulargrid.net/e/befaco-mixer-", "tags": [ "Mixer", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -75,7 +79,8 @@ "tags": [ "Slew Limiter", "Envelope Follower", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -86,7 +91,8 @@ "tags": [ "Attenuator", "Dual", - "Hardware clone" + "Hardware clone", + "Polyphonic" ] }, { @@ -137,4 +143,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/ABC.cpp b/src/ABC.cpp index 6741918..4f8dc04 100644 --- a/src/ABC.cpp +++ b/src/ABC.cpp @@ -1,17 +1,20 @@ #include "plugin.hpp" #include "Common.hpp" +#include "simd_input.hpp" -inline float clip(float x) { +template +static T clip4(T x) { // Pade approximant of x/(1 + x^12)^(1/12) - const float limit = 1.16691853009184f; - x = clamp(x, -limit, limit); - return (x + 1.45833f * std::pow(x, 13) + 0.559028f * std::pow(x, 25) + 0.0427035f * std::pow(x, 37)) - / (1 + 1.54167f * std::pow(x, 12) + 0.642361f * std::pow(x, 24) + 0.0579909f * std::pow(x, 36)); + const T limit = 1.16691853009184f; + x = clamp(x * 0.1f, -limit, limit); + return 10.0f * (x + 1.45833f * simd::pow(x, 13) + 0.559028f * simd::pow(x, 25) + 0.0427035f * simd::pow(x, 37)) + / (1.0f + 1.54167f * simd::pow(x, 12) + 0.642361f * simd::pow(x, 24) + 0.0579909f * simd::pow(x, 36)); } -inline float exponentialBipolar80Pade_5_4(float x) { - return (0.109568f * x + 0.281588f * std::pow(x, 3) + 0.133841f * std::pow(x, 5)) - / (1 - 0.630374f * std::pow(x, 2) + 0.166271f * std::pow(x, 4)); + +static float exponentialBipolar80Pade_5_4(float x) { + return (0.109568 * x + 0.281588 * std::pow(x, 3) + 0.133841 * std::pow(x, 5)) + / (1. - 0.630374 * std::pow(x, 2) + 0.166271 * std::pow(x, 4)); } @@ -38,8 +41,8 @@ struct ABC : Module { NUM_OUTPUTS }; enum LightIds { - ENUMS(OUT1_LIGHT, 2), - ENUMS(OUT2_LIGHT, 2), + ENUMS(OUT1_LIGHT, 3), + ENUMS(OUT2_LIGHT, 3), NUM_LIGHTS }; @@ -52,32 +55,158 @@ struct ABC : Module { } void process(const ProcessArgs &args) override { - float a1 = inputs[A1_INPUT].getVoltage(); - float b1 = inputs[B1_INPUT].getNormalVoltage(5.f) * 2.f*exponentialBipolar80Pade_5_4(params[B1_LEVEL_PARAM].getValue()); - float c1 = inputs[C1_INPUT].getNormalVoltage(10.f) * exponentialBipolar80Pade_5_4(params[C1_LEVEL_PARAM].getValue()); - float out1 = a1 * b1 / 5.f + c1; - float a2 = inputs[A2_INPUT].getVoltage(); - float b2 = inputs[B2_INPUT].getNormalVoltage(5.f) * 2.f*exponentialBipolar80Pade_5_4(params[B2_LEVEL_PARAM].getValue()); - float c2 = inputs[C2_INPUT].getNormalVoltage(10.f) * exponentialBipolar80Pade_5_4(params[C2_LEVEL_PARAM].getValue()); - float out2 = a2 * b2 / 5.f + c2; + simd::float_4 a1[4] = {}; + simd::float_4 b1[4] = {}; + simd::float_4 c1[4] = {}; + simd::float_4 out1[4]; + + simd::float_4 a2[4] = {}; + simd::float_4 b2[4] = {}; + simd::float_4 c2[4] = {}; + simd::float_4 out2[4]; + + int channels_1 = 1; + int channels_2 = 1; + + memset(out1, 0, sizeof(out1)); + memset(out2, 0, sizeof(out2)); + + // process upper section + + if (outputs[OUT1_OUTPUT].isConnected() || outputs[OUT2_OUTPUT].isConnected()) { + + int channels_A1 = inputs[A1_INPUT].getChannels(); + int channels_B1 = inputs[B1_INPUT].getChannels(); + int channels_C1 = inputs[C1_INPUT].getChannels(); + + channels_1 = std::max(channels_1, channels_A1); + channels_1 = std::max(channels_1, channels_B1); + channels_1 = std::max(channels_1, channels_C1); + + float mult_B1 = (2.f / 5.f) * exponentialBipolar80Pade_5_4(params[B1_LEVEL_PARAM].getValue()); + float mult_C1 = exponentialBipolar80Pade_5_4(params[C1_LEVEL_PARAM].getValue()); + + if (inputs[A1_INPUT].isConnected()) + load_input(inputs[A1_INPUT], a1, channels_A1); + else + memset(a1, 0, sizeof(a1)); + + if (inputs[B1_INPUT].isConnected()) { + load_input(inputs[B1_INPUT], b1, channels_B1); + for (int c = 0; c < channels_1; c += 4) + b1[c / 4] *= simd::float_4(mult_B1); + } + else { + for (int c = 0; c < channels_1; c += 4) + b1[c / 4] = simd::float_4(5.f * mult_B1); + } + + if (inputs[C1_INPUT].isConnected()) { + load_input(inputs[C1_INPUT], c1, channels_C1); + for (int c = 0; c < channels_1; c += 4) + c1[c / 4] *= simd::float_4(mult_C1); + } + else { + for (int c = 0; c < channels_1; c += 4) + c1[c / 4] = simd::float_4(10.f * mult_C1); + } + + for (int c = 0; c < channels_1; c += 4) + out1[c / 4] = clip4(a1[c / 4] * b1[c / 4] + c1[c / 4]); + } + + // process lower section + + if (outputs[OUT2_OUTPUT].isConnected()) { + int channels_A2 = inputs[A2_INPUT].getChannels(); + int channels_B2 = inputs[B2_INPUT].getChannels(); + int channels_C2 = inputs[C2_INPUT].getChannels(); + + channels_2 = std::max(channels_2, channels_A2); + channels_2 = std::max(channels_2, channels_B2); + channels_2 = std::max(channels_2, channels_C2); + + float mult_B2 = (2.f / 5.f) * exponentialBipolar80Pade_5_4(params[B2_LEVEL_PARAM].getValue()); + float mult_C2 = exponentialBipolar80Pade_5_4(params[C2_LEVEL_PARAM].getValue()); + + if (inputs[A2_INPUT].isConnected()) + load_input(inputs[A2_INPUT], a2, channels_A2); + else + memset(a2, 0, sizeof(a2)); + + if (inputs[B2_INPUT].isConnected()) { + load_input(inputs[B2_INPUT], b2, channels_B2); + for (int c = 0; c < channels_2; c += 4) + b2[c / 4] *= simd::float_4(mult_B2); + } + else { + for (int c = 0; c < channels_2; c += 4) + b2[c / 4] = simd::float_4(5.f * mult_B2); + } + + if (inputs[C2_INPUT].isConnected()) { + load_input(inputs[C2_INPUT], c2, channels_C2); + for (int c = 0; c < channels_2; c += 4) + c2[c / 4] *= simd::float_4(mult_C2); + } + else { + for (int c = 0; c < channels_2; c += 4) + c2[c / 4] = simd::float_4(10.f * mult_C2); + } + + for (int c = 0; c < channels_2; c += 4) + out2[c / 4] = clip4(a2[c / 4] * b2[c / 4] + c2[c / 4]); + }; // Set outputs if (outputs[OUT1_OUTPUT].isConnected()) { - outputs[OUT1_OUTPUT].setVoltage(clip(out1 / 10.f) * 10.f); + outputs[OUT1_OUTPUT].setChannels(channels_1); + for (int c = 0; c < channels_1; c += 4) + out1[c / 4].store(outputs[OUT1_OUTPUT].getVoltages(c)); } else { - out2 += out1; + for (int c = 0; c < channels_1; c += 4) + out2[c / 4] += out1[c / 4]; + channels_2 = std::max(channels_1, channels_2); } + if (outputs[OUT2_OUTPUT].isConnected()) { - outputs[OUT2_OUTPUT].setVoltage(clip(out2 / 10.f) * 10.f); + outputs[OUT2_OUTPUT].setChannels(channels_2); + for (int c = 0; c < channels_2; c += 4) + out2[c / 4].store(outputs[OUT2_OUTPUT].getVoltages(c)); } // Lights - lights[OUT1_LIGHT + 0].setSmoothBrightness(out1 / 5.f, args.sampleTime); - lights[OUT1_LIGHT + 1].setSmoothBrightness(-out1 / 5.f, args.sampleTime); - lights[OUT2_LIGHT + 0].setSmoothBrightness(out2 / 5.f, args.sampleTime); - lights[OUT2_LIGHT + 1].setSmoothBrightness(-out2 / 5.f, args.sampleTime); + + float light_1; + float light_2; + + if (channels_1 == 1) { + light_1 = out1[0].s[0]; + lights[OUT1_LIGHT + 0].setSmoothBrightness(light_1 / 5.f, args.sampleTime); + lights[OUT1_LIGHT + 1].setSmoothBrightness(-light_1 / 5.f, args.sampleTime); + lights[OUT1_LIGHT + 2].setBrightness(0.f); + } + else { + light_1 = 10.f; + lights[OUT1_LIGHT + 0].setBrightness(0.0f); + lights[OUT1_LIGHT + 1].setBrightness(0.0f); + lights[OUT1_LIGHT + 2].setBrightness(light_1); + } + + if (channels_2 == 1) { + light_2 = out2[0].s[0]; + lights[OUT2_LIGHT + 0].setSmoothBrightness(light_2 / 5.f, args.sampleTime); + lights[OUT2_LIGHT + 1].setSmoothBrightness(-light_2 / 5.f, args.sampleTime); + lights[OUT2_LIGHT + 2].setBrightness(0.f); + } + else { + light_2 = 10.f; + lights[OUT2_LIGHT + 0].setBrightness(0.0f); + lights[OUT2_LIGHT + 1].setBrightness(0.0f); + lights[OUT2_LIGHT + 2].setBrightness(light_2); + } } }; @@ -104,8 +233,8 @@ struct ABCWidget : ModuleWidget { addInput(createInput(Vec(7, 279), module, ABC::C2_INPUT)); addOutput(createOutput(Vec(7, 321), module, ABC::OUT2_OUTPUT)); - addChild(createLight>(Vec(37, 162), module, ABC::OUT1_LIGHT)); - addChild(createLight>(Vec(37, 329), module, ABC::OUT2_LIGHT)); + addChild(createLight>(Vec(37, 162), module, ABC::OUT1_LIGHT)); + addChild(createLight>(Vec(37, 329), module, ABC::OUT2_LIGHT)); } }; diff --git a/src/DualAtenuverter.cpp b/src/DualAtenuverter.cpp index 46b7a7b..e24e2fd 100644 --- a/src/DualAtenuverter.cpp +++ b/src/DualAtenuverter.cpp @@ -1,5 +1,6 @@ #include "plugin.hpp" #include "Common.hpp" +#include "simd_input.hpp" struct DualAtenuverter : Module { enum ParamIds { @@ -20,10 +21,8 @@ struct DualAtenuverter : Module { NUM_OUTPUTS }; enum LightIds { - OUT1_POS_LIGHT, - OUT1_NEG_LIGHT, - OUT2_POS_LIGHT, - OUT2_NEG_LIGHT, + ENUMS(OUT1_LIGHT, 3), + ENUMS(OUT2_LIGHT, 3), NUM_LIGHTS }; @@ -36,17 +35,63 @@ struct DualAtenuverter : Module { } void process(const ProcessArgs &args) override { - float out1 = inputs[IN1_INPUT].getVoltage() * params[ATEN1_PARAM].getValue() + params[OFFSET1_PARAM].getValue(); - float out2 = inputs[IN2_INPUT].getVoltage() * params[ATEN2_PARAM].getValue() + params[OFFSET2_PARAM].getValue(); - out1 = clamp(out1, -10.f, 10.f); - out2 = clamp(out2, -10.f, 10.f); - - outputs[OUT1_OUTPUT].setVoltage(out1); - outputs[OUT2_OUTPUT].setVoltage(out2); - lights[OUT1_POS_LIGHT].setSmoothBrightness(out1 / 5.f, args.sampleTime); - lights[OUT1_NEG_LIGHT].setSmoothBrightness(-out1 / 5.f, args.sampleTime); - lights[OUT2_POS_LIGHT].setSmoothBrightness(out2 / 5.f, args.sampleTime); - lights[OUT2_NEG_LIGHT].setSmoothBrightness(-out2 / 5.f, args.sampleTime); + using simd::float_4; + + float_4 out1[4]; + float_4 out2[4]; + + int channels1 = inputs[IN1_INPUT].getChannels(); + channels1 = channels1 > 0 ? channels1 : 1; + int channels2 = inputs[IN2_INPUT].getChannels(); + channels2 = channels2 > 0 ? channels2 : 1; + + float att1 = params[ATEN1_PARAM].getValue(); + float att2 = params[ATEN2_PARAM].getValue(); + + float offset1 = params[OFFSET1_PARAM].getValue(); + float offset2 = params[OFFSET2_PARAM].getValue(); + + for (int c = 0; c < channels1; c += 4) { + out1[c / 4] = clamp(float_4::load(inputs[IN1_INPUT].getVoltages(c)) * att1 + offset1, -10.f, 10.f); + } + for (int c = 0; c < channels2; c += 4) { + out2[c / 4] = clamp(float_4::load(inputs[IN2_INPUT].getVoltages(c)) * att2 + offset2, -10.f, 10.f); + } + + outputs[OUT1_OUTPUT].setChannels(channels1); + outputs[OUT2_OUTPUT].setChannels(channels2); + + for (int c = 0; c < channels1; c += 4) { + out1[c / 4].store(outputs[OUT1_OUTPUT].getVoltages(c)); + } + for (int c = 0; c < channels2; c += 4) { + out2[c / 4].store(outputs[OUT2_OUTPUT].getVoltages(c)); + } + + float light1 = outputs[OUT1_OUTPUT].getVoltageSum() / channels1; + float light2 = outputs[OUT2_OUTPUT].getVoltageSum() / channels2; + + if (channels1 == 1) { + lights[OUT1_LIGHT + 0].setSmoothBrightness(light1 / 5.f, args.sampleTime); + lights[OUT1_LIGHT + 1].setSmoothBrightness(-light1 / 5.f, args.sampleTime); + lights[OUT1_LIGHT + 2].setBrightness(0.0f); + } + else { + lights[OUT1_LIGHT + 0].setBrightness(0.0f); + lights[OUT1_LIGHT + 1].setBrightness(0.0f); + lights[OUT1_LIGHT + 2].setBrightness(10.0f); + } + + if (channels2 == 1) { + lights[OUT2_LIGHT + 0].setSmoothBrightness(light2 / 5.f, args.sampleTime); + lights[OUT2_LIGHT + 1].setSmoothBrightness(-light2 / 5.f, args.sampleTime); + lights[OUT2_LIGHT + 2].setBrightness(0.0f); + } + else { + lights[OUT2_LIGHT + 0].setBrightness(0.0f); + lights[OUT2_LIGHT + 1].setBrightness(0.0f); + lights[OUT2_LIGHT + 2].setBrightness(10.0f); + } } }; @@ -70,8 +115,8 @@ struct DualAtenuverterWidget : ModuleWidget { addInput(createInput(Vec(7, 319), module, DualAtenuverter::IN2_INPUT)); addOutput(createOutput(Vec(43, 319), module, DualAtenuverter::OUT2_OUTPUT)); - addChild(createLight>(Vec(33, 143), module, DualAtenuverter::OUT1_POS_LIGHT)); - addChild(createLight>(Vec(33, 311), module, DualAtenuverter::OUT2_POS_LIGHT)); + addChild(createLight>(Vec(33, 143), module, DualAtenuverter::OUT1_LIGHT)); + addChild(createLight>(Vec(33, 311), module, DualAtenuverter::OUT2_LIGHT)); } }; diff --git a/src/EvenVCO.cpp b/src/EvenVCO.cpp index 32e50ac..ec69ea7 100644 --- a/src/EvenVCO.cpp +++ b/src/EvenVCO.cpp @@ -1,4 +1,5 @@ #include "plugin.hpp" +#include "simd_input.hpp" #include "Common.hpp" struct EvenVCO : Module { @@ -25,20 +26,21 @@ struct EvenVCO : Module { NUM_OUTPUTS }; - float phase = 0.0; + simd::float_4 phase[4]; + simd::float_4 tri[4]; + /** The value of the last sync input */ float sync = 0.0; /** The outputs */ - float tri = 0.0; /** Whether we are past the pulse width already */ - bool halfPhase = false; + bool halfPhase[PORT_MAX_CHANNELS]; - dsp::MinBlepGenerator<16, 32> triSquareMinBlep; - dsp::MinBlepGenerator<16, 32> triMinBlep; - dsp::MinBlepGenerator<16, 32> sineMinBlep; - dsp::MinBlepGenerator<16, 32> doubleSawMinBlep; - dsp::MinBlepGenerator<16, 32> sawMinBlep; - dsp::MinBlepGenerator<16, 32> squareMinBlep; + dsp::MinBlepGenerator<16, 32> triSquareMinBlep[PORT_MAX_CHANNELS]; + dsp::MinBlepGenerator<16, 32> triMinBlep[PORT_MAX_CHANNELS]; + dsp::MinBlepGenerator<16, 32> sineMinBlep[PORT_MAX_CHANNELS]; + dsp::MinBlepGenerator<16, 32> doubleSawMinBlep[PORT_MAX_CHANNELS]; + dsp::MinBlepGenerator<16, 32> sawMinBlep[PORT_MAX_CHANNELS]; + dsp::MinBlepGenerator<16, 32> squareMinBlep[PORT_MAX_CHANNELS]; dsp::RCFilter triFilter; @@ -47,72 +49,183 @@ struct EvenVCO : Module { configParam(OCTAVE_PARAM, -5.0, 4.0, 0.0, "Octave", "'", 0.5); configParam(TUNE_PARAM, -7.0, 7.0, 0.0, "Tune", " semitones"); configParam(PWM_PARAM, -1.0, 1.0, 0.0, "Pulse width"); + + for (int i = 0; i < 4; i++) { + phase[i] = simd::float_4(0.0f); + tri[i] = simd::float_4(0.0f); + } + for (int c = 0; c < PORT_MAX_CHANNELS; c++) + halfPhase[c] = false; } void process(const ProcessArgs &args) override { + simd::float_4 pitch[4]; + simd::float_4 pitch_1[4]; + simd::float_4 pitch_2[4]; + simd::float_4 pitch_fm[4]; + simd::float_4 freq[4]; + simd::float_4 pw[4]; + simd::float_4 pwm[4]; + simd::float_4 deltaPhase[4]; + simd::float_4 oldPhase[4]; + + int channels_pitch1 = inputs[PITCH1_INPUT].getChannels(); + int channels_pitch2 = inputs[PITCH2_INPUT].getChannels(); + int channels_fm = inputs[FM_INPUT].getChannels(); + int channels_pwm = inputs[PWM_INPUT].getChannels(); + + int channels = 1; + channels = std::max(channels, channels_pitch1); + channels = std::max(channels, channels_pitch2); + + float pitch_0 = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f; + // Compute frequency, pitch is 1V/oct - float pitch = 1.f + std::round(params[OCTAVE_PARAM].getValue()) + params[TUNE_PARAM].getValue() / 12.f; - pitch += inputs[PITCH1_INPUT].getVoltage() + inputs[PITCH2_INPUT].getVoltage(); - pitch += inputs[FM_INPUT].getVoltage() / 4.f; - float freq = dsp::FREQ_C4 * std::pow(2.f, pitch); - freq = clamp(freq, 0.f, 20000.f); + + for (int c = 0; c < channels; c += 4) + pitch[c / 4] = simd::float_4(pitch_0); + + if (inputs[PITCH1_INPUT].isConnected()) { + load_input(inputs[PITCH1_INPUT], pitch_1, channels_pitch1); + for (int c = 0; c < channels_pitch1; c += 4) + pitch[c / 4] += pitch_1[c / 4]; + } + + if (inputs[PITCH2_INPUT].isConnected()) { + load_input(inputs[PITCH2_INPUT], pitch_2, channels_pitch2); + for (int c = 0; c < channels_pitch2; c += 4) + pitch[c / 4] += pitch_2[c / 4]; + } + + if (inputs[FM_INPUT].isConnected()) { + load_input(inputs[FM_INPUT], pitch_fm, channels_fm); + for (int c = 0; c < channels_fm; c += 4) + pitch[c / 4] += pitch_fm[c / 4] / 4.f; + } + + for (int c = 0; c < channels; c += 4) { + freq[c / 4] = dsp::FREQ_C4 * simd::pow(2.f, pitch[c / 4]); + freq[c / 4] = clamp(freq[c / 4], 0.f, 20000.f); + } + // Pulse width - float pw = params[PWM_PARAM].getValue() + inputs[PWM_INPUT].getVoltage() / 5.f; - const float minPw = 0.05; - pw = rescale(clamp(pw, -1.f, 1.f), -1.f, 1.f, minPw, 1.f - minPw); - - // Advance phase - float deltaPhase = clamp(freq * args.sampleTime, 1e-6f, 0.5f); - float oldPhase = phase; - phase += deltaPhase; - - if (oldPhase < 0.5 && phase >= 0.5) { - float crossing = -(phase - 0.5) / deltaPhase; - triSquareMinBlep.insertDiscontinuity(crossing, 2.f); - doubleSawMinBlep.insertDiscontinuity(crossing, -2.f); + + float pw_0 = params[PWM_PARAM].getValue(); + + for (int c = 0; c < channels; c += 4) + pw[c / 4] = simd::float_4(pw_0); + + if (inputs[PWM_INPUT].isConnected()) { + load_input(inputs[PWM_INPUT], pwm, channels_pwm); + for (int c = 0; c < channels_pwm; c += 4) + pw[c / 4] += pwm[c / 4] / 5.f; } - if (!halfPhase && phase >= pw) { - float crossing = -(phase - pw) / deltaPhase; - squareMinBlep.insertDiscontinuity(crossing, 2.f); - halfPhase = true; + const simd::float_4 minPw_4 = simd::float_4(0.05f); + const simd::float_4 m_one_4 = simd::float_4(-1.0f); + const simd::float_4 one_4 = simd::float_4(1.0f); + + for (int c = 0; c < channels; c += 4) { + pw[c / 4] = rescale(clamp(pw[c / 4], m_one_4, one_4), m_one_4, one_4, minPw_4, one_4 - minPw_4); + + // Advance phase + deltaPhase[c / 4] = clamp(freq[c / 4] * args.sampleTime, simd::float_4(1e-6f), simd::float_4(0.5f)); + oldPhase[c / 4] = phase[c / 4]; + phase[c / 4] += deltaPhase[c / 4]; } - // Reset phase if at end of cycle - if (phase >= 1.f) { - phase -= 1.f; - float crossing = -phase / deltaPhase; - triSquareMinBlep.insertDiscontinuity(crossing, -2.f); - doubleSawMinBlep.insertDiscontinuity(crossing, -2.f); - squareMinBlep.insertDiscontinuity(crossing, -2.f); - sawMinBlep.insertDiscontinuity(crossing, -2.f); - halfPhase = false; + // the next block can't be done with SIMD instructions: + + for (int c = 0; c < channels; c++) { + + if (oldPhase[c / 4].s[c % 4] < 0.5 && phase[c / 4].s[c % 4] >= 0.5) { + float crossing = -(phase[c / 4].s[c % 4] - 0.5) / deltaPhase[c / 4].s[c % 4]; + triSquareMinBlep[c].insertDiscontinuity(crossing, 2.f); + doubleSawMinBlep[c].insertDiscontinuity(crossing, -2.f); + } + + if (!halfPhase[c] && phase[c / 4].s[c % 4] >= pw[c / 4].s[c % 4]) { + float crossing = -(phase[c / 4].s[c % 4] - pw[c / 4].s[c % 4]) / deltaPhase[c / 4].s[c % 4]; + squareMinBlep[c].insertDiscontinuity(crossing, 2.f); + halfPhase[c] = true; + } + + // Reset phase if at end of cycle + if (phase[c / 4].s[c % 4] >= 1.f) { + phase[c / 4].s[c % 4] -= 1.f; + float crossing = -phase[c / 4].s[c % 4] / deltaPhase[c / 4].s[c % 4]; + triSquareMinBlep[c].insertDiscontinuity(crossing, -2.f); + doubleSawMinBlep[c].insertDiscontinuity(crossing, -2.f); + squareMinBlep[c].insertDiscontinuity(crossing, -2.f); + sawMinBlep[c].insertDiscontinuity(crossing, -2.f); + halfPhase[c] = false; + } + } + + simd::float_4 triSquareMinBlepOut[4]; + simd::float_4 doubleSawMinBlepOut[4]; + simd::float_4 sawMinBlepOut[4]; + simd::float_4 squareMinBlepOut[4]; + + simd::float_4 triSquare[4]; + simd::float_4 sine[4]; + simd::float_4 doubleSaw[4]; + + simd::float_4 even[4]; + simd::float_4 saw[4]; + simd::float_4 square[4]; + simd::float_4 triOut[4]; + + for (int c = 0; c < channels; c++) { + triSquareMinBlepOut[c / 4].s[c % 4] = triSquareMinBlep[c].process(); + doubleSawMinBlepOut[c / 4].s[c % 4] = doubleSawMinBlep[c].process(); + sawMinBlepOut[c / 4].s[c % 4] = sawMinBlep[c].process(); + squareMinBlepOut[c / 4].s[c % 4] = squareMinBlep[c].process(); } // Outputs - float triSquare = (phase < 0.5) ? -1.f : 1.f; - triSquare += triSquareMinBlep.process(); - - // Integrate square for triangle - tri += 4.f * triSquare * freq * args.sampleTime; - tri *= (1.f - 40.f * args.sampleTime); - - float sine = -std::cos(2*M_PI * phase); - float doubleSaw = (phase < 0.5) ? (-1.f + 4.f*phase) : (-1.f + 4.f*(phase - 0.5)); - doubleSaw += doubleSawMinBlep.process(); - float even = 0.55 * (doubleSaw + 1.27 * sine); - float saw = -1.f + 2.f*phase; - saw += sawMinBlep.process(); - float square = (phase < pw) ? -1.f : 1.f; - square += squareMinBlep.process(); - - // Set outputs - outputs[TRI_OUTPUT].setVoltage(5.f*tri); - outputs[SINE_OUTPUT].setVoltage(5.f*sine); - outputs[EVEN_OUTPUT].setVoltage(5.f*even); - outputs[SAW_OUTPUT].setVoltage(5.f*saw); - outputs[SQUARE_OUTPUT].setVoltage(5.f*square); + + outputs[TRI_OUTPUT].setChannels(channels); + outputs[SINE_OUTPUT].setChannels(channels); + outputs[EVEN_OUTPUT].setChannels(channels); + outputs[SAW_OUTPUT].setChannels(channels); + outputs[SQUARE_OUTPUT].setChannels(channels); + + for (int c = 0; c < channels; c += 4) { + + triSquare[c / 4] = simd::ifelse((phase[c / 4] < 0.5f * one_4), m_one_4, one_4); + triSquare[c / 4] += triSquareMinBlepOut[c / 4]; + + // Integrate square for triangle + + tri[c / 4] += (4.f * triSquare[c / 4]) * (freq[c / 4] * args.sampleTime); + tri[c / 4] *= (1.f - 40.f * args.sampleTime); + triOut[c / 4] = 5.f * tri[c / 4]; + + sine[c / 4] = 5.f * simd::cos(2 * M_PI * phase[c / 4]); + + doubleSaw[c / 4] = simd::ifelse((phase[c / 4] < 0.5), (-1.f + 4.f * phase[c / 4]), (-1.f + 4.f * (phase[c / 4] - 0.5f))); + doubleSaw[c / 4] += doubleSawMinBlepOut[c / 4]; + doubleSaw[c / 4] *= 5.f; + + even[c / 4] = 0.55 * (doubleSaw[c / 4] + 1.27 * sine[c / 4]); + saw[c / 4] = -1.f + 2.f * phase[c / 4]; + saw[c / 4] += sawMinBlepOut[c / 4]; + saw[c / 4] *= 5.f; + + square[c / 4] = simd::ifelse((phase[c / 4] < pw[c / 4]), m_one_4, one_4) ; + square[c / 4] += squareMinBlepOut[c / 4]; + square[c / 4] *= 5.f; + + // Set outputs + + triOut[c / 4].store(outputs[TRI_OUTPUT].getVoltages(c)); + sine[c / 4].store(outputs[SINE_OUTPUT].getVoltages(c)); + even[c / 4].store(outputs[EVEN_OUTPUT].getVoltages(c)); + saw[c / 4].store(outputs[SAW_OUTPUT].getVoltages(c)); + square[c / 4].store(outputs[SQUARE_OUTPUT].getVoltages(c)); + } } }; @@ -124,8 +237,8 @@ struct EvenVCOWidget : ModuleWidget { addChild(createWidget(Vec(15, 0))); addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(15*6, 0))); - addChild(createWidget(Vec(15*6, 365))); + addChild(createWidget(Vec(15 * 6, 0))); + addChild(createWidget(Vec(15 * 6, 365))); addParam(createParam(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM)); addParam(createParam(Vec(73, 131), module, EvenVCO::TUNE_PARAM)); diff --git a/src/Mixer.cpp b/src/Mixer.cpp index eb5d9b9..8ea8c2c 100644 --- a/src/Mixer.cpp +++ b/src/Mixer.cpp @@ -1,5 +1,6 @@ #include "plugin.hpp" #include "Common.hpp" +#include "simd_input.hpp" struct Mixer : Module { enum ParamIds { @@ -24,6 +25,7 @@ struct Mixer : Module { enum LightIds { OUT_POS_LIGHT, OUT_NEG_LIGHT, + OUT_BLUE_LIGHT, NUM_LIGHTS }; @@ -36,22 +38,78 @@ struct Mixer : Module { } void process(const ProcessArgs &args) override { - float in1 = inputs[IN1_INPUT].getVoltage() * params[CH1_PARAM].getValue(); - float in2 = inputs[IN2_INPUT].getVoltage() * params[CH2_PARAM].getValue(); - float in3 = inputs[IN3_INPUT].getVoltage() * params[CH3_PARAM].getValue(); - float in4 = inputs[IN4_INPUT].getVoltage() * params[CH4_PARAM].getValue(); - - float out = in1 + in2 + in3 + in4; - outputs[OUT1_OUTPUT].setVoltage(out); - outputs[OUT2_OUTPUT].setVoltage(-out); - lights[OUT_POS_LIGHT].setSmoothBrightness(out / 5.f, args.sampleTime); - lights[OUT_NEG_LIGHT].setSmoothBrightness(-out / 5.f, args.sampleTime); + int channels1 = inputs[IN1_INPUT].getChannels(); + int channels2 = inputs[IN2_INPUT].getChannels(); + int channels3 = inputs[IN3_INPUT].getChannels(); + int channels4 = inputs[IN4_INPUT].getChannels(); + + int out_channels = 1; + out_channels = std::max(out_channels, channels1); + out_channels = std::max(out_channels, channels2); + out_channels = std::max(out_channels, channels3); + out_channels = std::max(out_channels, channels4); + + simd::float_4 mult1 = simd::float_4(params[CH1_PARAM].getValue()); + simd::float_4 mult2 = simd::float_4(params[CH2_PARAM].getValue()); + simd::float_4 mult3 = simd::float_4(params[CH3_PARAM].getValue()); + simd::float_4 mult4 = simd::float_4(params[CH4_PARAM].getValue()); + + simd::float_4 out[4]; + + std::memset(out, 0, sizeof(out)); + + + if (inputs[IN1_INPUT].isConnected()) { + for (int c = 0; c < channels1; c += 4) + out[c / 4] += simd::float_4::load(inputs[IN1_INPUT].getVoltages(c)) * mult1; + } + + if (inputs[IN2_INPUT].isConnected()) { + for (int c = 0; c < channels2; c += 4) + out[c / 4] += simd::float_4::load(inputs[IN2_INPUT].getVoltages(c)) * mult2; + } + + if (inputs[IN3_INPUT].isConnected()) { + for (int c = 0; c < channels3; c += 4) + out[c / 4] += simd::float_4::load(inputs[IN3_INPUT].getVoltages(c)) * mult3; + } + + if (inputs[IN4_INPUT].isConnected()) { + for (int c = 0; c < channels4; c += 4) + out[c / 4] += simd::float_4::load(inputs[IN4_INPUT].getVoltages(c)) * mult4; + } + + + outputs[OUT1_OUTPUT].setChannels(out_channels); + outputs[OUT2_OUTPUT].setChannels(out_channels); + + for (int c = 0; c < out_channels; c += 4) { + out[c / 4].store(outputs[OUT1_OUTPUT].getVoltages(c)); + out[c / 4] *= -1.f; + out[c / 4].store(outputs[OUT2_OUTPUT].getVoltages(c)); + } + + if (out_channels == 1) { + float light = outputs[OUT1_OUTPUT].getVoltage(); + lights[OUT_POS_LIGHT].setSmoothBrightness(light / 5.f, args.sampleTime); + lights[OUT_NEG_LIGHT].setSmoothBrightness(-light / 5.f, args.sampleTime); + } + else { + float light = 0.0f; + for (int c = 0; c < out_channels; c++) { + float tmp = outputs[OUT1_OUTPUT].getVoltage(c); + light += tmp * tmp; + } + light = std::sqrt(light); + lights[OUT_POS_LIGHT].setBrightness(0.0f); + lights[OUT_NEG_LIGHT].setBrightness(0.0f); + lights[OUT_BLUE_LIGHT].setSmoothBrightness(light / 5.f, args.sampleTime); + } + } }; - - struct MixerWidget : ModuleWidget { MixerWidget(Mixer *module) { setModule(module); @@ -74,7 +132,7 @@ struct MixerWidget : ModuleWidget { addOutput(createOutput(Vec(7, 324), module, Mixer::OUT1_OUTPUT)); addOutput(createOutput(Vec(43, 324), module, Mixer::OUT2_OUTPUT)); - addChild(createLight>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); + addChild(createLight>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); } }; diff --git a/src/PulseGenerator_4.hpp b/src/PulseGenerator_4.hpp new file mode 100644 index 0000000..24afeac --- /dev/null +++ b/src/PulseGenerator_4.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "rack.hpp" + + +/** When triggered, holds a high value for a specified time before going low again */ +struct PulseGenerator_4 { + + simd::float_4 remaining = simd::float_4::zero(); + + /** Immediately disables the pulse */ + void reset() { + remaining = simd::float_4::zero(); + } + + /** Advances the state by `deltaTime`. Returns whether the pulse is in the HIGH state. */ + inline simd::float_4 process(float deltaTime) { + + simd::float_4 mask = (remaining > simd::float_4::zero()); + + remaining -= ifelse(mask, simd::float_4(deltaTime), simd::float_4::zero()); + return ifelse(mask, simd::float_4::mask(), simd::float_4::zero()); + } + + /** Begins a trigger with the given `duration`. */ + inline void trigger(simd::float_4 mask, float duration = 1e-3f) { + // Keep the previous pulse if the existing pulse will be held longer than the currently requested one. + simd::float_4 duration_4 = simd::float_4(duration); + remaining = ifelse(mask & (duration_4 > remaining), duration_4, remaining); + } +}; + diff --git a/src/Rampage.cpp b/src/Rampage.cpp index baf56b1..0a4d7df 100644 --- a/src/Rampage.cpp +++ b/src/Rampage.cpp @@ -1,19 +1,20 @@ #include "plugin.hpp" #include "Common.hpp" +#include "simd_input.hpp" +#include "PulseGenerator_4.hpp" -static float shapeDelta(float delta, float tau, float shape) { - float lin = sgn(delta) * 10.f / tau; +static simd::float_4 shapeDelta(simd::float_4 delta, simd::float_4 tau, float shape) { + simd::float_4 lin = simd::sgn(delta) * 10.f / tau; if (shape < 0.f) { - float log = sgn(delta) * 40.f / tau / (std::fabs(delta) + 1.f); - return crossfade(lin, log, -shape * 0.95f); + simd::float_4 log = simd::sgn(delta) * simd::float_4(40.f) / tau / (simd::fabs(delta) + simd::float_4(1.f)); + return simd::crossfade(lin, log, -shape * 0.95f); } else { - float exp = M_E * delta / tau; - return crossfade(lin, exp, shape * 0.90f); + simd::float_4 exp = M_E * delta / tau; + return simd::crossfade(lin, exp, shape * 0.90f); } } - struct Rampage : Module { enum ParamIds { RANGE_A_PARAM, @@ -61,22 +62,26 @@ struct Rampage : Module { NUM_OUTPUTS }; enum LightIds { - COMPARATOR_LIGHT, - MIN_LIGHT, - MAX_LIGHT, - OUT_A_LIGHT, - OUT_B_LIGHT, - RISING_A_LIGHT, - RISING_B_LIGHT, - FALLING_A_LIGHT, - FALLING_B_LIGHT, + ENUMS(COMPARATOR_LIGHT, 3), + ENUMS(MIN_LIGHT, 3), + ENUMS(MAX_LIGHT, 3), + ENUMS(OUT_A_LIGHT, 3), + ENUMS(OUT_B_LIGHT, 3), + ENUMS(RISING_A_LIGHT, 3), + ENUMS(RISING_B_LIGHT, 3), + ENUMS(FALLING_A_LIGHT, 3), + ENUMS(FALLING_B_LIGHT, 3), NUM_LIGHTS }; - float out[2] = {}; - bool gate[2] = {}; - dsp::SchmittTrigger trigger[2]; - dsp::PulseGenerator endOfCyclePulse[2]; + + simd::float_4 out[2][4]; + simd::float_4 gate[2][4]; // use simd __m128 logic instead of bool + + dsp::TSchmittTrigger trigger_4[2][4]; + PulseGenerator_4 endOfCyclePulse[2][4]; + + // ChannelMask channelMask; Rampage() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -93,90 +98,229 @@ struct Rampage : Module { configParam(FALL_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 fall time"); configParam(CYCLE_B_PARAM, 0.0, 1.0, 0.0, "Ch 2 cycle"); configParam(BALANCE_PARAM, 0.0, 1.0, 0.5, "Balance"); + + std::memset(out, 0, sizeof(out)); + std::memset(gate, 0, sizeof(gate)); } void process(const ProcessArgs &args) override { - for (int c = 0; c < 2; c++) { - float in = inputs[IN_A_INPUT + c].getVoltage(); - if (trigger[c].process(params[TRIGG_A_PARAM + c].getValue() * 10.0 + inputs[TRIGG_A_INPUT + c].getVoltage() / 2.0)) { - gate[c] = true; + int channels_in[2]; + int channels_trig[2]; + int channels[2]; + + // determine number of channels: + + for (int part = 0; part < 2; part++) { + + channels_in[part] = inputs[IN_A_INPUT + part].getChannels(); + channels_trig[part] = inputs[TRIGG_A_INPUT + part].getChannels(); + channels[part] = std::max(channels_in[part], channels_trig[part]); + channels[part] = std::max(1, channels[part]); + + outputs[OUT_A_OUTPUT + part].setChannels(channels[part]); + outputs[RISING_A_OUTPUT + part].setChannels(channels[part]); + outputs[FALLING_A_OUTPUT + part].setChannels(channels[part]); + outputs[EOC_A_OUTPUT + part].setChannels(channels[part]); + } + int channels_max = std::max(channels[0], channels[1]); + + outputs[COMPARATOR_OUTPUT].setChannels(channels_max); + outputs[MIN_OUTPUT].setChannels(channels_max); + outputs[MAX_OUTPUT].setChannels(channels_max); + + // loop over two parts of Rampage: + + for (int part = 0; part < 2; part++) { + + simd::float_4 in[4]; + simd::float_4 in_trig[4]; + simd::float_4 expCV[4]; + simd::float_4 riseCV[4]; + simd::float_4 fallCV[4]; + simd::float_4 cycle[4]; + + // get parameters: + + float shape = params[SHAPE_A_PARAM + part].getValue(); + float minTime; + switch ((int) params[RANGE_A_PARAM + part].getValue()) { + case 0: + minTime = 1e-2; + break; + case 1: + minTime = 1e-3; + break; + default: + minTime = 1e-1; + break; } - if (gate[c]) { - in = 10.0; + + simd::float_4 param_rise = simd::float_4(params[RISE_A_PARAM + part].getValue() * 10.0f); + simd::float_4 param_fall = simd::float_4(params[FALL_A_PARAM + part].getValue() * 10.0f); + simd::float_4 param_trig = simd::float_4(params[TRIGG_A_PARAM + part].getValue() * 20.0f); + simd::float_4 param_cycle = simd::float_4(params[CYCLE_A_PARAM + part].getValue() * 10.0f); + + for (int c = 0; c < channels[part]; c += 4) { + riseCV[c / 4] = param_rise; + fallCV[c / 4] = param_fall; + cycle[c / 4] = param_cycle; + in_trig[c / 4] = param_trig; + } - float shape = params[SHAPE_A_PARAM + c].getValue(); - float delta = in - out[c]; - // Integrator - float minTime; - switch ((int) params[RANGE_A_PARAM + c].getValue()) { - case 0: minTime = 1e-2; break; - case 1: minTime = 1e-3; break; - default: minTime = 1e-1; break; + // read inputs: + + if (inputs[IN_A_INPUT + part].isConnected()) { + load_input(inputs[IN_A_INPUT + part], in, channels_in[part]); + // channelMask.apply_all(in, channels_in[part]); + } + else { + std::memset(in, 0, sizeof(in)); } - bool rising = false; - bool falling = false; - - if (delta > 0) { - // Rise - float riseCv = params[RISE_A_PARAM + c].getValue() - inputs[EXP_CV_A_INPUT + c].getVoltage() / 10.0 + inputs[RISE_CV_A_INPUT + c].getVoltage() / 10.0; - riseCv = clamp(riseCv, 0.0f, 1.0f); - float rise = minTime * std::pow(2.0, riseCv * 10.0); - out[c] += shapeDelta(delta, rise, shape) * args.sampleTime; - rising = (in - out[c] > 1e-3); - if (!rising) { - gate[c] = false; - } + if (inputs[TRIGG_A_INPUT + part].isConnected()) { + add_input(inputs[TRIGG_A_INPUT + part], in_trig, channels_trig[part]); + // channelMask.apply_all(in_trig, channels_trig[part]); } - else if (delta < 0) { - // Fall - float fallCv = params[FALL_A_PARAM + c].getValue() - inputs[EXP_CV_A_INPUT + c].getVoltage() / 10.0 + inputs[FALL_CV_A_INPUT + c].getVoltage() / 10.0; - fallCv = clamp(fallCv, 0.0f, 1.0f); - float fall = minTime * std::pow(2.0, fallCv * 10.0); - out[c] += shapeDelta(delta, fall, shape) * args.sampleTime; - falling = (in - out[c] < -1e-3); - if (!falling) { - // End of cycle, check if we should turn the gate back on (cycle mode) - endOfCyclePulse[c].trigger(1e-3); - if (params[CYCLE_A_PARAM + c].getValue() * 10.0 + inputs[CYCLE_A_INPUT + c].getVoltage() >= 4.0) { - gate[c] = true; - } + + if (inputs[EXP_CV_A_INPUT + part].isConnected()) { + load_input(inputs[EXP_CV_A_INPUT + part], expCV, channels[part]); + for (int c = 0; c < channels[part]; c += 4) { + riseCV[c / 4] -= expCV[c / 4]; + fallCV[c / 4] -= expCV[c / 4]; } } + + add_input(inputs[RISE_CV_A_INPUT + part], riseCV, channels[part]); + add_input(inputs[FALL_CV_A_INPUT + part], fallCV, channels[part]); + add_input(inputs[CYCLE_A_INPUT + part], cycle, channels[part]); + // channelMask.apply(cycle, channels[part]); // check whether this is necessary + + // start processing: + + for (int c = 0; c < channels[part]; c += 4) { + + // process SchmittTriggers + + simd::float_4 trig_mask = trigger_4[part][c / 4].process(in_trig[c / 4] / 2.0); + gate[part][c / 4] = ifelse(trig_mask, simd::float_4::mask(), gate[part][c / 4]); + in[c / 4] = ifelse(gate[part][c / 4], simd::float_4(10.0f), in[c / 4]); + + simd::float_4 delta = in[c / 4] - out[part][c / 4]; + + // rise / fall branching + + simd::float_4 delta_gt_0 = delta > simd::float_4::zero(); + simd::float_4 delta_lt_0 = delta < simd::float_4::zero(); + simd::float_4 delta_eq_0 = ~(delta_lt_0 | delta_gt_0); + + simd::float_4 rateCV = ifelse(delta_gt_0, riseCV[c / 4], simd::float_4::zero()); + rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV); + rateCV = clamp(rateCV, simd::float_4::zero(), simd::float_4(10.0f)); + + simd::float_4 rate = minTime * simd::pow(2.0f, rateCV); + + out[part][c / 4] += shapeDelta(delta, rate, shape) * args.sampleTime; + + simd::float_4 rising = (in[c / 4] - out[part][c / 4]) > simd::float_4(1e-3); + simd::float_4 falling = (in[c / 4] - out[part][c / 4]) < simd::float_4(-1e-3); + simd::float_4 end_of_cycle = simd::andnot(falling, delta_lt_0); + + endOfCyclePulse[part][c / 4].trigger(end_of_cycle, 1e-3); + + gate[part][c / 4] = ifelse(simd::andnot(rising, delta_gt_0), simd::float_4::zero(), gate[part][c / 4]); + gate[part][c / 4] = ifelse(end_of_cycle & (cycle[c / 4] >= simd::float_4(4.0f)), simd::float_4::mask(), gate[part][c / 4]); + gate[part][c / 4] = ifelse(delta_eq_0, simd::float_4::zero(), gate[part][c / 4]); + + out[part][c / 4] = ifelse(rising | falling, out[part][c / 4], in[c / 4]); + + simd::float_4 out_rising = ifelse(rising, simd::float_4(10.0f), simd::float_4::zero()); + simd::float_4 out_falling = ifelse(falling, simd::float_4(10.0f), simd::float_4::zero()); + + simd::float_4 pulse = endOfCyclePulse[part][c / 4].process(args.sampleTime); + simd::float_4 out_EOC = ifelse(pulse, simd::float_4(10.f), simd::float_4::zero()); + + out[part][c / 4].store(outputs[OUT_A_OUTPUT + part].getVoltages(c)); + + out_rising.store(outputs[RISING_A_OUTPUT + part].getVoltages(c)); + out_falling.store(outputs[FALLING_A_OUTPUT + part].getVoltages(c)); + out_EOC.store(outputs[EOC_A_OUTPUT + part].getVoltages(c)); + + + } // for(int c, ...) + + if (channels[part] == 1) { + lights[RISING_A_LIGHT + 3 * part ].setSmoothBrightness(outputs[RISING_A_OUTPUT + part].getVoltage() / 10.f, args.sampleTime); + lights[RISING_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[RISING_A_LIGHT + 3 * part + 2].setBrightness(0.0f); + lights[FALLING_A_LIGHT + 3 * part ].setSmoothBrightness(outputs[FALLING_A_OUTPUT + part].getVoltage() / 10.f, args.sampleTime); + lights[FALLING_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[FALLING_A_LIGHT + 3 * part + 2].setBrightness(0.0f); + lights[OUT_A_LIGHT + 3 * part ].setSmoothBrightness(out[part][0].s[0] / 10.0, args.sampleTime); + lights[OUT_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[OUT_A_LIGHT + 3 * part + 2].setBrightness(0.0f); + } else { - gate[c] = false; + lights[RISING_A_LIGHT + 3 * part ].setBrightness(0.0f); + lights[RISING_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[RISING_A_LIGHT + 3 * part + 2].setBrightness(10.0f); + lights[FALLING_A_LIGHT + 3 * part ].setBrightness(0.0f); + lights[FALLING_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[FALLING_A_LIGHT + 3 * part + 2].setBrightness(10.0f); + lights[OUT_A_LIGHT + 3 * part ].setBrightness(0.0f); + lights[OUT_A_LIGHT + 3 * part + 1].setBrightness(0.0f); + lights[OUT_A_LIGHT + 3 * part + 2].setBrightness(10.0f); } - if (!rising && !falling) { - out[c] = in; - } + } // for (int part, ... ) - outputs[RISING_A_OUTPUT + c].setVoltage((rising ? 10.0 : 0.0)); - outputs[FALLING_A_OUTPUT + c].setVoltage((falling ? 10.0 : 0.0)); - lights[RISING_A_LIGHT + c].setSmoothBrightness(rising ? 1.0 : 0.0, args.sampleTime); - lights[FALLING_A_LIGHT + c].setSmoothBrightness(falling ? 1.0 : 0.0, args.sampleTime); - outputs[EOC_A_OUTPUT + c].setVoltage((endOfCyclePulse[c].process(args.sampleTime) ? 10.0 : 0.0)); - outputs[OUT_A_OUTPUT + c].setVoltage(out[c]); - lights[OUT_A_LIGHT + c].setSmoothBrightness(out[c] / 10.0, args.sampleTime); - } // Logic float balance = params[BALANCE_PARAM].getValue(); - float a = out[0]; - float b = out[1]; - if (balance < 0.5) - b *= 2.0 * balance; - else if (balance > 0.5) - a *= 2.0 * (1.0 - balance); - outputs[COMPARATOR_OUTPUT].setVoltage((b > a ? 10.0 : 0.0)); - outputs[MIN_OUTPUT].setVoltage(std::min(a, b)); - outputs[MAX_OUTPUT].setVoltage(std::max(a, b)); + + for (int c = 0; c < channels_max; c += 4) { + + simd::float_4 a = out[0][c / 4]; + simd::float_4 b = out[1][c / 4]; + + if (balance < 0.5) + b *= 2.0f * balance; + else if (balance > 0.5) + a *= 2.0f * (1.0 - balance); + + simd::float_4 comp = ifelse(b > a, simd::float_4(10.0f), simd::float_4::zero()); + simd::float_4 out_min = simd::fmin(a, b); + simd::float_4 out_max = simd::fmax(a, b); + + comp.store(outputs[COMPARATOR_OUTPUT].getVoltages(c)); + out_min.store(outputs[MIN_OUTPUT].getVoltages(c)); + out_max.store(outputs[MAX_OUTPUT].getVoltages(c)); + + } // Lights - lights[COMPARATOR_LIGHT].setSmoothBrightness(outputs[COMPARATOR_OUTPUT].value / 10.0, args.sampleTime); - lights[MIN_LIGHT].setSmoothBrightness(outputs[MIN_OUTPUT].value / 10.0, args.sampleTime); - lights[MAX_LIGHT].setSmoothBrightness(outputs[MAX_OUTPUT].value / 10.0, args.sampleTime); + if (channels_max == 1) { + lights[COMPARATOR_LIGHT ].setSmoothBrightness(outputs[COMPARATOR_OUTPUT].getVoltage(), args.sampleTime); + lights[COMPARATOR_LIGHT + 1].setBrightness(0.0f); + lights[COMPARATOR_LIGHT + 2].setBrightness(0.0f); + lights[MIN_LIGHT ].setSmoothBrightness(outputs[MIN_OUTPUT].getVoltage(), args.sampleTime); + lights[MIN_LIGHT + 1].setBrightness(0.0f); + lights[MIN_LIGHT + 2].setBrightness(0.0f); + lights[MAX_LIGHT ].setSmoothBrightness(outputs[MAX_OUTPUT].getVoltage(), args.sampleTime); + lights[MAX_LIGHT + 1].setBrightness(0.0f); + lights[MAX_LIGHT + 2].setBrightness(0.0f); + } + else { + lights[COMPARATOR_LIGHT ].setBrightness(0.0f); + lights[COMPARATOR_LIGHT + 1].setBrightness(0.0f); + lights[COMPARATOR_LIGHT + 2].setBrightness(10.0f); + lights[MIN_LIGHT ].setBrightness(0.0f); + lights[MIN_LIGHT + 1].setBrightness(0.0f); + lights[MIN_LIGHT + 2].setBrightness(10.0f); + lights[MAX_LIGHT ].setBrightness(0.0f); + lights[MAX_LIGHT + 1].setBrightness(0.0f); + lights[MAX_LIGHT + 2].setBrightness(10.0f); + } } }; @@ -189,9 +333,9 @@ struct RampageWidget : ModuleWidget { setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Rampage.svg"))); addChild(createWidget(Vec(15, 0))); - addChild(createWidget(Vec(box.size.x-30, 0))); + addChild(createWidget(Vec(box.size.x - 30, 0))); addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(box.size.x-30, 365))); + addChild(createWidget(Vec(box.size.x - 30, 365))); addParam(createParam(Vec(94, 32), module, Rampage::RANGE_A_PARAM)); addParam(createParam(Vec(27, 90), module, Rampage::SHAPE_A_PARAM)); @@ -232,15 +376,15 @@ struct RampageWidget : ModuleWidget { addOutput(createOutput(Vec(89, 157), module, Rampage::MIN_OUTPUT)); addOutput(createOutput(Vec(155, 157), module, Rampage::MAX_OUTPUT)); - addChild(createLight>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT)); - addChild(createLight>(Vec(123, 174), module, Rampage::MIN_LIGHT)); - addChild(createLight>(Vec(141, 174), module, Rampage::MAX_LIGHT)); - addChild(createLight>(Vec(126, 185), module, Rampage::OUT_A_LIGHT)); - addChild(createLight>(Vec(138, 185), module, Rampage::OUT_B_LIGHT)); - addChild(createLight>(Vec(18, 312), module, Rampage::RISING_A_LIGHT)); - addChild(createLight>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT)); - addChild(createLight>(Vec(187, 312), module, Rampage::RISING_B_LIGHT)); - addChild(createLight>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT)); + addChild(createLight>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT)); + addChild(createLight>(Vec(123, 174), module, Rampage::MIN_LIGHT)); + addChild(createLight>(Vec(141, 174), module, Rampage::MAX_LIGHT)); + addChild(createLight>(Vec(126, 185), module, Rampage::OUT_A_LIGHT)); + addChild(createLight>(Vec(138, 185), module, Rampage::OUT_B_LIGHT)); + addChild(createLight>(Vec(18, 312), module, Rampage::RISING_A_LIGHT)); + addChild(createLight>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT)); + addChild(createLight>(Vec(187, 312), module, Rampage::RISING_B_LIGHT)); + addChild(createLight>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT)); } }; diff --git a/src/SlewLimiter.cpp b/src/SlewLimiter.cpp index 5e15787..f87c735 100644 --- a/src/SlewLimiter.cpp +++ b/src/SlewLimiter.cpp @@ -1,5 +1,6 @@ #include "plugin.hpp" #include "Common.hpp" +#include "simd_input.hpp" struct SlewLimiter : Module { enum ParamIds { @@ -19,43 +20,65 @@ struct SlewLimiter : Module { NUM_OUTPUTS }; - float out = 0.0; + simd::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 in = inputs[IN_INPUT].getVoltage(); - float shape = params[SHAPE_PARAM].getValue(); - // minimum and maximum slopes in volts per second + simd::float_4 in[4]; + simd::float_4 riseCV[4]; + simd::float_4 fallCV[4]; + + int channels = 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; - - // Rise - if (in > out) { - float rise = inputs[RISE_INPUT].getVoltage() / 10.f + params[RISE_PARAM].getValue(); - float slew = slewMax * std::pow(slewMin / slewMax, rise); - out += slew * crossfade(1.f, shapeScale * (in - out), shape) * args.sampleTime; - if (out > in) - out = in; - } - // Fall - else if (in < out) { - float fall = inputs[FALL_INPUT].getVoltage() / 10.f + params[FALL_PARAM].getValue(); - float slew = slewMax * std::pow(slewMin / slewMax, fall); - out -= slew * crossfade(1.f, shapeScale * (out - in), shape) * args.sampleTime; - if (out < in) - out = in; - } + const float shapeScale = 1 / 10.f; + + const simd::float_4 shape = simd::float_4(params[SHAPE_PARAM].getValue()); + const simd::float_4 param_rise = simd::float_4(params[RISE_PARAM].getValue() * 10.f); + const simd::float_4 param_fall = simd::float_4(params[FALL_PARAM].getValue() * 10.f); + + outputs[OUT_OUTPUT].setChannels(channels); + + load_input(inputs[IN_INPUT], in, channels); + load_input(inputs[RISE_INPUT], riseCV, channels); + load_input(inputs[FALL_INPUT], fallCV, channels); - outputs[OUT_OUTPUT].setVoltage(out); + for (int c = 0; c < channels; c += 4) { + riseCV[c / 4] += param_rise; + fallCV[c / 4] += param_fall; + + simd::float_4 delta = in[c / 4] - out[c / 4]; + + simd::float_4 delta_gt_0 = delta > simd::float_4::zero(); + simd::float_4 delta_lt_0 = delta < simd::float_4::zero(); + + simd::float_4 rateCV; + rateCV = ifelse(delta_gt_0, riseCV[c / 4], simd::float_4::zero()); + rateCV = ifelse(delta_lt_0, fallCV[c / 4], rateCV) * 0.1f; + + simd::float_4 pm_one = simd::sgn(delta); + + simd::float_4 slew = slewMax * simd::pow(simd::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)); + } } }; diff --git a/src/SpringReverb.cpp b/src/SpringReverb.cpp index a149fe2..d982fdd 100644 --- a/src/SpringReverb.cpp +++ b/src/SpringReverb.cpp @@ -32,15 +32,15 @@ struct SpringReverb : Module { }; enum LightIds { PEAK_LIGHT, - VU1_LIGHT, - NUM_LIGHTS = VU1_LIGHT + 7 + ENUMS(VU1_LIGHTS, 7), + NUM_LIGHTS }; dsp::RealTimeConvolver *convolver = NULL; dsp::SampleRateConverter<1> inputSrc; dsp::SampleRateConverter<1> outputSrc; - dsp::DoubleRingBuffer, 16*BLOCK_SIZE> inputBuffer; - dsp::DoubleRingBuffer, 16*BLOCK_SIZE> outputBuffer; + dsp::DoubleRingBuffer, 16 * BLOCK_SIZE> inputBuffer; + dsp::DoubleRingBuffer, 16 * BLOCK_SIZE> outputBuffer; dsp::RCFilter dryFilter; dsp::PeakFilter vuFilter; @@ -55,7 +55,7 @@ struct SpringReverb : Module { convolver = new dsp::RealTimeConvolver(BLOCK_SIZE); - const float *kernel = (const float*) BINARY_START(src_SpringReverbIR_pcm); + const float *kernel = (const float *) BINARY_START(src_SpringReverbIR_pcm); size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float); convolver->setKernel(kernel, kernelLen); } @@ -65,8 +65,8 @@ struct SpringReverb : Module { } void process(const ProcessArgs &args) override { - float in1 = inputs[IN1_INPUT].getVoltage(); - float in2 = inputs[IN2_INPUT].getVoltage(); + float in1 = inputs[IN1_INPUT].getVoltageSum(); + float in2 = inputs[IN2_INPUT].getVoltageSum(); const float levelScale = 0.030; const float levelBase = 25.0; float level1 = levelScale * dsp::exponentialBipolar(levelBase, params[LEVEL1_PARAM].getValue()) * inputs[CV1_INPUT].getNormalVoltage(10.0) / 10.0; @@ -94,7 +94,7 @@ struct SpringReverb : Module { inputSrc.setRates(args.sampleRate, 48000); int inLen = inputBuffer.size(); int outLen = BLOCK_SIZE; - inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1>*) input, &outLen); + inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1> *) input, &outLen); inputBuffer.startIncr(inLen); } @@ -106,7 +106,7 @@ struct SpringReverb : Module { outputSrc.setRates(48000, args.sampleRate); int inLen = BLOCK_SIZE; int outLen = outputBuffer.capacity(); - outputSrc.process((dsp::Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen); + outputSrc.process((dsp::Frame<1> *) output, &inLen, outputBuffer.endData(), &outLen); outputBuffer.endIncr(outLen); } } @@ -123,14 +123,14 @@ struct SpringReverb : Module { // Set lights float lightRate = 5.0 * args.sampleTime; - vuFilter.setLambda(1.0f - lightRate); + vuFilter.setLambda(1.f - lightRate); float vuValue = vuFilter.process(1.f, std::fabs(wet)); - lightFilter.setLambda(1.0f - lightRate); - float lightValue = lightFilter.process(1.f, std::fabs(dry*50.0)); + lightFilter.setLambda(1.f - lightRate); + float lightValue = lightFilter.process(1.f, std::fabs(dry * 50.0)); for (int i = 0; i < 7; i++) { float light = std::pow(1.413, i) * vuValue / 10.0 - 1.0; - lights[VU1_LIGHT + i].value = clamp(light, 0.0f, 1.0f); + lights[VU1_LIGHTS + i].value = clamp(light, 0.0f, 1.0f); } lights[PEAK_LIGHT].value = lightValue; } @@ -144,8 +144,8 @@ struct SpringReverbWidget : ModuleWidget { addChild(createWidget(Vec(15, 0))); addChild(createWidget(Vec(15, 365))); - addChild(createWidget(Vec(15*6, 0))); - addChild(createWidget(Vec(15*6, 365))); + addChild(createWidget(Vec(15 * 6, 0))); + addChild(createWidget(Vec(15 * 6, 365))); addParam(createParam(Vec(22, 29), module, SpringReverb::WET_PARAM)); @@ -164,13 +164,13 @@ struct SpringReverbWidget : ModuleWidget { addOutput(createOutput(Vec(88, 317), module, SpringReverb::WET_OUTPUT)); addChild(createLight>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT)); - addChild(createLight>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0)); - addChild(createLight>(Vec(55, 126), module, SpringReverb::VU1_LIGHT + 1)); - addChild(createLight>(Vec(55, 138), module, SpringReverb::VU1_LIGHT + 2)); - addChild(createLight>(Vec(55, 150), module, SpringReverb::VU1_LIGHT + 3)); - addChild(createLight>(Vec(55, 163), module, SpringReverb::VU1_LIGHT + 4)); - addChild(createLight>(Vec(55, 175), module, SpringReverb::VU1_LIGHT + 5)); - addChild(createLight>(Vec(55, 188), module, SpringReverb::VU1_LIGHT + 6)); + addChild(createLight>(Vec(55, 113), module, SpringReverb::VU1_LIGHTS + 0)); + addChild(createLight>(Vec(55, 126), module, SpringReverb::VU1_LIGHTS + 1)); + addChild(createLight>(Vec(55, 138), module, SpringReverb::VU1_LIGHTS + 2)); + addChild(createLight>(Vec(55, 150), module, SpringReverb::VU1_LIGHTS + 3)); + addChild(createLight>(Vec(55, 163), module, SpringReverb::VU1_LIGHTS + 4)); + addChild(createLight>(Vec(55, 175), module, SpringReverb::VU1_LIGHTS + 5)); + addChild(createLight>(Vec(55, 188), module, SpringReverb::VU1_LIGHTS + 6)); } }; diff --git a/src/simd_input.hpp b/src/simd_input.hpp new file mode 100644 index 0000000..4b1265f --- /dev/null +++ b/src/simd_input.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "rack.hpp" + + +inline void load_input(Input &in, simd::float_4 *v, int numChannels) { + int inChannels = in.getChannels(); + if (inChannels == 1) { + for (int i = 0; i < numChannels; i++) + v[i] = simd::float_4(in.getVoltage()); + } + else { + for (int c = 0; c < inChannels; c += 4) + v[c / 4] = simd::float_4::load(in.getVoltages(c)); + } +} + +inline void add_input(Input &in, simd::float_4 *v, int numChannels) { + int inChannels = in.getChannels(); + if (inChannels == 1) { + for (int i = 0; i < numChannels; i++) + v[i] += simd::float_4(in.getVoltage()); + } + else { + for (int c = 0; c < inChannels; c += 4) + v[c / 4] += simd::float_4::load(in.getVoltages(c)); + } +}