BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

commit d26ec5da70dd74f3e8df4326105ee14044cee5e2
parent 079511c3a3ab67b54aba42ab6d17153e91d4e402
Author: Matt Demanett <matt@demanett.net>
Date:   Tue, 24 Sep 2019 22:29:56 -0400

Poly: *FO.

Diffstat:
Msrc/EightFO.cpp | 141+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/EightFO.hpp | 96+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/LFO.cpp | 102++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/LFO.hpp | 67+++++++++++++++++++++++++++++++++++++------------------------------
Msrc/LLFO.cpp | 31+++++++++++++++++++++----------
Msrc/LLFO.hpp | 10++++++----
Msrc/dsp/oscillator.hpp | 4++--
Msrc/lfo_base.cpp | 4++--
Msrc/lfo_base.hpp | 2+-
9 files changed, 276 insertions(+), 181 deletions(-)

diff --git a/src/EightFO.cpp b/src/EightFO.cpp @@ -10,14 +10,30 @@ const Phasor::phase_delta_t basePhase2Offset = Phasor::radiansToPhase(0.5f * M_P const Phasor::phase_delta_t basePhase1Offset = Phasor::radiansToPhase(0.25f * M_PI); const Phasor::phase_delta_t basePhase0Offset = Phasor::radiansToPhase(0.0f); +void EightFO::Engine::reset() { + resetTrigger.reset(); + sampleStep = phasor._sampleRate; +} + +void EightFO::Engine::sampleRateChange() { + phasor.setSampleRate(APP->engine->getSampleRate()); + sampleStep = phasor._sampleRate; +} + void EightFO::reset() { - _resetTrigger.reset(); - _sampleStep = _phasor._sampleRate; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + _engines[c]->reset(); + } + } } void EightFO::sampleRateChange() { - _phasor.setSampleRate(APP->engine->getSampleRate()); - _sampleStep = _phasor._sampleRate; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + _engines[c]->sampleRateChange(); + } + } } bool EightFO::active() { @@ -33,93 +49,112 @@ bool EightFO::active() { ); } -void EightFO::modulate() { - setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor); +int EightFO::channels() { + return std::max(1, inputs[PITCH_INPUT].getChannels()); +} + +void EightFO::addEngine(int c) { + _engines[c] = new Engine(); + _engines[c]->reset(); + _engines[c]->sampleRateChange(); +} + +void EightFO::removeEngine(int c) { + delete _engines[c]; + _engines[c] = NULL; +} +void EightFO::modulate() { _wave = (Wave)roundf(params[WAVE_PARAM].getValue()); +} + +void EightFO::modulateChannel(int c) { + setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _engines[c]->phasor, c); + if (_wave == SQUARE_WAVE) { float pw = params[SAMPLE_PWM_PARAM].getValue(); if (inputs[SAMPLE_PWM_INPUT].isConnected()) { - pw *= clamp(inputs[SAMPLE_PWM_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f); + pw *= clamp(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } - pw *= 1.0f - 2.0f * _square.minPulseWidth; + pw *= 1.0f - 2.0f * _engines[c]->square.minPulseWidth; pw *= 0.5f; pw += 0.5f; - _square.setPulseWidth(pw); - _sampleSteps = 1; + _engines[c]->square.setPulseWidth(pw); + _engines[c]->sampleSteps = 1; } else { float sample = fabsf(params[SAMPLE_PWM_PARAM].getValue()); if (inputs[SAMPLE_PWM_INPUT].isConnected()) { - sample *= clamp(fabsf(inputs[SAMPLE_PWM_INPUT].getVoltage()) / 5.0f, 0.0f, 1.0f); + sample *= clamp(fabsf(inputs[SAMPLE_PWM_INPUT].getPolyVoltage(c)) / 5.0f, 0.0f, 1.0f); } - float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f; - _sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); - _square.setPulseWidth(SquareOscillator::defaultPulseWidth); + float maxSampleSteps = (_engines[c]->phasor._sampleRate / _engines[c]->phasor._frequency) / 4.0f; + _engines[c]->sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); + _engines[c]->square.setPulseWidth(SquareOscillator::defaultPulseWidth); } - _offset = params[OFFSET_PARAM].getValue(); + _engines[c]->offset = params[OFFSET_PARAM].getValue(); if (inputs[OFFSET_INPUT].isConnected()) { - _offset *= clamp(inputs[OFFSET_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f); + _engines[c]->offset *= clamp(inputs[OFFSET_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } - _offset *= 5.0f; - _scale = params[SCALE_PARAM].getValue(); + _engines[c]->offset *= 5.0f; + _engines[c]->scale = params[SCALE_PARAM].getValue(); if (inputs[SCALE_INPUT].isConnected()) { - _scale *= clamp(inputs[SCALE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); + _engines[c]->scale *= clamp(inputs[SCALE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } - _phase7Offset = phaseOffset(params[PHASE7_PARAM], inputs[PHASE7_INPUT], basePhase7Offset); - _phase6Offset = phaseOffset(params[PHASE6_PARAM], inputs[PHASE6_INPUT], basePhase6Offset); - _phase5Offset = phaseOffset(params[PHASE5_PARAM], inputs[PHASE5_INPUT], basePhase5Offset); - _phase4Offset = phaseOffset(params[PHASE4_PARAM], inputs[PHASE4_INPUT], basePhase4Offset); - _phase3Offset = phaseOffset(params[PHASE3_PARAM], inputs[PHASE3_INPUT], basePhase3Offset); - _phase2Offset = phaseOffset(params[PHASE2_PARAM], inputs[PHASE2_INPUT], basePhase2Offset); - _phase1Offset = phaseOffset(params[PHASE1_PARAM], inputs[PHASE1_INPUT], basePhase1Offset); - _phase0Offset = phaseOffset(params[PHASE0_PARAM], inputs[PHASE0_INPUT], basePhase0Offset); + _engines[c]->phase7Offset = phaseOffset(c, params[PHASE7_PARAM], inputs[PHASE7_INPUT], basePhase7Offset); + _engines[c]->phase6Offset = phaseOffset(c, params[PHASE6_PARAM], inputs[PHASE6_INPUT], basePhase6Offset); + _engines[c]->phase5Offset = phaseOffset(c, params[PHASE5_PARAM], inputs[PHASE5_INPUT], basePhase5Offset); + _engines[c]->phase4Offset = phaseOffset(c, params[PHASE4_PARAM], inputs[PHASE4_INPUT], basePhase4Offset); + _engines[c]->phase3Offset = phaseOffset(c, params[PHASE3_PARAM], inputs[PHASE3_INPUT], basePhase3Offset); + _engines[c]->phase2Offset = phaseOffset(c, params[PHASE2_PARAM], inputs[PHASE2_INPUT], basePhase2Offset); + _engines[c]->phase1Offset = phaseOffset(c, params[PHASE1_PARAM], inputs[PHASE1_INPUT], basePhase1Offset); + _engines[c]->phase0Offset = phaseOffset(c, params[PHASE0_PARAM], inputs[PHASE0_INPUT], basePhase0Offset); } void EightFO::always(const ProcessArgs& args) { lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f; } -void EightFO::processChannel(const ProcessArgs& args, int _c) { - if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) { - _phasor.resetPhase(); +void EightFO::processChannel(const ProcessArgs& args, int c) { + if (_engines[c]->resetTrigger.next(inputs[RESET_INPUT].getPolyVoltage(c))) { + _engines[c]->phasor.resetPhase(); } - _phasor.advancePhase(); + _engines[c]->phasor.advancePhase(); bool useSample = false; - if (_sampleSteps > 1) { - ++_sampleStep; - if (_sampleStep >= _sampleSteps) { - _sampleStep = 0; + if (_engines[c]->sampleSteps > 1) { + ++_engines[c]->sampleStep; + if (_engines[c]->sampleStep >= _engines[c]->sampleSteps) { + _engines[c]->sampleStep = 0; } else { useSample = true; } } - updateOutput(useSample, outputs[PHASE7_OUTPUT], _phase7Offset, _phase7Sample, _phase7Active); - updateOutput(useSample, outputs[PHASE6_OUTPUT], _phase6Offset, _phase6Sample, _phase6Active); - updateOutput(useSample, outputs[PHASE5_OUTPUT], _phase5Offset, _phase5Sample, _phase5Active); - updateOutput(useSample, outputs[PHASE4_OUTPUT], _phase4Offset, _phase4Sample, _phase4Active); - updateOutput(useSample, outputs[PHASE3_OUTPUT], _phase3Offset, _phase3Sample, _phase3Active); - updateOutput(useSample, outputs[PHASE2_OUTPUT], _phase2Offset, _phase2Sample, _phase2Active); - updateOutput(useSample, outputs[PHASE1_OUTPUT], _phase1Offset, _phase1Sample, _phase1Active); - updateOutput(useSample, outputs[PHASE0_OUTPUT], _phase0Offset, _phase0Sample, _phase0Active); + updateOutput(c, useSample, outputs[PHASE7_OUTPUT], _engines[c]->phase7Offset, _engines[c]->phase7Sample, _engines[c]->phase7Active); + updateOutput(c, useSample, outputs[PHASE6_OUTPUT], _engines[c]->phase6Offset, _engines[c]->phase6Sample, _engines[c]->phase6Active); + updateOutput(c, useSample, outputs[PHASE5_OUTPUT], _engines[c]->phase5Offset, _engines[c]->phase5Sample, _engines[c]->phase5Active); + updateOutput(c, useSample, outputs[PHASE4_OUTPUT], _engines[c]->phase4Offset, _engines[c]->phase4Sample, _engines[c]->phase4Active); + updateOutput(c, useSample, outputs[PHASE3_OUTPUT], _engines[c]->phase3Offset, _engines[c]->phase3Sample, _engines[c]->phase3Active); + updateOutput(c, useSample, outputs[PHASE2_OUTPUT], _engines[c]->phase2Offset, _engines[c]->phase2Sample, _engines[c]->phase2Active); + updateOutput(c, useSample, outputs[PHASE1_OUTPUT], _engines[c]->phase1Offset, _engines[c]->phase1Sample, _engines[c]->phase1Active); + updateOutput(c, useSample, outputs[PHASE0_OUTPUT], _engines[c]->phase0Offset, _engines[c]->phase0Sample, _engines[c]->phase0Active); } -Phasor::phase_delta_t EightFO::phaseOffset(Param& p, Input& i, Phasor::phase_delta_t baseOffset) { +Phasor::phase_delta_t EightFO::phaseOffset(int c, Param& p, Input& i, Phasor::phase_delta_t baseOffset) { float o = p.getValue() * Phasor::maxPhase / 2.0f; if (i.isConnected()) { - o *= clamp(i.getVoltage() / 5.0f, -1.0f, 1.0f); + o *= clamp(i.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } return baseOffset - o; } -void EightFO::updateOutput(bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active) { +void EightFO::updateOutput(int c, bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active) { if (output.isConnected()) { + output.setChannels(_channels); if (useSample && active) { - output.setVoltage(sample); + output.setVoltage(sample, c); } else { float v = 0.0f; @@ -128,27 +163,27 @@ void EightFO::updateOutput(bool useSample, Output& output, Phasor::phase_delta_t assert(false); } case SINE_WAVE: { - v = _sine.nextFromPhasor(_phasor, offset); + v = _engines[c]->sine.nextFromPhasor(_engines[c]->phasor, offset); break; } case TRIANGLE_WAVE: { - v = _triangle.nextFromPhasor(_phasor, offset); + v = _engines[c]->triangle.nextFromPhasor(_engines[c]->phasor, offset); break; } case RAMP_UP_WAVE: { - v = _ramp.nextFromPhasor(_phasor, offset); + v = _engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset); break; } case RAMP_DOWN_WAVE: { - v = -_ramp.nextFromPhasor(_phasor, offset); + v = -_engines[c]->ramp.nextFromPhasor(_engines[c]->phasor, offset); break; } case SQUARE_WAVE: { - v = _square.nextFromPhasor(_phasor, offset); + v = _engines[c]->square.nextFromPhasor(_engines[c]->phasor, offset); break; } } - output.setVoltage(sample = amplitude * _scale * v + _offset); + output.setVoltage(sample = amplitude * _engines[c]->scale * v + _engines[c]->offset, c); } active = true; } diff --git a/src/EightFO.hpp b/src/EightFO.hpp @@ -71,46 +71,53 @@ struct EightFO : LFOBase { SQUARE_WAVE }; + struct Engine { + int sampleSteps = 1; + int sampleStep = 0; + float offset = 0.0f; + float scale = 0.0f; + PositiveZeroCrossing resetTrigger; + + Phasor phasor; + SineTableOscillator sine; + TriangleOscillator triangle; + SawOscillator ramp; + SquareOscillator square; + + Phasor::phase_delta_t phase7Offset = 0.0f; + Phasor::phase_delta_t phase6Offset = 0.0f; + Phasor::phase_delta_t phase5Offset = 0.0f; + Phasor::phase_delta_t phase4Offset = 0.0f; + Phasor::phase_delta_t phase3Offset = 0.0f; + Phasor::phase_delta_t phase2Offset = 0.0f; + Phasor::phase_delta_t phase1Offset = 0.0f; + Phasor::phase_delta_t phase0Offset = 0.0f; + + float phase7Sample = 0.0f; + float phase6Sample = 0.0f; + float phase5Sample = 0.0f; + float phase4Sample = 0.0f; + float phase3Sample = 0.0f; + float phase2Sample = 0.0f; + float phase1Sample = 0.0f; + float phase0Sample = 0.0f; + + bool phase7Active = false; + bool phase6Active = false; + bool phase5Active = false; + bool phase4Active = false; + bool phase3Active = false; + bool phase2Active = false; + bool phase1Active = false; + bool phase0Active = false; + + void reset(); + void sampleRateChange(); + }; + const float amplitude = 5.0f; Wave _wave = NO_WAVE; - int _sampleSteps = 1; - int _sampleStep = 0; - float _offset = 0.0f; - float _scale = 0.0f; - PositiveZeroCrossing _resetTrigger; - - Phasor _phasor; - SineTableOscillator _sine; - TriangleOscillator _triangle; - SawOscillator _ramp; - SquareOscillator _square; - - Phasor::phase_delta_t _phase7Offset = 0.0f; - Phasor::phase_delta_t _phase6Offset = 0.0f; - Phasor::phase_delta_t _phase5Offset = 0.0f; - Phasor::phase_delta_t _phase4Offset = 0.0f; - Phasor::phase_delta_t _phase3Offset = 0.0f; - Phasor::phase_delta_t _phase2Offset = 0.0f; - Phasor::phase_delta_t _phase1Offset = 0.0f; - Phasor::phase_delta_t _phase0Offset = 0.0f; - - float _phase7Sample = 0.0f; - float _phase6Sample = 0.0f; - float _phase5Sample = 0.0f; - float _phase4Sample = 0.0f; - float _phase3Sample = 0.0f; - float _phase2Sample = 0.0f; - float _phase1Sample = 0.0f; - float _phase0Sample = 0.0f; - - bool _phase7Active = false; - bool _phase6Active = false; - bool _phase5Active = false; - bool _phase4Active = false; - bool _phase3Active = false; - bool _phase2Active = false; - bool _phase1Active = false; - bool _phase0Active = false; + Engine* _engines[maxChannels] {}; EightFO() : LFOBase(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { configParam<LFOFrequencyParamQuantity>(FREQUENCY_PARAM, -5.0f, 8.0f, 0.0, "Frequency", " Hz"); @@ -127,19 +134,20 @@ struct EightFO : LFOBase { configParam(PHASE2_PARAM, -1.0, 1.0, 0.0, "Phase 90", "º", 0.0f, 180.0f); configParam(PHASE1_PARAM, -1.0, 1.0, 0.0, "Phase 45", "º", 0.0f, 180.0f); configParam(PHASE0_PARAM, -1.0, 1.0, 0.0f, "Phase 0", "º", 0.0f, 180.0f); - - reset(); - sampleRateChange(); } void reset() override; void sampleRateChange() override; bool active() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; void modulate() override; + void modulateChannel(int c) override; void always(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override; - Phasor::phase_delta_t phaseOffset(Param& p, Input& i, Phasor::phase_delta_t baseOffset); - void updateOutput(bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active); + void processChannel(const ProcessArgs& args, int c) override; + Phasor::phase_delta_t phaseOffset(int c, Param& p, Input& i, Phasor::phase_delta_t baseOffset); + void updateOutput(int c, bool useSample, Output& output, Phasor::phase_delta_t& offset, float& sample, bool& active); }; } // namespace bogaudio diff --git a/src/LFO.cpp b/src/LFO.cpp @@ -1,14 +1,30 @@ #include "LFO.hpp" +void LFO::Engine::reset() { + resetTrigger.reset(); + sampleStep = phasor._sampleRate; +} + +void LFO::Engine::sampleRateChange() { + phasor.setSampleRate(APP->engine->getSampleRate()); + sampleStep = phasor._sampleRate; +} + void LFO::reset() { - _resetTrigger.reset(); - _sampleStep = _phasor._sampleRate; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + _engines[c]->reset(); + } + } } void LFO::sampleRateChange() { - _phasor.setSampleRate(APP->engine->getSampleRate()); - _sampleStep = _phasor._sampleRate; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + _engines[c]->sampleRateChange(); + } + } } bool LFO::active() { @@ -21,34 +37,49 @@ bool LFO::active() { ); } -void LFO::modulate() { - setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor); +int LFO::channels() { + return std::max(1, inputs[PITCH_INPUT].getChannels()); +} + +void LFO::addEngine(int c) { + _engines[c] = new Engine(); + _engines[c]->reset(); + _engines[c]->sampleRateChange(); +} + +void LFO::removeEngine(int c) { + delete _engines[c]; + _engines[c] = NULL; +} + +void LFO::modulateChannel(int c) { + setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _engines[c]->phasor, c); float pw = params[PW_PARAM].getValue(); if (inputs[PW_INPUT].isConnected()) { - pw *= clamp(inputs[PW_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f); + pw *= clamp(inputs[PW_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } - pw *= 1.0f - 2.0f * _square.minPulseWidth; + pw *= 1.0f - 2.0f * _engines[c]->square.minPulseWidth; pw *= 0.5f; pw += 0.5f; - _square.setPulseWidth(pw); + _engines[c]->square.setPulseWidth(pw); float sample = params[SAMPLE_PARAM].getValue(); if (inputs[SAMPLE_INPUT].isConnected()) { - sample *= clamp(inputs[SAMPLE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); + sample *= clamp(inputs[SAMPLE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } - float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f; - _sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); + float maxSampleSteps = (_engines[c]->phasor._sampleRate / _engines[c]->phasor._frequency) / 4.0f; + _engines[c]->sampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); - _offset = params[OFFSET_PARAM].getValue(); + _engines[c]->offset = params[OFFSET_PARAM].getValue(); if (inputs[OFFSET_INPUT].isConnected()) { - _offset *= clamp(inputs[OFFSET_INPUT].getVoltage() / 5.0f, -1.0f, 1.0f); + _engines[c]->offset *= clamp(inputs[OFFSET_INPUT].getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } - _offset *= 5.0f; + _engines[c]->offset *= 5.0f; - _scale = params[SCALE_PARAM].getValue(); + _engines[c]->scale = params[SCALE_PARAM].getValue(); if (inputs[SCALE_INPUT].isConnected()) { - _scale *= clamp(inputs[SCALE_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); + _engines[c]->scale *= clamp(inputs[SCALE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } } @@ -56,41 +87,42 @@ void LFO::always(const ProcessArgs& args) { lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f; } -void LFO::processChannel(const ProcessArgs& args, int _c) { - if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) { - _phasor.resetPhase(); +void LFO::processChannel(const ProcessArgs& args, int c) { + if (_engines[c]->resetTrigger.next(inputs[RESET_INPUT].getPolyVoltage(c))) { + _engines[c]->phasor.resetPhase(); } - _phasor.advancePhase(); + _engines[c]->phasor.advancePhase(); bool useSample = false; - if (_sampleSteps > 1) { - ++_sampleStep; - if (_sampleStep >= _sampleSteps) { - _sampleStep = 0; + if (_engines[c]->sampleSteps > 1) { + ++_engines[c]->sampleStep; + if (_engines[c]->sampleStep >= _engines[c]->sampleSteps) { + _engines[c]->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); + updateOutput(c, _engines[c]->sine, useSample, false, outputs[SINE_OUTPUT], _engines[c]->sineSample, _engines[c]->sineActive); + updateOutput(c, _engines[c]->triangle, useSample, false, outputs[TRIANGLE_OUTPUT], _engines[c]->triangleSample, _engines[c]->triangleActive); + updateOutput(c, _engines[c]->ramp, useSample, false, outputs[RAMP_UP_OUTPUT], _engines[c]->rampUpSample, _engines[c]->rampUpActive); + updateOutput(c, _engines[c]->ramp, useSample, true, outputs[RAMP_DOWN_OUTPUT], _engines[c]->rampDownSample, _engines[c]->rampDownActive); + updateOutput(c, _engines[c]->square, false, false, outputs[SQUARE_OUTPUT], _engines[c]->squareSample, _engines[c]->squareActive); } -void LFO::updateOutput(Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active) { +void LFO::updateOutput(int c, Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active) { if (output.isConnected()) { + output.setChannels(_channels); if (useSample && active) { - output.setVoltage(sample); + output.setVoltage(sample, c); } else { - sample = wave.nextFromPhasor(_phasor) * amplitude * _scale; + sample = wave.nextFromPhasor(_engines[c]->phasor) * amplitude * _engines[c]->scale; if (invert) { sample = -sample; } - sample += _offset; - output.setVoltage(sample); + sample += _engines[c]->offset; + output.setVoltage(sample, c); } active = true; } diff --git a/src/LFO.hpp b/src/LFO.hpp @@ -44,50 +44,57 @@ struct LFO : LFOBase { NUM_LIGHTS }; + struct Engine { + int sampleSteps = 1; + int sampleStep = 0; + float offset = 0.0f; + float scale = 0.0f; + PositiveZeroCrossing resetTrigger; + + Phasor phasor; + SineTableOscillator sine; + TriangleOscillator triangle; + SawOscillator ramp; + SquareOscillator square; + + float sineSample = 0.0f; + float triangleSample = 0.0f; + float rampUpSample = 0.0f; + float rampDownSample = 0.0f; + float squareSample = 0.0f; + + bool sineActive = false; + bool triangleActive = false; + bool rampUpActive = false; + bool rampDownActive = false; + bool squareActive = false; + + void reset(); + void sampleRateChange(); + }; + const float amplitude = 5.0f; - int _sampleSteps = 1; - int _sampleStep = 0; - float _offset = 0.0f; - float _scale = 0.0f; - PositiveZeroCrossing _resetTrigger; - - Phasor _phasor; - SineTableOscillator _sine; - TriangleOscillator _triangle; - SawOscillator _ramp; - SquareOscillator _square; - - float _sineSample = 0.0f; - float _triangleSample = 0.0f; - float _rampUpSample = 0.0f; - float _rampDownSample = 0.0f; - float _squareSample = 0.0f; - - bool _sineActive = false; - bool _triangleActive = false; - bool _rampUpActive = false; - bool _rampDownActive = false; - bool _squareActive = false; + Engine* _engines[maxChannels] {}; LFO() : LFOBase(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { configParam<LFOFrequencyParamQuantity>(FREQUENCY_PARAM, -5.0f, 8.0f, 0.0f, "Frequency", " Hz"); configParam(SLOW_PARAM, 0.0f, 1.0f, 0.0f, "Slow"); configParam(SAMPLE_PARAM, 0.0f, 1.0f, 0.0f, "Output sampling", "%", 0.0f, 100.0f); - configParam(PW_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width", "%", 0.0f, 100.0f*0.5f*(1.0f - 2.0f * _square.minPulseWidth), 50.0f); + configParam(PW_PARAM, -1.0f, 1.0f, 0.0f, "Pulse width", "%", 0.0f, 100.0f*0.5f*(1.0f - 2.0f * SquareOscillator::minPulseWidth), 50.0f); configParam(OFFSET_PARAM, -1.0f, 1.0f, 0.0f, "Offset", " V", 0.0f, 5.0f); configParam(SCALE_PARAM, 0.0f, 1.0f, 1.0f, "Scale", "%", 0.0f, 100.0f); - - reset(); - sampleRateChange(); } void reset() override; void sampleRateChange() override; bool active() override; - void modulate() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void modulateChannel(int c) override; void always(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override; - void updateOutput(Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active); + void processChannel(const ProcessArgs& args, int c) override; + void updateOutput(int c, Phasor& wave, bool useSample, bool invert, Output& output, float& sample, bool& active); }; } // namespace bogaudio diff --git a/src/LLFO.cpp b/src/LLFO.cpp @@ -2,20 +2,26 @@ #include "LLFO.hpp" void LLFO::reset() { - _resetTrigger.reset(); + for (int c = 0; c < maxChannels; ++c) { + _resetTrigger[c].reset(); + } } void LLFO::sampleRateChange() { - _phasor.setSampleRate(APP->engine->getSampleRate()); + for (int c = 0; c < maxChannels; ++c) { + _phasor[c].setSampleRate(APP->engine->getSampleRate()); + } } bool LLFO::active() { return outputs[OUT_OUTPUT].isConnected(); } -void LLFO::modulate() { - setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor); +int LLFO::channels() { + return std::max(1, inputs[PITCH_INPUT].getChannels()); +} +void LLFO::modulate() { _invert = false; Wave wave = (Wave)params[WAVE_PARAM].getValue(); switch (wave) { @@ -52,6 +58,10 @@ void LLFO::modulate() { _scale = params[SCALE_PARAM].getValue(); } +void LLFO::modulateChannel(int c) { + setFrequency(params[FREQUENCY_PARAM], inputs[PITCH_INPUT], _phasor[c], c); +} + void LLFO::always(const ProcessArgs& args) { lights[SLOW_LIGHT].value = _slowMode = params[SLOW_PARAM].getValue() > 0.5f; @@ -64,17 +74,18 @@ void LLFO::always(const ProcessArgs& args) { lights[PULSE_LIGHT].value = wave == PULSE_WAVE; } -void LLFO::processChannel(const ProcessArgs& args, int _c) { - if (_resetTrigger.next(inputs[RESET_INPUT].getVoltage())) { - _phasor.resetPhase(); +void LLFO::processChannel(const ProcessArgs& args, int c) { + if (_resetTrigger[c].next(inputs[RESET_INPUT].getPolyVoltage(c))) { + _phasor[c].resetPhase(); } - _phasor.advancePhase(); - float sample = _oscillator->nextFromPhasor(_phasor) * amplitude * _scale; + _phasor[c].advancePhase(); + float sample = _oscillator->nextFromPhasor(_phasor[c]) * amplitude * _scale; if (_invert) { sample = -sample; } sample += _offset; - outputs[OUT_OUTPUT].setVoltage(sample); + outputs[OUT_OUTPUT].setChannels(_channels); + outputs[OUT_OUTPUT].setVoltage(sample, c); } struct LLFOWidget : ModuleWidget { diff --git a/src/LLFO.hpp b/src/LLFO.hpp @@ -53,14 +53,14 @@ struct LLFO : LFOBase { const float amplitude = 5.0f; float _offset = 0.0f; float _scale = 0.0f; - PositiveZeroCrossing _resetTrigger; - Phasor _phasor; + PositiveZeroCrossing _resetTrigger[maxChannels]; + Phasor _phasor[maxChannels]; + SineTableOscillator _sine; TriangleOscillator _triangle; SawOscillator _ramp; SquareOscillator _square; - bool _invert; Phasor* _oscillator; @@ -82,9 +82,11 @@ struct LLFO : LFOBase { void reset() override; void sampleRateChange() override; bool active() override; + int channels() override; void modulate() override; + void modulateChannel(int c) override; void always(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override; + void processChannel(const ProcessArgs& args, int c) override; }; } // namespace bogaudio diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -217,8 +217,8 @@ struct BandLimitedSawOscillator : SaturatingSawOscillator { }; struct SquareOscillator : Phasor { - const float minPulseWidth = 0.03f; - const float maxPulseWidth = 1.0f - minPulseWidth; + static constexpr float minPulseWidth = 0.03f; + static constexpr float maxPulseWidth = 1.0f - minPulseWidth; static constexpr float defaultPulseWidth = 0.5f; float _pulseWidthInput; phase_t _pulseWidth = maxPhase * defaultPulseWidth; diff --git a/src/lfo_base.cpp b/src/lfo_base.cpp @@ -18,10 +18,10 @@ float LFOBase::getPitchOffset() { return offset; } -void LFOBase::setFrequency(Param& frequency, Input& pitch, Phasor& phasor) { +void LFOBase::setFrequency(Param& frequency, Input& pitch, Phasor& phasor, int c) { float f = frequency.getValue(); if (pitch.isConnected()) { - f += pitch.getVoltage(); + f += pitch.getVoltage(c); } f += getPitchOffset(); diff --git a/src/lfo_base.hpp b/src/lfo_base.hpp @@ -26,7 +26,7 @@ struct LFOBase : BGModule { } float getPitchOffset(); - void setFrequency(Param& frequency, Input& pitch, Phasor& phasor); + void setFrequency(Param& frequency, Input& pitch, Phasor& phasor, int c = 0); // FIXME };