@@ -131,18 +131,18 @@ struct LFO : Module { | |||
// FM1, polyphonic | |||
pitch += float_4::load(inputs[FM1_INPUT].getVoltages(c)) * fm1Param; | |||
// FM2, polyphonic or monophonic | |||
if (inputs[FM2_INPUT].getChannels() == 1) | |||
pitch += inputs[FM2_INPUT].getVoltage() * fm2Param; | |||
else | |||
if (inputs[FM2_INPUT].isPolyphonic()) | |||
pitch += float_4::load(inputs[FM2_INPUT].getVoltages(c)) * fm2Param; | |||
else | |||
pitch += inputs[FM2_INPUT].getVoltage() * fm2Param; | |||
oscillator->setPitch(pitch); | |||
// Pulse width | |||
float_4 pw = pwParam; | |||
if (inputs[PW_INPUT].getChannels() == 1) | |||
pw += inputs[PW_INPUT].getVoltage() / 10.f * pwmParam; | |||
else | |||
if (inputs[PW_INPUT].isPolyphonic()) | |||
pw += float_4::load(inputs[PW_INPUT].getVoltages(c)) / 10.f * pwmParam; | |||
else | |||
pw += inputs[PW_INPUT].getVoltage() / 10.f * pwmParam; | |||
oscillator->setPulseWidth(pw); | |||
// Settings | |||
@@ -284,10 +284,10 @@ struct LFO2 : Module { | |||
// Wave | |||
float_4 wave = waveParam; | |||
inputs[WAVE_INPUT].getVoltage(); | |||
if (inputs[WAVE_INPUT].getChannels() == 1) | |||
wave += inputs[WAVE_INPUT].getVoltage() / 10.f * 3.f; | |||
else | |||
if (inputs[WAVE_INPUT].isPolyphonic()) | |||
wave += float_4::load(inputs[WAVE_INPUT].getVoltages(c)) / 10.f * 3.f; | |||
else | |||
wave += inputs[WAVE_INPUT].getVoltage() / 10.f * 3.f; | |||
wave = clamp(wave, 0.f, 3.f); | |||
// Settings | |||
@@ -42,7 +42,8 @@ struct Merge : Module { | |||
outputs[POLY_OUTPUT].setVoltage(v, c); | |||
} | |||
outputs[POLY_OUTPUT].setChannels((channels >= 0) ? channels : (lastChannel + 1)); | |||
// In order to allow 0 channels, modify channels directly instead of using `setChannels()` | |||
outputs[POLY_OUTPUT].channels = (channels >= 0) ? channels : (lastChannel + 1); | |||
// Set channel lights infrequently | |||
if (lightDivider.process()) { | |||
@@ -85,13 +85,13 @@ struct Scope : Module { | |||
int frameCount = (int) std::ceil(deltaTime * args.sampleRate); | |||
// Set channels | |||
int channelsX = inputs[X_INPUT].isConnected() ? inputs[X_INPUT].getChannels() : 0; | |||
int channelsX = inputs[X_INPUT].getChannels(); | |||
if (channelsX != this->channelsX) { | |||
std::memset(bufferX, 0, sizeof(bufferX)); | |||
this->channelsX = channelsX; | |||
} | |||
int channelsY = inputs[Y_INPUT].isConnected() ? inputs[Y_INPUT].getChannels() : 0; | |||
int channelsY = inputs[Y_INPUT].getChannels(); | |||
if (channelsY != this->channelsY) { | |||
std::memset(bufferY, 0, sizeof(bufferY)); | |||
this->channelsY = channelsY; | |||
@@ -28,7 +28,7 @@ struct Split : Module { | |||
void process(const ProcessArgs &args) override { | |||
for (int c = 0; c < 16; c++) { | |||
float v = inputs[POLY_INPUT].getVoltage(c); | |||
// To allow users to debug buggy modules, don't assume that undefined channels are 0V. | |||
// To allow users to debug buggy modules, don't assume that undefined channel voltages are 0V. | |||
outputs[MONO_OUTPUTS + c].setVoltage(v); | |||
} | |||
@@ -29,9 +29,6 @@ struct VCA : Module { | |||
} | |||
void processChannel(Input &in, Param &level, Input &lin, Input &exp, Output &out) { | |||
if (!in.isConnected() || !out.isConnected()) | |||
return; | |||
// Get input | |||
int channels = in.getChannels(); | |||
simd::float_4 v[4]; | |||
@@ -47,17 +44,17 @@ struct VCA : Module { | |||
// Apply linear CV gain | |||
if (lin.isConnected()) { | |||
if (lin.getChannels() == 1) { | |||
float cv = lin.getVoltage() / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
if (lin.isPolyphonic()) { | |||
for (int c = 0; c < channels; c += 4) { | |||
simd::float_4 cv = simd::float_4::load(lin.getVoltages(c)) / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
v[c / 4] *= cv; | |||
} | |||
} | |||
else { | |||
float cv = lin.getVoltage() / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
for (int c = 0; c < channels; c += 4) { | |||
simd::float_4 cv = simd::float_4::load(lin.getVoltages(c)) / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
v[c / 4] *= cv; | |||
} | |||
} | |||
@@ -66,19 +63,19 @@ struct VCA : Module { | |||
// Apply exponential CV gain | |||
const float expBase = 50.f; | |||
if (exp.isConnected()) { | |||
if (exp.getChannels() == 1) { | |||
float cv = exp.getVoltage() / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
cv = rescale(std::pow(expBase, cv), 1.f, expBase, 0.f, 1.f); | |||
if (exp.isPolyphonic()) { | |||
for (int c = 0; c < channels; c += 4) { | |||
simd::float_4 cv = simd::float_4::load(exp.getVoltages(c)) / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
cv = rescale(pow(expBase, cv), 1.f, expBase, 0.f, 1.f); | |||
v[c / 4] *= cv; | |||
} | |||
} | |||
else { | |||
float cv = exp.getVoltage() / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
cv = rescale(std::pow(expBase, cv), 1.f, expBase, 0.f, 1.f); | |||
for (int c = 0; c < channels; c += 4) { | |||
simd::float_4 cv = simd::float_4::load(exp.getVoltages(c)) / 10.f; | |||
cv = clamp(cv, 0.f, 1.f); | |||
cv = rescale(pow(expBase, cv), 1.f, expBase, 0.f, 1.f); | |||
v[c / 4] *= cv; | |||
} | |||
} | |||
@@ -128,12 +128,10 @@ struct VCF : Module { | |||
// Drive gain | |||
float_4 drive = driveParam; | |||
if (inputs[DRIVE_INPUT].isConnected()) { | |||
if (inputs[DRIVE_INPUT].isMonophonic()) | |||
drive += inputs[DRIVE_INPUT].getVoltage() / 10.f; | |||
else | |||
drive += float_4::load(inputs[DRIVE_INPUT].getVoltages(c)) / 10.f; | |||
} | |||
if (inputs[DRIVE_INPUT].isPolyphonic()) | |||
drive += float_4::load(inputs[DRIVE_INPUT].getVoltages(c)) / 10.f; | |||
else | |||
drive += inputs[DRIVE_INPUT].getVoltage() / 10.f; | |||
drive = clamp(drive, 0.f, 1.f); | |||
float_4 gain = simd::pow(1.f + drive, 5); | |||
input *= gain; | |||
@@ -143,23 +141,19 @@ struct VCF : Module { | |||
// Set resonance | |||
float_4 resonance = resParam; | |||
if (inputs[RES_INPUT].isConnected()) { | |||
if (inputs[RES_INPUT].isMonophonic()) | |||
resonance += inputs[RES_INPUT].getVoltage() / 10.f; | |||
else | |||
resonance += float_4::load(inputs[RES_INPUT].getVoltages(c)) / 10.f; | |||
} | |||
if (inputs[RES_INPUT].isPolyphonic()) | |||
resonance += float_4::load(inputs[RES_INPUT].getVoltages(c)) / 10.f; | |||
else | |||
resonance += inputs[RES_INPUT].getVoltage() / 10.f; | |||
resonance = clamp(resonance, 0.f, 1.f); | |||
filter->resonance = simd::pow(resonance, 2) * 10.f; | |||
// Get pitch | |||
float_4 pitch = 0.f; | |||
if (inputs[FREQ_INPUT].isConnected()) { | |||
if (inputs[FREQ_INPUT].isMonophonic()) | |||
pitch += inputs[FREQ_INPUT].getVoltage() * freqCvParam; | |||
else | |||
pitch += float_4::load(inputs[FREQ_INPUT].getVoltages(c)) * freqCvParam; | |||
} | |||
if (inputs[FREQ_INPUT].isPolyphonic()) | |||
pitch += float_4::load(inputs[FREQ_INPUT].getVoltages(c)) * freqCvParam; | |||
else | |||
pitch += inputs[FREQ_INPUT].getVoltage() * freqCvParam; | |||
pitch += freqParam; | |||
pitch += fineParam; | |||
// Set cutoff | |||
@@ -17,7 +17,7 @@ struct Viz : Module { | |||
NUM_LIGHTS | |||
}; | |||
int lastChannels = 0; | |||
int lastChannel = 0; | |||
dsp::ClockDivider lightDivider; | |||
Viz() { | |||
@@ -27,7 +27,7 @@ struct Viz : Module { | |||
void process(const ProcessArgs &args) override { | |||
if (lightDivider.process()) { | |||
lastChannels = inputs[POLY_INPUT].getChannels(); | |||
lastChannel = inputs[POLY_INPUT].getChannels(); | |||
float deltaTime = args.sampleTime * lightDivider.getDivision(); | |||
for (int c = 0; c < 16; c++) { | |||
@@ -58,7 +58,7 @@ struct VizDisplay : Widget { | |||
nvgFontSize(args.vg, 11); | |||
nvgTextLetterSpacing(args.vg, 0.0); | |||
nvgTextAlign(args.vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); | |||
if (module && c < module->lastChannels) | |||
if (module && c < module->lastChannel) | |||
nvgFillColor(args.vg, nvgRGB(255, 255, 255)); | |||
else | |||
nvgFillColor(args.vg, nvgRGB(99, 99, 99)); | |||