# Conflicts: # plugin.json # src/ABC.cpp # src/DualAtenuverter.cpp # src/EvenVCO.cpp # src/Mixer.cpp # src/Rampage.cpp # src/SlewLimiter.cpp # src/SpringReverb.cpp # src/plugin.hpptags/v1.1.0^2
@@ -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 @@ | |||
] | |||
} | |||
] | |||
} | |||
} |
@@ -1,17 +1,20 @@ | |||
#include "plugin.hpp" | |||
#include "Common.hpp" | |||
#include "simd_input.hpp" | |||
inline float clip(float x) { | |||
template <typename T> | |||
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<BefacoInputPort>(Vec(7, 279), module, ABC::C2_INPUT)); | |||
addOutput(createOutput<BefacoOutputPort>(Vec(7, 321), module, ABC::OUT2_OUTPUT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 162), module, ABC::OUT1_LIGHT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 329), module, ABC::OUT2_LIGHT)); | |||
addChild(createLight<MediumLight<RedGreenBlueLight>>(Vec(37, 162), module, ABC::OUT1_LIGHT)); | |||
addChild(createLight<MediumLight<RedGreenBlueLight>>(Vec(37, 329), module, ABC::OUT2_LIGHT)); | |||
} | |||
}; | |||
@@ -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<BefacoInputPort>(Vec(7, 319), module, DualAtenuverter::IN2_INPUT)); | |||
addOutput(createOutput<BefacoOutputPort>(Vec(43, 319), module, DualAtenuverter::OUT2_OUTPUT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 143), module, DualAtenuverter::OUT1_POS_LIGHT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 311), module, DualAtenuverter::OUT2_POS_LIGHT)); | |||
addChild(createLight<MediumLight<RedGreenBlueLight>>(Vec(33, 143), module, DualAtenuverter::OUT1_LIGHT)); | |||
addChild(createLight<MediumLight<RedGreenBlueLight>>(Vec(33, 311), module, DualAtenuverter::OUT2_LIGHT)); | |||
} | |||
}; | |||
@@ -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<Knurlie>(Vec(15, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
addChild(createWidget<Knurlie>(Vec(15*6, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15*6, 365))); | |||
addChild(createWidget<Knurlie>(Vec(15 * 6, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15 * 6, 365))); | |||
addParam(createParam<BefacoBigSnapKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM)); | |||
addParam(createParam<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM)); | |||
@@ -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<BefacoOutputPort>(Vec(7, 324), module, Mixer::OUT1_OUTPUT)); | |||
addOutput(createOutput<BefacoOutputPort>(Vec(43, 324), module, Mixer::OUT2_OUTPUT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); | |||
addChild(createLight<MediumLight<RedGreenBlueLight>>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); | |||
} | |||
}; | |||
@@ -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); | |||
} | |||
}; | |||
@@ -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<simd::float_4> 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<Knurlie>(Vec(15, 0))); | |||
addChild(createWidget<Knurlie>(Vec(box.size.x-30, 0))); | |||
addChild(createWidget<Knurlie>(Vec(box.size.x - 30, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
addChild(createWidget<Knurlie>(Vec(box.size.x-30, 365))); | |||
addChild(createWidget<Knurlie>(Vec(box.size.x - 30, 365))); | |||
addParam(createParam<BefacoSwitch>(Vec(94, 32), module, Rampage::RANGE_A_PARAM)); | |||
addParam(createParam<BefacoTinyKnob>(Vec(27, 90), module, Rampage::SHAPE_A_PARAM)); | |||
@@ -232,15 +376,15 @@ struct RampageWidget : ModuleWidget { | |||
addOutput(createOutput<BefacoOutputPort>(Vec(89, 157), module, Rampage::MIN_OUTPUT)); | |||
addOutput(createOutput<BefacoOutputPort>(Vec(155, 157), module, Rampage::MAX_OUTPUT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(141, 174), module, Rampage::MAX_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(126, 185), module, Rampage::OUT_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(138, 185), module, Rampage::OUT_B_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(18, 312), module, Rampage::RISING_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(187, 312), module, Rampage::RISING_B_LIGHT)); | |||
addChild(createLight<SmallLight<RedLight>>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(141, 174), module, Rampage::MAX_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(126, 185), module, Rampage::OUT_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(138, 185), module, Rampage::OUT_B_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(18, 312), module, Rampage::RISING_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(78, 312), module, Rampage::FALLING_A_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(187, 312), module, Rampage::RISING_B_LIGHT)); | |||
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(247, 312), module, Rampage::FALLING_B_LIGHT)); | |||
} | |||
}; | |||
@@ -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)); | |||
} | |||
} | |||
}; | |||
@@ -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<dsp::Frame<1>, 16*BLOCK_SIZE> inputBuffer; | |||
dsp::DoubleRingBuffer<dsp::Frame<1>, 16*BLOCK_SIZE> outputBuffer; | |||
dsp::DoubleRingBuffer<dsp::Frame<1>, 16 * BLOCK_SIZE> inputBuffer; | |||
dsp::DoubleRingBuffer<dsp::Frame<1>, 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<Knurlie>(Vec(15, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
addChild(createWidget<Knurlie>(Vec(15*6, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15*6, 365))); | |||
addChild(createWidget<Knurlie>(Vec(15 * 6, 0))); | |||
addChild(createWidget<Knurlie>(Vec(15 * 6, 365))); | |||
addParam(createParam<BefacoBigKnob>(Vec(22, 29), module, SpringReverb::WET_PARAM)); | |||
@@ -164,13 +164,13 @@ struct SpringReverbWidget : ModuleWidget { | |||
addOutput(createOutput<BefacoOutputPort>(Vec(88, 317), module, SpringReverb::WET_OUTPUT)); | |||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT)); | |||
addChild(createLight<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0)); | |||
addChild(createLight<MediumLight<YellowLight>>(Vec(55, 126), module, SpringReverb::VU1_LIGHT + 1)); | |||
addChild(createLight<MediumLight<YellowLight>>(Vec(55, 138), module, SpringReverb::VU1_LIGHT + 2)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 150), module, SpringReverb::VU1_LIGHT + 3)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 163), module, SpringReverb::VU1_LIGHT + 4)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 175), module, SpringReverb::VU1_LIGHT + 5)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 188), module, SpringReverb::VU1_LIGHT + 6)); | |||
addChild(createLight<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHTS + 0)); | |||
addChild(createLight<MediumLight<YellowLight>>(Vec(55, 126), module, SpringReverb::VU1_LIGHTS + 1)); | |||
addChild(createLight<MediumLight<YellowLight>>(Vec(55, 138), module, SpringReverb::VU1_LIGHTS + 2)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 150), module, SpringReverb::VU1_LIGHTS + 3)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 163), module, SpringReverb::VU1_LIGHTS + 4)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 175), module, SpringReverb::VU1_LIGHTS + 5)); | |||
addChild(createLight<MediumLight<GreenLight>>(Vec(55, 188), module, SpringReverb::VU1_LIGHTS + 6)); | |||
} | |||
}; | |||
@@ -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)); | |||
} | |||
} |