#include "Fundamental.hpp" struct LFOGenerator { float phase = 0.0; float pw = 0.5; float freq = 1.0; float offset = 0.0; float factor = 1.0; void setPitch(float pitch) { pitch = fminf(pitch, 8.0); freq = powf(2.0, pitch); } void setPulseWidth(float pw_) { const float pwMin = 0.01; pw = clampf(pw_, pwMin, 1.0 - pwMin); } void setOffset(bool offset_) { offset = offset_ ? 1.0 : 0.0; } void setInvert(bool invert) { factor = invert ? -1.0 : 1.0; } void step(float dt) { float deltaPhase = fminf(freq * dt, 0.5); phase += deltaPhase; if (phase >= 1.0) phase -= 1.0; } float sin() { float sin = sinf(2*M_PI * phase); return factor * sin + offset; } float tri() { float tri; if (phase < 0.25) tri = 4.0*phase; else if (phase < 0.75) tri = 2.0 - 4.0*phase; else tri = -4.0 + 4.0*phase; return factor * tri + offset; } float saw() { float saw; if (phase < 0.5) saw = 2.0*phase; else saw = -2.0 + 2.0*phase; return factor * saw + offset; } float sqr() { float sqr = phase < pw ? 1.0 : -1.0; return factor * sqr + offset; } }; struct LFO : Module { enum ParamIds { OFFSET_PARAM, INVERT_PARAM, FREQ_PARAM, FM1_PARAM, FM2_PARAM, PW_PARAM, PWM_PARAM, NUM_PARAMS }; enum InputIds { FM1_INPUT, FM2_INPUT, RESET_INPUT, PW_INPUT, NUM_INPUTS }; enum OutputIds { SIN_OUTPUT, TRI_OUTPUT, SAW_OUTPUT, SQR_OUTPUT, NUM_OUTPUTS }; LFOGenerator generator; float lights[1] = {}; LFO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; void LFO::step() { generator.setPitch(params[FREQ_PARAM].value + params[FM1_PARAM].value * inputs[FM1_INPUT].value + params[FM2_PARAM].value * inputs[FM2_INPUT].value); generator.setPulseWidth(params[PW_PARAM].value + params[PWM_PARAM].value * inputs[PW_INPUT].value / 10.0); generator.setOffset(params[OFFSET_PARAM].value > 0.0); generator.setInvert(params[INVERT_PARAM].value <= 0.0); generator.step(1.0 / gSampleRate); outputs[SIN_OUTPUT].value = 5.0 * generator.sin(); outputs[TRI_OUTPUT].value = 5.0 * generator.tri(); outputs[SAW_OUTPUT].value = 5.0 * generator.saw(); outputs[SQR_OUTPUT].value = 5.0 * generator.sqr(); lights[0] = -1.0 + 2.0*generator.phase; } LFOWidget::LFOWidget() { LFO *module = new LFO(); setModule(module); box.size = Vec(15*10, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/LFO-1.svg"))); addChild(panel); } addChild(createScrew(Vec(15, 0))); addChild(createScrew(Vec(box.size.x-30, 0))); addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); addParam(createParam(Vec(15, 77), module, LFO::OFFSET_PARAM, 0.0, 1.0, 1.0)); addParam(createParam(Vec(119, 77), module, LFO::INVERT_PARAM, 0.0, 1.0, 1.0)); addParam(createParam(Vec(47, 61), module, LFO::FREQ_PARAM, -8.0, 6.0, -1.0)); addParam(createParam(Vec(23, 143), module, LFO::FM1_PARAM, 0.0, 1.0, 0.0)); addParam(createParam(Vec(91, 143), module, LFO::PW_PARAM, 0.0, 1.0, 0.5)); addParam(createParam(Vec(23, 208), module, LFO::FM2_PARAM, 0.0, 1.0, 0.0)); addParam(createParam(Vec(91, 208), module, LFO::PWM_PARAM, 0.0, 1.0, 0.0)); addInput(createInput(Vec(11, 276), module, LFO::FM1_INPUT)); addInput(createInput(Vec(45, 276), module, LFO::FM2_INPUT)); addInput(createInput(Vec(80, 276), module, LFO::RESET_INPUT)); addInput(createInput(Vec(114, 276), module, LFO::PW_INPUT)); addOutput(createOutput(Vec(11, 320), module, LFO::SIN_OUTPUT)); addOutput(createOutput(Vec(45, 320), module, LFO::TRI_OUTPUT)); addOutput(createOutput(Vec(80, 320), module, LFO::SAW_OUTPUT)); addOutput(createOutput(Vec(114, 320), module, LFO::SQR_OUTPUT)); addChild(createValueLight>(Vec(99, 42), &module->lights[0])); } struct LFO2 : Module { enum ParamIds { OFFSET_PARAM, INVERT_PARAM, FREQ_PARAM, WAVE_PARAM, FM_PARAM, NUM_PARAMS }; enum InputIds { FM_INPUT, RESET_INPUT, WAVE_INPUT, NUM_INPUTS }; enum OutputIds { INTERP_OUTPUT, NUM_OUTPUTS }; LFOGenerator generator; float lights[1] = {}; LFO2() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} void step(); }; void LFO2::step() { generator.setPitch(params[FREQ_PARAM].value + params[FM_PARAM].value * inputs[FM_INPUT].value); generator.setOffset(params[OFFSET_PARAM].value > 0.0); generator.setInvert(params[INVERT_PARAM].value <= 0.0); generator.step(1.0 / gSampleRate); float wave = params[WAVE_PARAM].value + inputs[WAVE_INPUT].value; wave = clampf(wave, 0.0, 3.0); float interp; if (wave < 1.0) interp = crossf(generator.sin(), generator.tri(), wave); else if (wave < 2.0) interp = crossf(generator.tri(), generator.saw(), wave - 1.0); else interp = crossf(generator.saw(), generator.sqr(), wave - 2.0); outputs[INTERP_OUTPUT].value = 5.0 * interp; lights[0] = -1.0 + 2.0*generator.phase; } LFO2Widget::LFO2Widget() { LFO2 *module = new LFO2(); setModule(module); box.size = Vec(15*6, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/LFO-2.svg"))); addChild(panel); } addChild(createScrew(Vec(15, 0))); addChild(createScrew(Vec(box.size.x-30, 0))); addChild(createScrew(Vec(15, 365))); addChild(createScrew(Vec(box.size.x-30, 365))); addParam(createParam(Vec(62, 150), module, LFO2::OFFSET_PARAM, 0.0, 1.0, 1.0)); addParam(createParam(Vec(62, 215), module, LFO2::INVERT_PARAM, 0.0, 1.0, 1.0)); addParam(createParam(Vec(18, 60), module, LFO2::FREQ_PARAM, -8.0, 6.0, -1.0)); addParam(createParam(Vec(11, 142), module, LFO2::WAVE_PARAM, 0.0, 3.0, 0.0)); addParam(createParam(Vec(11, 207), module, LFO2::FM_PARAM, 0.0, 1.0, 0.5)); addInput(createInput(Vec(11, 276), module, LFO2::FM_INPUT)); addInput(createInput(Vec(54, 276), module, LFO2::RESET_INPUT)); addInput(createInput(Vec(11, 319), module, LFO2::WAVE_INPUT)); addOutput(createOutput(Vec(54, 319), module, LFO2::INTERP_OUTPUT)); addChild(createValueLight>(Vec(68, 41), &module->lights[0])); }