#include "LFO.hpp" #include "dsp/pitch.hpp" void LFO::onReset() { _resetTrigger.reset(); _modulationStep = modulationSteps; _sampleStep = _phasor._sampleRate; } void LFO::onSampleRateChange() { _phasor.setSampleRate(engineGetSampleRate()); _modulationStep = modulationSteps; _sampleStep = _phasor._sampleRate; } void LFO::step() { lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].value > 0.5f; if (!( outputs[SINE_OUTPUT].active || outputs[TRIANGLE_OUTPUT].active || outputs[RAMP_UP_OUTPUT].active || outputs[RAMP_DOWN_OUTPUT].active || outputs[SQUARE_OUTPUT].active )) { return; } ++_modulationStep; if (_modulationStep >= modulationSteps) { _modulationStep = 0; float frequency = params[FREQUENCY_PARAM].value; if (inputs[PITCH_INPUT].active) { frequency += inputs[PITCH_INPUT].value; } if (_slowMode) { frequency -= 8.0f; } else { frequency -= 4.0f; } frequency = cvToFrequency(frequency); if (frequency > 2000.0f) { frequency = 2000.0f; } _phasor.setFrequency(frequency); float pw = params[PW_PARAM].value; if (inputs[PW_INPUT].active) { pw *= clamp(inputs[PW_INPUT].value / 5.0f, -1.0f, 1.0f); } pw *= 1.0f - 2.0f * _square.minPulseWidth; pw *= 0.5f; pw += 0.5f; _square.setPulseWidth(pw); float sample = params[SAMPLE_PARAM].value; if (inputs[SAMPLE_INPUT].active) { sample *= clamp(inputs[SAMPLE_INPUT].value / 10.0f, 0.0f, 1.0f); } float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f; _sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); _offset = params[OFFSET_PARAM].value; if (inputs[OFFSET_INPUT].active) { _offset *= clamp(inputs[OFFSET_INPUT].value / 5.0f, -1.0f, 1.0f); } _offset *= 5.0f; _scale = params[SCALE_PARAM].value; if (inputs[SCALE_INPUT].active) { _scale *= clamp(inputs[SCALE_INPUT].value / 10.0f, 0.0f, 1.0f); } } if (_resetTrigger.next(inputs[RESET_INPUT].value)) { _phasor.resetPhase(); } _phasor.advancePhase(); bool useSample = false; if (_sampleSteps > 1) { ++_sampleStep; if (_sampleStep >= _sampleSteps) { _sampleStep = 0; } else { useSample = true; } } updateOutput(_sine, useSample, false, outputs[SINE_OUTPUT], _sineSample, _sineActive); updateOutput(_triangle, useSample, false, outputs[TRIANGLE_OUTPUT], _triangleSample, _triangleActive); updateOutput(_ramp, useSample, false, outputs[RAMP_UP_OUTPUT], _rampUpSample, _rampUpActive); updateOutput(_ramp, useSample, true, outputs[RAMP_DOWN_OUTPUT], _rampDownSample, _rampDownActive); updateOutput(_square, false, false, outputs[SQUARE_OUTPUT], _squareSample, _squareActive); } void LFO::updateOutput(Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active) { if (output.active) { if (useSample && active) { output.value = sample; } else { sample = wave.nextFromPhasor(_phasor) * amplitude * _scale + (invert ? -_offset : _offset); if (invert) { sample = -sample; } output.value = sample; } active = true; } else { active = false; } } struct LFOWidget : ModuleWidget { static constexpr int hp = 10; LFOWidget(LFO* module) : ModuleWidget(module) { box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/LFO.svg"))); addChild(panel); } addChild(Widget::create(Vec(0, 0))); addChild(Widget::create(Vec(box.size.x - 15, 0))); addChild(Widget::create(Vec(0, 365))); addChild(Widget::create(Vec(box.size.x - 15, 365))); // generated by svg_widgets.rb auto frequencyParamPosition = Vec(41.0, 45.0); auto slowParamPosition = Vec(120.0, 249.0); auto sampleParamPosition = Vec(37.0, 150.0); auto pwParamPosition = Vec(102.0, 150.0); auto offsetParamPosition = Vec(42.0, 196.0); auto scaleParamPosition = Vec(107.0, 196.0); auto sampleInputPosition = Vec(15.0, 230.0); auto pwInputPosition = Vec(47.0, 230.0); auto offsetInputPosition = Vec(15.0, 274.0); auto scaleInputPosition = Vec(47.0, 274.0); auto pitchInputPosition = Vec(15.0, 318.0); auto resetInputPosition = Vec(47.0, 318.0); auto rampDownOutputPosition = Vec(79.0, 230.0); auto rampUpOutputPosition = Vec(79.0, 274.0); auto squareOutputPosition = Vec(111.0, 274.0); auto triangleOutputPosition = Vec(79.0, 318.0); auto sineOutputPosition = Vec(111.0, 318.0); auto slowLightPosition = Vec(111.0, 240.0); // end generated by svg_widgets.rb addParam(ParamWidget::create(frequencyParamPosition, module, LFO::FREQUENCY_PARAM, -8.0, 5.0, 0.0)); addParam(ParamWidget::create(slowParamPosition, module, LFO::SLOW_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(sampleParamPosition, module, LFO::SAMPLE_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(pwParamPosition, module, LFO::PW_PARAM, -1.0, 1.0, 0.0)); addParam(ParamWidget::create(offsetParamPosition, module, LFO::OFFSET_PARAM, -1.0, 1.0, 0.0)); addParam(ParamWidget::create(scaleParamPosition, module, LFO::SCALE_PARAM, 0.0, 1.0, 1.0)); addInput(Port::create(sampleInputPosition, Port::INPUT, module, LFO::SAMPLE_INPUT)); addInput(Port::create(pwInputPosition, Port::INPUT, module, LFO::PW_INPUT)); addInput(Port::create(offsetInputPosition, Port::INPUT, module, LFO::OFFSET_INPUT)); addInput(Port::create(scaleInputPosition, Port::INPUT, module, LFO::SCALE_INPUT)); addInput(Port::create(pitchInputPosition, Port::INPUT, module, LFO::PITCH_INPUT)); addInput(Port::create(resetInputPosition, Port::INPUT, module, LFO::RESET_INPUT)); addOutput(Port::create(rampUpOutputPosition, Port::OUTPUT, module, LFO::RAMP_UP_OUTPUT)); addOutput(Port::create(rampDownOutputPosition, Port::OUTPUT, module, LFO::RAMP_DOWN_OUTPUT)); addOutput(Port::create(squareOutputPosition, Port::OUTPUT, module, LFO::SQUARE_OUTPUT)); addOutput(Port::create(triangleOutputPosition, Port::OUTPUT, module, LFO::TRIANGLE_OUTPUT)); addOutput(Port::create(sineOutputPosition, Port::OUTPUT, module, LFO::SINE_OUTPUT)); addChild(ModuleLightWidget::create>(slowLightPosition, module, LFO::SLOW_LIGHT)); } }; RACK_PLUGIN_MODEL_INIT(Bogaudio, LFO) { Model *modelLFO = createModel("Bogaudio-LFO", "LFO", "low-frequency oscillator", LFO_TAG); return modelLFO; }