Browse Source

Make LFO-1 and LFO-2 polyphonic.

tags/v1.0.1
Andrew Belt 3 years ago
parent
commit
15dd52b7e4
2 changed files with 196 additions and 91 deletions
  1. +6
    -3
      plugin.json
  2. +190
    -88
      src/LFO.cpp

+ 6
- 3
plugin.json View File

@@ -60,7 +60,8 @@
"name": "LFO-1",
"description": "Low-frequency oscillator",
"tags": [
"LFO"
"LFO",
"Polyphonic"
]
},
{
@@ -68,7 +69,8 @@
"name": "LFO-2",
"description": "Low-frequency oscillator",
"tags": [
"LFO"
"LFO",
"Polyphonic"
]
},
{
@@ -130,7 +132,8 @@
"name": "Scope",
"description": "Inspect waveforms with an oscilloscope",
"tags": [
"Visual"
"Visual",
"Polyphonic"
]
},
{


+ 190
- 88
src/LFO.cpp View File

@@ -1,64 +1,69 @@
#include "plugin.hpp"


template <typename T>
struct LowFrequencyOscillator {
float phase = 0.f;
float pw = 0.5f;
float freq = 1.f;
bool offset = false;
T phase = 0.f;
T pw = 0.5f;
T freq = 1.f;
bool invert = false;
dsp::SchmittTrigger resetTrigger;
bool bipolar = false;
T resetState = T::mask();

LowFrequencyOscillator() {}
void setPitch(float pitch) {
pitch = std::fmin(pitch, 10.f);
freq = std::pow(2.f, pitch);
void setPitch(T pitch) {
pitch = simd::fmin(pitch, 10.f);
freq = simd::pow(2.f, pitch);
}
void setPulseWidth(float pw_) {
const float pwMin = 0.01f;
pw = clamp(pw_, pwMin, 1.f - pwMin);
void setPulseWidth(T pw) {
const T pwMin = 0.01f;
this->pw = clamp(pw, pwMin, 1.f - pwMin);
}
void setReset(float reset) {
if (resetTrigger.process(reset / 0.01f)) {
phase = 0.f;
}
void setReset(T reset) {
reset = simd::rescale(reset, 0.1f, 2.f, 0.f, 1.f);
T on = (reset >= 1.f);
T off = (reset <= 0.f);
T triggered = ~resetState & on;
resetState = simd::ifelse(off, 0.f, resetState);
resetState = simd::ifelse(on, T::mask(), resetState);
phase = simd::ifelse(triggered, 0.f, phase);
}
void step(float dt) {
float deltaPhase = std::fmin(freq * dt, 0.5f);
T deltaPhase = simd::fmin(freq * dt, 0.5f);
phase += deltaPhase;
if (phase >= 1.f)
phase -= 1.f;
}
float sin() {
if (offset)
return 1.f - std::cos(2*M_PI * phase) * (invert ? -1.f : 1.f);
else
return std::sin(2*M_PI * phase) * (invert ? -1.f : 1.f);
}
float tri(float x) {
return 4.f * std::fabs(x - std::round(x));
phase -= (phase >= 1.f) & 1.f;
}
float tri() {
if (offset)
return tri(invert ? phase - 0.5f : phase);
else
return -1.f + tri(invert ? phase - 0.25f : phase - 0.75f);
T sin() {
T p = phase;
if (!bipolar) p -= 0.25f;
T v = simd::sin(2*M_PI * p);
if (invert) v *= -1.f;
if (!bipolar) v += 1.f;
return v;
}
float saw(float x) {
return 2.f * (x - std::round(x));
T tri() {
T p = phase;
if (bipolar) p += 0.25f;
T v = 4.f * simd::fabs(p - simd::round(p)) - 1.f;
if (invert) v *= -1.f;
if (!bipolar) v += 1.f;
return v;
}
float saw() {
if (offset)
return invert ? 2.f * (1.f - phase) : 2.f * phase;
else
return saw(phase) * (invert ? -1.f : 1.f);
T saw() {
T p = phase;
if (!bipolar) p -= 0.5f;
T v = 2.f * (p - simd::round(p));
if (invert) v *= -1.f;
if (!bipolar) v += 1.f;
return v;
}
float sqr() {
float sqr = (phase < pw) ^ invert ? 1.f : -1.f;
return offset ? sqr + 1.f : sqr;
T sqr() {
T v = simd::ifelse(phase < pw, 1.f, -1.f);
if (invert) v *= -1.f;
if (!bipolar) v += 1.f;
return v;
}
float light() {
return std::sin(2*M_PI * phase);
T light() {
return 1.f - 2.f * phase;
}
};

@@ -89,12 +94,12 @@ struct LFO : Module {
NUM_OUTPUTS
};
enum LightIds {
PHASE_POS_LIGHT,
PHASE_NEG_LIGHT,
ENUMS(PHASE_LIGHT, 3),
NUM_LIGHTS
};

LowFrequencyOscillator oscillator;
LowFrequencyOscillator<simd::float_4> oscillators[4];
dsp::ClockDivider lightDivider;

LFO() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -105,25 +110,84 @@ struct LFO : Module {
configParam(PW_PARAM, 0.f, 1.f, 0.5f);
configParam(FM2_PARAM, 0.f, 1.f, 0.f);
configParam(PWM_PARAM, 0.f, 1.f, 0.f);
lightDivider.setDivision(16);
}

void process(const ProcessArgs &args) override {
oscillator.setPitch(params[FREQ_PARAM].getValue() + params[FM1_PARAM].getValue() * inputs[FM1_INPUT].getVoltage() + params[FM2_PARAM].getValue() * inputs[FM2_INPUT].getVoltage());
oscillator.setPulseWidth(params[PW_PARAM].getValue() + params[PWM_PARAM].getValue() * inputs[PW_INPUT].getVoltage() / 10.f);
oscillator.offset = (params[OFFSET_PARAM].getValue() > 0.f);
oscillator.invert = (params[INVERT_PARAM].getValue() <= 0.f);
oscillator.step(args.sampleTime);
oscillator.setReset(inputs[RESET_INPUT].getVoltage());

outputs[SIN_OUTPUT].setVoltage(5.f * oscillator.sin());
outputs[TRI_OUTPUT].setVoltage(5.f * oscillator.tri());
outputs[SAW_OUTPUT].setVoltage(5.f * oscillator.saw());
outputs[SQR_OUTPUT].setVoltage(5.f * oscillator.sqr());

lights[PHASE_POS_LIGHT].setSmoothBrightness(oscillator.light(), args.sampleTime);
lights[PHASE_NEG_LIGHT].setSmoothBrightness(-oscillator.light(), args.sampleTime);
}
using simd::float_4;

float freqParam = params[FREQ_PARAM].getValue();
float fm1Param = params[FM1_PARAM].getValue();
float fm2Param = params[FM2_PARAM].getValue();
float pwParam = params[PW_PARAM].getValue();
float pwmParam = params[PWM_PARAM].getValue();

int channels = std::max(inputs[FM1_INPUT].getChannels(), inputs[FM2_INPUT].getChannels());

for (int c = 0; c < channels; c += 4) {
auto *oscillator = &oscillators[c / 4];
float_4 pitch = freqParam;
// 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
pitch += float_4::load(inputs[FM2_INPUT].getVoltages(c)) * 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
pw += float_4::load(inputs[PW_INPUT].getVoltages(c)) / 10.f * pwmParam;
oscillator->setPulseWidth(pw);

// Settings
oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
oscillator->step(args.sampleTime);
oscillator->setReset(inputs[RESET_INPUT].getVoltage());

// Outputs
if (outputs[SIN_OUTPUT].isConnected()) {
outputs[SIN_OUTPUT].setChannels(channels);
float_4 v = 5.f * oscillator->sin();
v.store(outputs[SIN_OUTPUT].getVoltages(c));
}
if (outputs[TRI_OUTPUT].isConnected()) {
outputs[TRI_OUTPUT].setChannels(channels);
float_4 v = 5.f * oscillator->tri();
v.store(outputs[TRI_OUTPUT].getVoltages(c));
}
if (outputs[SAW_OUTPUT].isConnected()) {
outputs[SAW_OUTPUT].setChannels(channels);
float_4 v = 5.f * oscillator->saw();
v.store(outputs[SAW_OUTPUT].getVoltages(c));
}
if (outputs[SQR_OUTPUT].isConnected()) {
outputs[SQR_OUTPUT].setChannels(channels);
float_4 v = 5.f * oscillator->sqr();
v.store(outputs[SQR_OUTPUT].getVoltages(c));
}
}

// Light
if (lightDivider.process()) {
if (channels == 1) {
float lightValue = oscillators[0].light().f[0];
lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
lights[PHASE_LIGHT + 2].setBrightness(0.f);
}
else {
lights[PHASE_LIGHT + 0].setBrightness(0.f);
lights[PHASE_LIGHT + 1].setBrightness(0.f);
lights[PHASE_LIGHT + 2].setBrightness(1.f);
}
}
}
};


@@ -157,7 +221,7 @@ struct LFOWidget : ModuleWidget {
addOutput(createOutput<PJ301MPort>(Vec(80, 320), module, LFO::SAW_OUTPUT));
addOutput(createOutput<PJ301MPort>(Vec(114, 320), module, LFO::SQR_OUTPUT));

addChild(createLight<SmallLight<GreenRedLight>>(Vec(99, 42.5f), module, LFO::PHASE_POS_LIGHT));
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(99, 42.5f), module, LFO::PHASE_LIGHT));
}
};

@@ -185,12 +249,12 @@ struct LFO2 : Module {
NUM_OUTPUTS
};
enum LightIds {
PHASE_POS_LIGHT,
PHASE_NEG_LIGHT,
ENUMS(PHASE_LIGHT, 3),
NUM_LIGHTS
};

LowFrequencyOscillator oscillator;
LowFrequencyOscillator<simd::float_4> oscillators[4];
dsp::ClockDivider lightDivider;

LFO2() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
@@ -199,29 +263,67 @@ struct LFO2 : Module {
configParam(FREQ_PARAM, -8.f, 10.f, 1.f);
configParam(WAVE_PARAM, 0.f, 3.f, 1.5f);
configParam(FM_PARAM, 0.f, 1.f, 0.5f);
lightDivider.setDivision(16);
}

void process(const ProcessArgs &args) override {
float deltaTime = args.sampleTime;
oscillator.setPitch(params[FREQ_PARAM].getValue() + params[FM_PARAM].getValue() * inputs[FM_INPUT].getVoltage());
oscillator.offset = (params[OFFSET_PARAM].getValue() > 0.f);
oscillator.invert = (params[INVERT_PARAM].getValue() <= 0.f);
oscillator.step(deltaTime);
oscillator.setReset(inputs[RESET_INPUT].getVoltage());

float wave = params[WAVE_PARAM].getValue() + inputs[WAVE_INPUT].getVoltage();
wave = clamp(wave, 0.f, 3.f);
float interp;
if (wave < 1.f)
interp = crossfade(oscillator.sin(), oscillator.tri(), wave);
else if (wave < 2.f)
interp = crossfade(oscillator.tri(), oscillator.saw(), wave - 1.f);
else
interp = crossfade(oscillator.saw(), oscillator.sqr(), wave - 2.f);
outputs[INTERP_OUTPUT].setVoltage(5.f * interp);

lights[PHASE_POS_LIGHT].setSmoothBrightness(oscillator.light(), deltaTime);
lights[PHASE_NEG_LIGHT].setSmoothBrightness(-oscillator.light(), deltaTime);
using simd::float_4;

float freqParam = params[FREQ_PARAM].getValue();
float waveParam = params[WAVE_PARAM].getValue();
float fmParam = params[FM_PARAM].getValue();

int channels = inputs[FM_INPUT].getChannels();

for (int c = 0; c < channels; c += 4) {
auto *oscillator = &oscillators[c / 4];
float_4 pitch = freqParam;
// FM, polyphonic
pitch += float_4::load(inputs[FM_INPUT].getVoltages(c)) * fmParam;
oscillator->setPitch(pitch);

// 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
wave += float_4::load(inputs[WAVE_INPUT].getVoltages(c)) / 10.f * 3.f;
wave = clamp(wave, 0.f, 3.f);

// Settings
oscillator->invert = (params[INVERT_PARAM].getValue() == 0.f);
oscillator->bipolar = (params[OFFSET_PARAM].getValue() == 0.f);
oscillator->step(args.sampleTime);
oscillator->setReset(inputs[RESET_INPUT].getVoltage());

// Outputs
if (outputs[INTERP_OUTPUT].isConnected()) {
outputs[INTERP_OUTPUT].setChannels(channels);
float_4 v = 0.f;
v += oscillator->sin() * simd::fmax(0.f, 1.f - simd::fabs(wave - 0.f));
v += oscillator->tri() * simd::fmax(0.f, 1.f - simd::fabs(wave - 1.f));
v += oscillator->saw() * simd::fmax(0.f, 1.f - simd::fabs(wave - 2.f));
v += oscillator->sqr() * simd::fmax(0.f, 1.f - simd::fabs(wave - 3.f));
v *= 5.f;
v.store(outputs[INTERP_OUTPUT].getVoltages(c));
}
}

// Light
if (lightDivider.process()) {
if (channels == 1) {
float lightValue = oscillators[0].light().f[0];
lights[PHASE_LIGHT + 0].setSmoothBrightness(-lightValue, args.sampleTime * lightDivider.getDivision());
lights[PHASE_LIGHT + 1].setSmoothBrightness(lightValue, args.sampleTime * lightDivider.getDivision());
lights[PHASE_LIGHT + 2].setBrightness(0.f);
}
else {
lights[PHASE_LIGHT + 0].setBrightness(0.f);
lights[PHASE_LIGHT + 1].setBrightness(0.f);
lights[PHASE_LIGHT + 2].setBrightness(1.f);
}
}
}
};

@@ -249,7 +351,7 @@ struct LFO2Widget : ModuleWidget {

addOutput(createOutput<PJ301MPort>(Vec(54, 319), module, LFO2::INTERP_OUTPUT));

addChild(createLight<SmallLight<GreenRedLight>>(Vec(68, 42.5f), module, LFO2::PHASE_POS_LIGHT));
addChild(createLight<SmallLight<RedGreenBlueLight>>(Vec(68, 42.5f), module, LFO2::PHASE_LIGHT));
}
};



Loading…
Cancel
Save