#include "VCO.hpp" #include "dsp/pitch.hpp" void VCO::onReset() { _syncTrigger.reset(); _modulationStep = modulationSteps; } void VCO::onSampleRateChange() { setSampleRate(engineGetSampleRate()); _modulationStep = modulationSteps; } void VCO::step() { lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].value > 0.5f; _fmLinearMode = params[FM_TYPE_PARAM].value < 0.5f; if (!( outputs[SQUARE_OUTPUT].active || outputs[SAW_OUTPUT].active || outputs[TRIANGLE_OUTPUT].active || outputs[SINE_OUTPUT].active )) { return; } ++_modulationStep; if (_modulationStep >= modulationSteps) { _modulationStep = 0; _baseVOct = params[FREQUENCY_PARAM].value; _baseVOct += params[FINE_PARAM].value / 12.0f; if (inputs[PITCH_INPUT].active) { _baseVOct += clamp(inputs[PITCH_INPUT].value, -5.0f, 5.0f); } if (_slowMode) { _baseVOct -= 7.0f; } _baseHz = cvToFrequency(_baseVOct); 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(_squarePulseWidthSL.next(pw)); _fmDepth = params[FM_PARAM].value; } if (_syncTrigger.next(inputs[SYNC_INPUT].value)) { _phasor.resetPhase(); } float frequency = _baseHz; Phasor::phase_delta_t phaseOffset = 0; if (inputs[FM_INPUT].active && _fmDepth > 0.01f) { float fm = inputs[FM_INPUT].value * _fmDepth; if (_fmLinearMode) { phaseOffset = Phasor::radiansToPhase(2.0f * fm); } else { frequency = cvToFrequency(_baseVOct + fm); } } setFrequency(frequency); const float oversampleWidth = 100.0f; float mix, oMix; if (frequency > _oversampleThreshold) { if (frequency > _oversampleThreshold + oversampleWidth) { mix = 0.0f; oMix = 1.0f; } else { oMix = (frequency - _oversampleThreshold) / oversampleWidth; mix = 1.0f - oMix; } } else { mix = 1.0f; oMix = 0.0f; } float squareOut = 0.0f; float sawOut = 0.0f; float triangleOut = 0.0f; if (oMix > 0.0f) { for (int i = 0; i < oversample; ++i) { _phasor.advancePhase(); if (outputs[SQUARE_OUTPUT].active) { _squareBuffer[i] = _square.nextFromPhasor(_phasor, phaseOffset); } if (outputs[SAW_OUTPUT].active) { _sawBuffer[i] = _saw.nextFromPhasor(_phasor, phaseOffset); } if (outputs[TRIANGLE_OUTPUT].active) { _triangleBuffer[i] = _triangle.nextFromPhasor(_phasor, phaseOffset); } } if (outputs[SQUARE_OUTPUT].active) { squareOut += oMix * amplitude * _squareDecimator.next(_squareBuffer); } if (outputs[SAW_OUTPUT].active) { sawOut += oMix * amplitude * _sawDecimator.next(_sawBuffer); } if (outputs[TRIANGLE_OUTPUT].active) { triangleOut += oMix * amplitude * _triangleDecimator.next(_triangleBuffer); } } else { _phasor.advancePhase(oversample); } if (mix > 0.0f) { if (outputs[SQUARE_OUTPUT].active) { squareOut += mix * amplitude * _square.nextFromPhasor(_phasor, phaseOffset); } if (outputs[SAW_OUTPUT].active) { sawOut += mix * amplitude * _saw.nextFromPhasor(_phasor, phaseOffset); } if (outputs[TRIANGLE_OUTPUT].active) { triangleOut += mix * amplitude * _triangle.nextFromPhasor(_phasor, phaseOffset); } } outputs[SQUARE_OUTPUT].value = squareOut; outputs[SAW_OUTPUT].value = sawOut; outputs[TRIANGLE_OUTPUT].value = triangleOut; outputs[SINE_OUTPUT].value = outputs[SINE_OUTPUT].active ? (amplitude * _sine.nextFromPhasor(_phasor, phaseOffset)) : 0.0f; } void VCO::setSampleRate(float sampleRate) { _oversampleThreshold = 0.06f * sampleRate; _phasor.setSampleRate(sampleRate); _square.setSampleRate(sampleRate); _saw.setSampleRate(sampleRate); _squareDecimator.setParams(sampleRate, oversample); _sawDecimator.setParams(sampleRate, oversample); _triangleDecimator.setParams(sampleRate, oversample); _squarePulseWidthSL.setParams(sampleRate, 0.1f, 2.0f); } void VCO::setFrequency(float frequency) { if (_frequency != frequency && frequency < 0.475f * _phasor._sampleRate) { _frequency = frequency; _phasor.setFrequency(_frequency / (float)oversample); _square.setFrequency(_frequency); _saw.setFrequency(_frequency); } } struct VCOWidget : ModuleWidget { static constexpr int hp = 10; VCOWidget(VCO* 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/VCO.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 fineParamPosition = Vec(48.0, 153.0); auto slowParamPosition = Vec(122.0, 157.2); auto pwParamPosition = Vec(62.0, 188.0); auto fmParamPosition = Vec(62.0, 230.0); auto fmTypeParamPosition = Vec(100.5, 231.5); auto pwInputPosition = Vec(15.0, 274.0); auto fmInputPosition = Vec(47.0, 274.0); auto pitchInputPosition = Vec(15.0, 318.0); auto syncInputPosition = Vec(47.0, 318.0); auto squareOutputPosition = Vec(79.0, 274.0); auto sawOutputPosition = Vec(111.0, 274.0); auto triangleOutputPosition = Vec(79.0, 318.0); auto sineOutputPosition = Vec(111.0, 318.0); auto slowLightPosition = Vec(82.0, 158.5); // end generated by svg_widgets.rb addParam(ParamWidget::create(frequencyParamPosition, module, VCO::FREQUENCY_PARAM, -3.0, 6.0, 0.0)); addParam(ParamWidget::create(fineParamPosition, module, VCO::FINE_PARAM, -1.0, 1.0, 0.0)); addParam(ParamWidget::create(slowParamPosition, module, VCO::SLOW_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(pwParamPosition, module, VCO::PW_PARAM, -1.0, 1.0, 0.0)); addParam(ParamWidget::create(fmParamPosition, module, VCO::FM_PARAM, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(fmTypeParamPosition, module, VCO::FM_TYPE_PARAM, 0.0, 1.0, 1.0)); addInput(Port::create(pitchInputPosition, Port::INPUT, module, VCO::PITCH_INPUT)); addInput(Port::create(syncInputPosition, Port::INPUT, module, VCO::SYNC_INPUT)); addInput(Port::create(pwInputPosition, Port::INPUT, module, VCO::PW_INPUT)); addInput(Port::create(fmInputPosition, Port::INPUT, module, VCO::FM_INPUT)); addOutput(Port::create(squareOutputPosition, Port::OUTPUT, module, VCO::SQUARE_OUTPUT)); addOutput(Port::create(sawOutputPosition, Port::OUTPUT, module, VCO::SAW_OUTPUT)); addOutput(Port::create(triangleOutputPosition, Port::OUTPUT, module, VCO::TRIANGLE_OUTPUT)); addOutput(Port::create(sineOutputPosition, Port::OUTPUT, module, VCO::SINE_OUTPUT)); addChild(ModuleLightWidget::create>(slowLightPosition, module, VCO::SLOW_LIGHT)); } }; RACK_PLUGIN_MODEL_INIT(Bogaudio, VCO) { Model *modelVCO = createModel("Bogaudio-VCO", "VCO", "oscillator", OSCILLATOR_TAG); return modelVCO; }