BogaudioModules

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

commit 079511c3a3ab67b54aba42ab6d17153e91d4e402
parent 1bdb285aec1ae3a8fede575dbd2f1d9f81e8ad3e
Author: Matt Demanett <matt@demanett.net>
Date:   Wed, 18 Sep 2019 00:11:02 -0400

Poly: SHAPER[+], DADSRH[+], DGATE.

Diffstat:
Msrc/DADSRH.cpp | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/DADSRH.hpp | 80+++++++++++++------------------------------------------------------------------
Msrc/DADSRHPlus.cpp | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/DADSRHPlus.hpp | 80+++++++++++++------------------------------------------------------------------
Msrc/DGate.cpp | 102++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/DGate.hpp | 32++++++++++++++++++++------------
Msrc/Shaper.cpp | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Shaper.hpp | 65++++++++++++-----------------------------------------------------
Msrc/ShaperPlus.cpp | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ShaperPlus.hpp | 68+++++++++++++++-----------------------------------------------------
Msrc/dadsrh_core.cpp | 74+++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/dadsrh_core.hpp | 38+++++++++++++++++++-------------------
Msrc/shaper_core.cpp | 56++++++++++++++++++++++++++++++++------------------------
Msrc/shaper_core.hpp | 30+++++++++++++++---------------
14 files changed, 645 insertions(+), 370 deletions(-)

diff --git a/src/DADSRH.cpp b/src/DADSRH.cpp @@ -1,6 +1,112 @@ #include "DADSRH.hpp" +void DADSRH::reset() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + _core[c]->reset(); + } + } +} + +int DADSRH::channels() { + return inputs[TRIGGER_INPUT].getChannels(); +} + +void DADSRH::addEngine(int c) { + _core[c] = new DADSRHCore( + params[DELAY_PARAM], + params[ATTACK_PARAM], + params[DECAY_PARAM], + params[SUSTAIN_PARAM], + params[RELEASE_PARAM], + params[HOLD_PARAM], + params[ATTACK_SHAPE_PARAM], + params[DECAY_SHAPE_PARAM], + params[RELEASE_SHAPE_PARAM], + params[TRIGGER_PARAM], + params[MODE_PARAM], + params[LOOP_PARAM], + params[SPEED_PARAM], + params[RETRIGGER_PARAM], + + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + inputs[TRIGGER_INPUT], + + NULL, + NULL, + NULL, + NULL, + NULL, + outputs[ENV_OUTPUT], + outputs[INV_OUTPUT], + outputs[TRIGGER_OUTPUT], + + _delayLights, + _attackLights, + _decayLights, + _sustainLights, + _releaseLights, + lights[ATTACK_SHAPE1_LIGHT], + lights[ATTACK_SHAPE2_LIGHT], + lights[ATTACK_SHAPE3_LIGHT], + lights[DECAY_SHAPE1_LIGHT], + lights[DECAY_SHAPE2_LIGHT], + lights[DECAY_SHAPE3_LIGHT], + lights[RELEASE_SHAPE1_LIGHT], + lights[RELEASE_SHAPE2_LIGHT], + lights[RELEASE_SHAPE3_LIGHT], + + _triggerOnLoad, + _shouldTriggerOnLoad + ); +} + +void DADSRH::removeEngine(int c) { + delete _core[c]; + _core[c] = NULL; +} + +void DADSRH::processChannel(const ProcessArgs& args, int c) { + _core[c]->step(c, _channels); +} + +void DADSRH::postProcess(const ProcessArgs& args) { + float delaySum = 0.0f; + float attackSum = 0.0f; + float decaySum = 0.0f; + float sustainSum = 0.0f; + float releaseSum = 0.0f; + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + delaySum += _delayLights[c]; + attackSum += _attackLights[c]; + decaySum += _decayLights[c]; + sustainSum += _sustainLights[c]; + releaseSum += _releaseLights[c]; + } + } + lights[DELAY_LIGHT].value = delaySum / (float)_channels; + lights[ATTACK_LIGHT].value = attackSum / (float)_channels; + lights[DECAY_LIGHT].value = decaySum / (float)_channels; + lights[SUSTAIN_LIGHT].value = sustainSum / (float)_channels; + lights[RELEASE_LIGHT].value = releaseSum / (float)_channels; +} + +bool DADSRH::shouldTriggerOnNextLoad() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c] && _core[c]->_stage != _core[c]->STOPPED_STAGE) { + return true; + } + } + return false; +} + struct DADSRHWidget : TriggerOnLoadModuleWidget { static constexpr int hp = 10; diff --git a/src/DADSRH.hpp b/src/DADSRH.hpp @@ -57,7 +57,12 @@ struct DADSRH : TriggerOnLoadModule { NUM_LIGHTS }; - DADSRHCore* _core; + DADSRHCore* _core[maxChannels] {}; + float _delayLights[maxChannels] {}; + float _attackLights[maxChannels] {}; + float _decayLights[maxChannels] {}; + float _sustainLights[maxChannels] {}; + float _releaseLights[maxChannels] {}; DADSRH() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -75,75 +80,16 @@ struct DADSRH : TriggerOnLoadModule { configParam(LOOP_PARAM, 0.0f, 1.0f, 1.0f, "Loop"); configParam(SPEED_PARAM, 0.0f, 1.0f, 1.0f, "Speed"); configParam(RETRIGGER_PARAM, 0.0f, 1.0f, 1.0f, "Retrigger"); - - _core = new DADSRHCore( - params[DELAY_PARAM], - params[ATTACK_PARAM], - params[DECAY_PARAM], - params[SUSTAIN_PARAM], - params[RELEASE_PARAM], - params[HOLD_PARAM], - params[ATTACK_SHAPE_PARAM], - params[DECAY_SHAPE_PARAM], - params[RELEASE_SHAPE_PARAM], - params[TRIGGER_PARAM], - params[MODE_PARAM], - params[LOOP_PARAM], - params[SPEED_PARAM], - params[RETRIGGER_PARAM], - - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - inputs[TRIGGER_INPUT], - - NULL, - NULL, - NULL, - NULL, - NULL, - outputs[ENV_OUTPUT], - outputs[INV_OUTPUT], - outputs[TRIGGER_OUTPUT], - - lights[DELAY_LIGHT], - lights[ATTACK_LIGHT], - lights[DECAY_LIGHT], - lights[SUSTAIN_LIGHT], - lights[RELEASE_LIGHT], - lights[ATTACK_SHAPE1_LIGHT], - lights[ATTACK_SHAPE2_LIGHT], - lights[ATTACK_SHAPE3_LIGHT], - lights[DECAY_SHAPE1_LIGHT], - lights[DECAY_SHAPE2_LIGHT], - lights[DECAY_SHAPE3_LIGHT], - lights[RELEASE_SHAPE1_LIGHT], - lights[RELEASE_SHAPE2_LIGHT], - lights[RELEASE_SHAPE3_LIGHT], - - _triggerOnLoad, - _shouldTriggerOnLoad - ); - reset(); - } - virtual ~DADSRH() { - delete _core; } - void reset() override { - _core->reset(); - } + void reset() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override { - _core->step(); - } - - bool shouldTriggerOnNextLoad() override { - return _core->_stage != _core->STOPPED_STAGE; - } + bool shouldTriggerOnNextLoad() override; }; } // namespace bogaudio diff --git a/src/DADSRHPlus.cpp b/src/DADSRHPlus.cpp @@ -1,6 +1,112 @@ #include "DADSRHPlus.hpp" +void DADSRHPlus::reset() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + _core[c]->reset(); + } + } +} + +int DADSRHPlus::channels() { + return inputs[TRIGGER_INPUT].getChannels(); +} + +void DADSRHPlus::addEngine(int c) { + _core[c] = new DADSRHCore( + params[DELAY_PARAM], + params[ATTACK_PARAM], + params[DECAY_PARAM], + params[SUSTAIN_PARAM], + params[RELEASE_PARAM], + params[HOLD_PARAM], + params[ATTACK_SHAPE_PARAM], + params[DECAY_SHAPE_PARAM], + params[RELEASE_SHAPE_PARAM], + params[TRIGGER_PARAM], + params[MODE_PARAM], + params[LOOP_PARAM], + params[SPEED_PARAM], + params[RETRIGGER_PARAM], + + &inputs[DELAY_INPUT], + &inputs[ATTACK_INPUT], + &inputs[DECAY_INPUT], + &inputs[SUSTAIN_INPUT], + &inputs[RELEASE_INPUT], + &inputs[HOLD_INPUT], + inputs[TRIGGER_INPUT], + + &outputs[DELAY_OUTPUT], + &outputs[ATTACK_OUTPUT], + &outputs[DECAY_OUTPUT], + &outputs[SUSTAIN_OUTPUT], + &outputs[RELEASE_OUTPUT], + outputs[ENV_OUTPUT], + outputs[INV_OUTPUT], + outputs[TRIGGER_OUTPUT], + + _delayLights, + _attackLights, + _decayLights, + _sustainLights, + _releaseLights, + lights[ATTACK_SHAPE1_LIGHT], + lights[ATTACK_SHAPE2_LIGHT], + lights[ATTACK_SHAPE3_LIGHT], + lights[DECAY_SHAPE1_LIGHT], + lights[DECAY_SHAPE2_LIGHT], + lights[DECAY_SHAPE3_LIGHT], + lights[RELEASE_SHAPE1_LIGHT], + lights[RELEASE_SHAPE2_LIGHT], + lights[RELEASE_SHAPE3_LIGHT], + + _triggerOnLoad, + _shouldTriggerOnLoad + ); +} + +void DADSRHPlus::removeEngine(int c) { + delete _core[c]; + _core[c] = NULL; +} + +void DADSRHPlus::processChannel(const ProcessArgs& args, int c) { + _core[c]->step(c, _channels); +} + +void DADSRHPlus::postProcess(const ProcessArgs& args) { + float delaySum = 0.0f; + float attackSum = 0.0f; + float decaySum = 0.0f; + float sustainSum = 0.0f; + float releaseSum = 0.0f; + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + delaySum += _delayLights[c]; + attackSum += _attackLights[c]; + decaySum += _decayLights[c]; + sustainSum += _sustainLights[c]; + releaseSum += _releaseLights[c]; + } + } + lights[DELAY_LIGHT].value = delaySum / (float)_channels; + lights[ATTACK_LIGHT].value = attackSum / (float)_channels; + lights[DECAY_LIGHT].value = decaySum / (float)_channels; + lights[SUSTAIN_LIGHT].value = sustainSum / (float)_channels; + lights[RELEASE_LIGHT].value = releaseSum / (float)_channels; +} + +bool DADSRHPlus::shouldTriggerOnNextLoad() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c] && _core[c]->_stage != _core[c]->STOPPED_STAGE) { + return true; + } + } + return false; +} + struct DADSRHPlusWidget : TriggerOnLoadModuleWidget { static constexpr int hp = 15; diff --git a/src/DADSRHPlus.hpp b/src/DADSRHPlus.hpp @@ -68,7 +68,12 @@ struct DADSRHPlus : TriggerOnLoadModule { NUM_LIGHTS }; - DADSRHCore* _core; + DADSRHCore* _core[maxChannels] {}; + float _delayLights[maxChannels] {}; + float _attackLights[maxChannels] {}; + float _decayLights[maxChannels] {}; + float _sustainLights[maxChannels] {}; + float _releaseLights[maxChannels] {}; DADSRHPlus() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -86,75 +91,16 @@ struct DADSRHPlus : TriggerOnLoadModule { configParam(LOOP_PARAM, 0.0f, 1.0f, 1.0f, "Loop"); configParam(SPEED_PARAM, 0.0f, 1.0f, 1.0f, "Speed"); configParam(RETRIGGER_PARAM, 0.0f, 1.0f, 1.0f, "Retrigger"); - - _core = new DADSRHCore( - params[DELAY_PARAM], - params[ATTACK_PARAM], - params[DECAY_PARAM], - params[SUSTAIN_PARAM], - params[RELEASE_PARAM], - params[HOLD_PARAM], - params[ATTACK_SHAPE_PARAM], - params[DECAY_SHAPE_PARAM], - params[RELEASE_SHAPE_PARAM], - params[TRIGGER_PARAM], - params[MODE_PARAM], - params[LOOP_PARAM], - params[SPEED_PARAM], - params[RETRIGGER_PARAM], - - &inputs[DELAY_INPUT], - &inputs[ATTACK_INPUT], - &inputs[DECAY_INPUT], - &inputs[SUSTAIN_INPUT], - &inputs[RELEASE_INPUT], - &inputs[HOLD_INPUT], - inputs[TRIGGER_INPUT], - - &outputs[DELAY_OUTPUT], - &outputs[ATTACK_OUTPUT], - &outputs[DECAY_OUTPUT], - &outputs[SUSTAIN_OUTPUT], - &outputs[RELEASE_OUTPUT], - outputs[ENV_OUTPUT], - outputs[INV_OUTPUT], - outputs[TRIGGER_OUTPUT], - - lights[DELAY_LIGHT], - lights[ATTACK_LIGHT], - lights[DECAY_LIGHT], - lights[SUSTAIN_LIGHT], - lights[RELEASE_LIGHT], - lights[ATTACK_SHAPE1_LIGHT], - lights[ATTACK_SHAPE2_LIGHT], - lights[ATTACK_SHAPE3_LIGHT], - lights[DECAY_SHAPE1_LIGHT], - lights[DECAY_SHAPE2_LIGHT], - lights[DECAY_SHAPE3_LIGHT], - lights[RELEASE_SHAPE1_LIGHT], - lights[RELEASE_SHAPE2_LIGHT], - lights[RELEASE_SHAPE3_LIGHT], - - _triggerOnLoad, - _shouldTriggerOnLoad - ); - reset(); - } - virtual ~DADSRHPlus() { - delete _core; } - void reset() override { - _core->reset(); - } + void reset() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override { - _core->step(); - } - - bool shouldTriggerOnNextLoad() override { - return _core->_stage != _core->STOPPED_STAGE; - } + bool shouldTriggerOnNextLoad() override; }; } // namespace bogaudio diff --git a/src/DGate.cpp b/src/DGate.cpp @@ -1,44 +1,68 @@ #include "DGate.hpp" +void DGate::Engine::reset() { + trigger.reset(); + triggerOuptutPulseGen.process(10.0); + stage = STOPPED_STAGE; + stageProgress = 0.0; + delayLight = 0.0; + gateLight = 0.0; +} + void DGate::reset() { - _trigger.reset(); - _triggerOuptutPulseGen.process(10.0); - _stage = STOPPED_STAGE; - _stageProgress = 0.0; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + _engines[c]->reset(); + } + } +} + +int DGate::channels() { + return inputs[TRIGGER_INPUT].getChannels(); +} + +void DGate::addEngine(int c) { + _engines[c] = new Engine(); + _engines[c]->reset(); +} + +void DGate::removeEngine(int c) { + delete _engines[c]; + _engines[c] = NULL; } -void DGate::processChannel(const ProcessArgs& args, int _c) { +void DGate::processChannel(const ProcessArgs& args, int c) { float envelope = 0.0; bool complete = false; if ( - _trigger.process(params[TRIGGER_PARAM].getValue() + inputs[TRIGGER_INPUT].getVoltage()) || - (_firstStep && _triggerOnLoad && _shouldTriggerOnLoad && params[LOOP_PARAM].getValue() <= 0.0) + _engines[c]->trigger.process(params[TRIGGER_PARAM].getValue() + inputs[TRIGGER_INPUT].getPolyVoltage(c)) || + (_engines[c]->firstStep && _triggerOnLoad && _shouldTriggerOnLoad && params[LOOP_PARAM].getValue() <= 0.0) ) { - _stage = DELAY_STAGE; - _stageProgress = 0.0; + _engines[c]->stage = DELAY_STAGE; + _engines[c]->stageProgress = 0.0; } else { - switch (_stage) { + switch (_engines[c]->stage) { case STOPPED_STAGE: { break; } case DELAY_STAGE: { - if (stepStage(params[DELAY_PARAM])) { - _stage = GATE_STAGE; - _stageProgress = 0.0; + if (stepStage(c, params[DELAY_PARAM])) { + _engines[c]->stage = GATE_STAGE; + _engines[c]->stageProgress = 0.0; } break; } case GATE_STAGE: { - if (stepStage(params[GATE_PARAM])) { + if (stepStage(c, params[GATE_PARAM])) { complete = true; - if (params[LOOP_PARAM].getValue() <= 0.0 || _trigger.isHigh()) { - _stage = DELAY_STAGE; - _stageProgress = 0.0; + if (params[LOOP_PARAM].getValue() <= 0.0 || _engines[c]->trigger.isHigh()) { + _engines[c]->stage = DELAY_STAGE; + _engines[c]->stageProgress = 0.0; } else { - _stage = STOPPED_STAGE; + _engines[c]->stage = STOPPED_STAGE; } } else { @@ -49,26 +73,50 @@ void DGate::processChannel(const ProcessArgs& args, int _c) { } } - outputs[GATE_OUTPUT].setVoltage(envelope * 10.0); + outputs[GATE_OUTPUT].setChannels(_channels); + outputs[GATE_OUTPUT].setVoltage(envelope * 10.0, c); if (complete) { - _triggerOuptutPulseGen.trigger(0.001); + _engines[c]->triggerOuptutPulseGen.trigger(0.001); } - outputs[END_OUTPUT].setVoltage(_triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0); + outputs[END_OUTPUT].setChannels(_channels); + outputs[END_OUTPUT].setVoltage(_engines[c]->triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0, c); + + _engines[c]->delayLight = _engines[c]->stage == DELAY_STAGE; + _engines[c]->gateLight = _engines[c]->stage == GATE_STAGE; - lights[DELAY_LIGHT].value = _stage == DELAY_STAGE; - lights[GATE_LIGHT].value = _stage == GATE_STAGE; + _engines[c]->firstStep = false; +} - _firstStep = false; +void DGate::postProcess(const ProcessArgs& args) { + float delaySum = 0.0f; + float gateSum = 0.0f; + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c]) { + delaySum += _engines[c]->delayLight; + gateSum += _engines[c]->gateLight; + } + } + lights[DELAY_LIGHT].value = delaySum / (float)_channels; + lights[GATE_LIGHT].value = gateSum / (float)_channels; } -bool DGate::stepStage(Param& knob) { +bool DGate::stepStage(int c, Param& knob) { float t = knob.getValue(); t = pow(t, 2); t *= 10.0; - _stageProgress += APP->engine->getSampleTime(); - return _stageProgress > t; + _engines[c]->stageProgress += APP->engine->getSampleTime(); + return _engines[c]->stageProgress > t; } +bool DGate::shouldTriggerOnNextLoad() { + for (int c = 0; c < maxChannels; ++c) { + if (_engines[c] && _engines[c]->stage != STOPPED_STAGE) { + return true; + } + } + return false; +}; + struct DGateWidget : TriggerOnLoadModuleWidget { static constexpr int hp = 3; diff --git a/src/DGate.hpp b/src/DGate.hpp @@ -39,11 +39,19 @@ struct DGate : TriggerOnLoadModule { GATE_STAGE }; - bool _firstStep = true; - Trigger _trigger; - rack::dsp::PulseGenerator _triggerOuptutPulseGen; - Stage _stage; - float _stageProgress; + struct Engine { + bool firstStep = true; + Trigger trigger; + rack::dsp::PulseGenerator triggerOuptutPulseGen; + Stage stage; + float stageProgress; + float delayLight; + float gateLight; + + void reset(); + }; + + Engine *_engines[maxChannels] {}; DGate() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -51,16 +59,16 @@ struct DGate : TriggerOnLoadModule { configParam<EnvelopeSegmentParamQuantity>(GATE_PARAM, 0.0f, 1.0f, 0.31623f, "Gate", " s"); configParam(LOOP_PARAM, 0.0f, 1.0f, 1.0f, "Loop"); configParam(TRIGGER_PARAM, 0.0f, 1.0f, 0.0f, "Trigger"); - - reset(); } void reset() override; - void processChannel(const ProcessArgs& args, int _c) override; - bool stepStage(Param& knob); - bool shouldTriggerOnNextLoad() override { - return _stage != STOPPED_STAGE; - }; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; + bool stepStage(int c, Param& knob); + bool shouldTriggerOnNextLoad() override; }; } // namespace bogaudio diff --git a/src/Shaper.cpp b/src/Shaper.cpp @@ -1,6 +1,95 @@ #include "Shaper.hpp" +void Shaper::reset() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + _core[c]->reset(); + } + } +} + +int Shaper::channels() { + return std::max(inputs[SIGNAL_INPUT].getChannels(), inputs[TRIGGER_INPUT].getChannels()); +} + +void Shaper::addEngine(int c) { + _core[c] = new ShaperCore( + params[ATTACK_PARAM], + params[ON_PARAM], + params[DECAY_PARAM], + params[OFF_PARAM], + params[ENV_PARAM], + params[SIGNAL_PARAM], + params[TRIGGER_PARAM], + params[SPEED_PARAM], + params[LOOP_PARAM], + + inputs[SIGNAL_INPUT], + inputs[TRIGGER_INPUT], + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + outputs[SIGNAL_OUTPUT], + outputs[ENV_OUTPUT], + outputs[INV_OUTPUT], + outputs[TRIGGER_OUTPUT], + NULL, + NULL, + NULL, + NULL, + + _attackLights, + _onLights, + _decayLights, + _offLights, + + _triggerOnLoad, + _shouldTriggerOnLoad + ); +} + +void Shaper::removeEngine(int c) { + delete _core[c]; + _core[c] = NULL; +} + +void Shaper::processChannel(const ProcessArgs& args, int c) { + _core[c]->step(c, _channels); +} + +void Shaper::postProcess(const ProcessArgs& args) { + float attackSum = 0.0f; + float onSum = 0.0f; + float decaySum = 0.0f; + float offSum = 0.0f; + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + attackSum += _attackLights[c]; + onSum += _onLights[c]; + decaySum += _decayLights[c]; + offSum += _offLights[c]; + } + } + lights[ATTACK_LIGHT].value = attackSum / (float)_channels; + lights[ON_LIGHT].value = onSum / (float)_channels; + lights[DECAY_LIGHT].value = decaySum / (float)_channels; + lights[OFF_LIGHT].value = offSum / (float)_channels; +} + +bool Shaper::shouldTriggerOnNextLoad() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c] && _core[c]->_stage != _core[c]->STOPPED_STAGE) { + return true; + } + } + return false; +} + struct ShaperWidget : TriggerOnLoadModuleWidget { static constexpr int hp = 10; diff --git a/src/Shaper.hpp b/src/Shaper.hpp @@ -44,7 +44,11 @@ struct Shaper : TriggerOnLoadModule { NUM_LIGHTS }; - ShaperCore* _core; + ShaperCore* _core[maxChannels] {}; + float _attackLights[maxChannels] {}; + float _onLights[maxChannels] {}; + float _decayLights[maxChannels] {}; + float _offLights[maxChannels] {}; Shaper() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -57,61 +61,16 @@ struct Shaper : TriggerOnLoadModule { configParam(TRIGGER_PARAM, 0.0f, 1.0f, 0.0f, "Trigger"); configParam(SPEED_PARAM, 0.0f, 1.0f, 1.0f, "Speed"); configParam(LOOP_PARAM, 0.0f, 1.0f, 1.0f, "Loop"); - - _core = new ShaperCore( - params[ATTACK_PARAM], - params[ON_PARAM], - params[DECAY_PARAM], - params[OFF_PARAM], - params[ENV_PARAM], - params[SIGNAL_PARAM], - params[TRIGGER_PARAM], - params[SPEED_PARAM], - params[LOOP_PARAM], - - inputs[SIGNAL_INPUT], - inputs[TRIGGER_INPUT], - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - outputs[SIGNAL_OUTPUT], - outputs[ENV_OUTPUT], - outputs[INV_OUTPUT], - outputs[TRIGGER_OUTPUT], - NULL, - NULL, - NULL, - NULL, - - lights[ATTACK_LIGHT], - lights[ON_LIGHT], - lights[DECAY_LIGHT], - lights[OFF_LIGHT], - - _triggerOnLoad, - _shouldTriggerOnLoad - ); - reset(); - } - virtual ~Shaper() { - delete _core; } - void reset() override { - _core->reset(); - } + void reset() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; - void processChannel(const ProcessArgs& args, int _c) override { - _core->step(); - } - - bool shouldTriggerOnNextLoad() override { - return _core->_stage != _core->STOPPED_STAGE; - } + bool shouldTriggerOnNextLoad() override; }; } // namespace bogaudio diff --git a/src/ShaperPlus.cpp b/src/ShaperPlus.cpp @@ -1,6 +1,95 @@ #include "ShaperPlus.hpp" +void ShaperPlus::reset() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + _core[c]->reset(); + } + } +} + +int ShaperPlus::channels() { + return std::max(inputs[SIGNAL_INPUT].getChannels(), inputs[TRIGGER_INPUT].getChannels()); +} + +void ShaperPlus::addEngine(int c) { + _core[c] = new ShaperCore( + params[ATTACK_PARAM], + params[ON_PARAM], + params[DECAY_PARAM], + params[OFF_PARAM], + params[ENV_PARAM], + params[SIGNAL_PARAM], + params[TRIGGER_PARAM], + params[SPEED_PARAM], + params[LOOP_PARAM], + + inputs[SIGNAL_INPUT], + inputs[TRIGGER_INPUT], + &inputs[ATTACK_INPUT], + &inputs[ON_INPUT], + &inputs[DECAY_INPUT], + &inputs[OFF_INPUT], + &inputs[ENV_INPUT], + &inputs[SIGNALCV_INPUT], + + outputs[SIGNAL_OUTPUT], + outputs[ENV_OUTPUT], + outputs[INV_OUTPUT], + outputs[TRIGGER_OUTPUT], + &outputs[ATTACK_OUTPUT], + &outputs[ON_OUTPUT], + &outputs[DECAY_OUTPUT], + &outputs[OFF_OUTPUT], + + _attackLights, + _onLights, + _decayLights, + _offLights, + + _triggerOnLoad, + _shouldTriggerOnLoad + ); +} + +void ShaperPlus::removeEngine(int c) { + delete _core[c]; + _core[c] = NULL; +} + +void ShaperPlus::processChannel(const ProcessArgs& args, int c) { + _core[c]->step(c, _channels); +} + +void ShaperPlus::postProcess(const ProcessArgs& args) { + float attackSum = 0.0f; + float onSum = 0.0f; + float decaySum = 0.0f; + float offSum = 0.0f; + for (int c = 0; c < maxChannels; ++c) { + if (_core[c]) { + attackSum += _attackLights[c]; + onSum += _onLights[c]; + decaySum += _decayLights[c]; + offSum += _offLights[c]; + } + } + lights[ATTACK_LIGHT].value = attackSum / (float)_channels; + lights[ON_LIGHT].value = onSum / (float)_channels; + lights[DECAY_LIGHT].value = decaySum / (float)_channels; + lights[OFF_LIGHT].value = offSum / (float)_channels; +} + +bool ShaperPlus::shouldTriggerOnNextLoad() { + for (int c = 0; c < maxChannels; ++c) { + if (_core[c] && _core[c]->_stage != _core[c]->STOPPED_STAGE) { + return true; + } + } + return false; +} + struct ShaperPlusWidget : TriggerOnLoadModuleWidget { static constexpr int hp = 15; diff --git a/src/ShaperPlus.hpp b/src/ShaperPlus.hpp @@ -21,6 +21,7 @@ struct ShaperPlus : TriggerOnLoadModule { LOOP_PARAM, NUM_PARAMS }; + enum InputIds { SIGNAL_INPUT, TRIGGER_INPUT, @@ -32,6 +33,7 @@ struct ShaperPlus : TriggerOnLoadModule { SIGNALCV_INPUT, NUM_INPUTS }; + enum OutputIds { SIGNAL_OUTPUT, ENV_OUTPUT, @@ -43,6 +45,7 @@ struct ShaperPlus : TriggerOnLoadModule { OFF_OUTPUT, NUM_OUTPUTS }; + enum LightIds { ATTACK_LIGHT, ON_LIGHT, @@ -51,7 +54,11 @@ struct ShaperPlus : TriggerOnLoadModule { NUM_LIGHTS }; - ShaperCore* _core; + ShaperCore* _core[maxChannels] {}; + float _attackLights[maxChannels] {}; + float _onLights[maxChannels] {}; + float _decayLights[maxChannels] {}; + float _offLights[maxChannels] {}; ShaperPlus() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); @@ -64,61 +71,16 @@ struct ShaperPlus : TriggerOnLoadModule { configParam(TRIGGER_PARAM, 0.0f, 1.0f, 0.0f, "Trigger"); configParam(SPEED_PARAM, 0.0f, 1.0f, 1.0f, "Speed"); configParam(LOOP_PARAM, 0.0f, 1.0f, 1.0f, "Loop"); - - _core = new ShaperCore( - params[ATTACK_PARAM], - params[ON_PARAM], - params[DECAY_PARAM], - params[OFF_PARAM], - params[ENV_PARAM], - params[SIGNAL_PARAM], - params[TRIGGER_PARAM], - params[SPEED_PARAM], - params[LOOP_PARAM], - - inputs[SIGNAL_INPUT], - inputs[TRIGGER_INPUT], - &inputs[ATTACK_INPUT], - &inputs[ON_INPUT], - &inputs[DECAY_INPUT], - &inputs[OFF_INPUT], - &inputs[ENV_INPUT], - &inputs[SIGNALCV_INPUT], - - outputs[SIGNAL_OUTPUT], - outputs[ENV_OUTPUT], - outputs[INV_OUTPUT], - outputs[TRIGGER_OUTPUT], - &outputs[ATTACK_OUTPUT], - &outputs[ON_OUTPUT], - &outputs[DECAY_OUTPUT], - &outputs[OFF_OUTPUT], - - lights[ATTACK_LIGHT], - lights[ON_LIGHT], - lights[DECAY_LIGHT], - lights[OFF_LIGHT], - - _triggerOnLoad, - _shouldTriggerOnLoad - ); - reset(); - } - virtual ~ShaperPlus() { - delete _core; - } - - void reset() override { - _core->reset(); } - void processChannel(const ProcessArgs& args, int _c) override { - _core->step(); - } + void reset() override; + int channels() override; + void addEngine(int c) override; + void removeEngine(int c) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; - bool shouldTriggerOnNextLoad() override { - return _core->_stage != _core->STOPPED_STAGE; - } + bool shouldTriggerOnNextLoad() override; }; } // namespace bogaudio diff --git a/src/dadsrh_core.cpp b/src/dadsrh_core.cpp @@ -8,7 +8,7 @@ void DADSRHCore::reset() { _releaseLevel = _holdProgress = _stageProgress = _envelope = 0.0; } -void DADSRHCore::step() { +void DADSRHCore::step(int c, int channels) { const int SHAPE1 = 1; const int SHAPE2 = 2; const int SHAPE3 = 3; @@ -17,7 +17,7 @@ void DADSRHCore::step() { bool slow = _speedParam.getValue() <= 0.5; if ( - _trigger.process(_triggerParam.getValue() + _triggerInput.getVoltage()) || + _trigger.process(_triggerParam.getValue() + _triggerInput.getPolyVoltage(c)) || (_firstStep && _triggerOnLoad && _shouldTriggerOnLoad && _loopParam.getValue() < 0.5 && _modeParam.getValue() < 0.5) ) { if (_stage == STOPPED_STAGE || _retriggerParam.getValue() <= 0.5) { @@ -36,8 +36,8 @@ void DADSRHCore::step() { _stageProgress = _envelope = 0.0; // we're skipping the delay; subtract the full delay time from hold time so that env has the same shape as if we'd waited out the delay. - float delayTime = knobTime(_delayParam, _delayInput, slow, true); - float holdTime = knobTime(_holdParam, _holdInput, slow); + float delayTime = knobTime(c, _delayParam, _delayInput, slow, true); + float holdTime = knobTime(c, _holdParam, _holdInput, slow); _holdProgress = fminf(1.0, delayTime / holdTime); break; } @@ -62,9 +62,9 @@ void DADSRHCore::step() { } // reset hold to what it would have been at this point in the attack the first time through. - float delayTime = knobTime(_delayParam, _delayInput, slow, true); - float attackTime = knobTime(_attackParam, _attackInput, slow); - float holdTime = knobTime(_holdParam, _holdInput, slow); + float delayTime = knobTime(c, _delayParam, _delayInput, slow, true); + float attackTime = knobTime(c, _attackParam, _attackInput, slow); + float holdTime = knobTime(c, _holdParam, _holdInput, slow); _holdProgress = fminf(1.0, (delayTime + _stageProgress * attackTime) / holdTime); break; } @@ -86,7 +86,7 @@ void DADSRHCore::step() { bool holdComplete = _holdProgress >= 1.0; if (!holdComplete) { // run the hold accumulation even if we're not in hold mode, in case we switch mid-cycle. - _holdProgress += stepAmount(_holdParam, _holdInput, slow); + _holdProgress += stepAmount(c, _holdParam, _holdInput, slow); holdComplete = _holdProgress >= 1.0; } @@ -107,7 +107,7 @@ void DADSRHCore::step() { } case DELAY_STAGE: { - _stageProgress += stepAmount(_delayParam, _delayInput, slow, true); + _stageProgress += stepAmount(c, _delayParam, _delayInput, slow, true); if (_stageProgress >= 1.0) { _stage = ATTACK_STAGE; _stageProgress = 0.0; @@ -116,7 +116,7 @@ void DADSRHCore::step() { } case ATTACK_STAGE: { - _stageProgress += stepAmount(_attackParam, _attackInput, slow); + _stageProgress += stepAmount(c, _attackParam, _attackInput, slow); switch ((int)_attackShapeParam.getValue()) { case SHAPE2: { _envelope = _stageProgress; @@ -139,8 +139,8 @@ void DADSRHCore::step() { } case DECAY_STAGE: { - float sustainLevel = knobAmount(_sustainParam, _sustainInput); - _stageProgress += stepAmount(_decayParam, _decayInput, slow); + float sustainLevel = knobAmount(c, _sustainParam, _sustainInput); + _stageProgress += stepAmount(c, _decayParam, _decayInput, slow); switch ((int)_decayShapeParam.getValue()) { case SHAPE2: { _envelope = 1.0 - _stageProgress; @@ -164,12 +164,12 @@ void DADSRHCore::step() { } case SUSTAIN_STAGE: { - _envelope = knobAmount(_sustainParam, _sustainInput); + _envelope = knobAmount(c, _sustainParam, _sustainInput); break; } case RELEASE_STAGE: { - _stageProgress += stepAmount(_releaseParam, _releaseInput, slow); + _stageProgress += stepAmount(c, _releaseParam, _releaseInput, slow); switch ((int)_releaseShapeParam.getValue()) { case SHAPE2: { _envelope = 1.0 - _stageProgress; @@ -201,35 +201,43 @@ void DADSRHCore::step() { } float env = _envelope * 10.0; - _envOutput.setVoltage(env); - _invOutput.setVoltage(10.0 - env); + _envOutput.setChannels(channels); + _envOutput.setVoltage(env, c); + _invOutput.setChannels(channels); + _invOutput.setVoltage(10.0 - env, c); if (complete) { _triggerOuptutPulseGen.trigger(0.001); } - _triggerOutput.setVoltage(_triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0); + _triggerOutput.setChannels(channels); + _triggerOutput.setVoltage(_triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0, c); if (_delayOutput) { - _delayOutput->value = _stage == DELAY_STAGE ? 5.0 : 0.0; + _delayOutput->setChannels(channels); + _delayOutput->setVoltage(_stage == DELAY_STAGE ? 5.0 : 0.0, c); } if (_attackOutput) { - _attackOutput->value = _stage == ATTACK_STAGE ? 5.0 : 0.0; + _attackOutput->setChannels(channels); + _attackOutput->setVoltage(_stage == ATTACK_STAGE ? 5.0 : 0.0, c); } if (_decayOutput) { - _decayOutput->value = _stage == DECAY_STAGE ? 5.0 : 0.0; + _decayOutput->setChannels(channels); + _decayOutput->setVoltage(_stage == DECAY_STAGE ? 5.0 : 0.0, c); } if (_sustainOutput) { - _sustainOutput->value = _stage == SUSTAIN_STAGE ? 5.0 : 0.0; + _sustainOutput->setChannels(channels); + _sustainOutput->setVoltage(_stage == SUSTAIN_STAGE ? 5.0 : 0.0, c); } if (_releaseOutput) { - _releaseOutput->value = _stage == RELEASE_STAGE ? 5.0 : 0.0; + _releaseOutput->setChannels(channels); + _releaseOutput->setVoltage(_stage == RELEASE_STAGE ? 5.0 : 0.0, c); } - _delayLight.value = _stage == DELAY_STAGE; - _attackLight.value = _stage == ATTACK_STAGE; - _decayLight.value = _stage == DECAY_STAGE; - _sustainLight.value = _stage == SUSTAIN_STAGE; - _releaseLight.value = _stage == RELEASE_STAGE; + _delayLights[c] = _stage == DELAY_STAGE; + _attackLights[c] = _stage == ATTACK_STAGE; + _decayLights[c] = _stage == DECAY_STAGE; + _sustainLights[c] = _stage == SUSTAIN_STAGE; + _releaseLights[c] = _stage == RELEASE_STAGE; _attackShape1Light.value = (int)_attackShapeParam.value == SHAPE1; _attackShape2Light.value = (int)_attackShapeParam.value == SHAPE2; @@ -244,21 +252,21 @@ void DADSRHCore::step() { _firstStep = false; } -float DADSRHCore::stepAmount(Param& knob, Input* cv, bool slow, bool allowZero) { - return APP->engine->getSampleTime() / knobTime(knob, cv, slow, allowZero); +float DADSRHCore::stepAmount(int c, Param& knob, Input* cv, bool slow, bool allowZero) { + return APP->engine->getSampleTime() / knobTime(c, knob, cv, slow, allowZero); } -float DADSRHCore::knobTime(Param& knob, Input* cv, bool slow, bool allowZero) { - float t = knobAmount(knob, cv); +float DADSRHCore::knobTime(int c, Param& knob, Input* cv, bool slow, bool allowZero) { + float t = knobAmount(c, knob, cv); t = pow(t, 2.0); t = fmaxf(t, allowZero ? 0.0 : 0.001); return t * (slow ? 100.0 : 10.0); } -float DADSRHCore::knobAmount(Param& knob, Input* cv) const { +float DADSRHCore::knobAmount(int c, Param& knob, Input* cv) const { float v = clamp(knob.getValue(), 0.0f, 1.0f); if (cv && cv->isConnected()) { - v *= clamp(cv->getVoltage() / 10.0f, 0.0f, 1.0f); + v *= clamp(cv->getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } return v; } diff --git a/src/dadsrh_core.hpp b/src/dadsrh_core.hpp @@ -46,11 +46,11 @@ struct DADSRHCore { Output& _invOutput; Output& _triggerOutput; - Light& _delayLight; - Light& _attackLight; - Light& _decayLight; - Light& _sustainLight; - Light& _releaseLight; + float* _delayLights; + float* _attackLights; + float* _decayLights; + float* _sustainLights; + float* _releaseLights; Light& _attackShape1Light; Light& _attackShape2Light; Light& _attackShape3Light; @@ -102,11 +102,11 @@ struct DADSRHCore { Output& invOutput, Output& triggerOutput, - Light& delayLight, - Light& attackLight, - Light& decayLight, - Light& sustainLight, - Light& releaseLight, + float* delayLights, + float* attackLights, + float* decayLights, + float* sustainLights, + float* releaseLights, Light& attackShape1Light, Light& attackShape2Light, Light& attackShape3Light, @@ -151,11 +151,11 @@ struct DADSRHCore { , _invOutput(invOutput) , _triggerOutput(triggerOutput) - , _delayLight(delayLight) - , _attackLight(attackLight) - , _decayLight(decayLight) - , _sustainLight(sustainLight) - , _releaseLight(releaseLight) + , _delayLights(delayLights) + , _attackLights(attackLights) + , _decayLights(decayLights) + , _sustainLights(sustainLights) + , _releaseLights(releaseLights) , _attackShape1Light(attackShape1Light) , _attackShape2Light(attackShape2Light) , _attackShape3Light(attackShape3Light) @@ -173,11 +173,11 @@ struct DADSRHCore { } void reset(); - void step(); + void step(int c, int channels); - float stepAmount(Param& knob, Input* cv, bool slow, bool allowZero = false); - float knobTime(Param& knob, Input* cv, bool slow, bool allowZero = false); - float knobAmount(Param& knob, Input* cv) const; + float stepAmount(int c, Param& knob, Input* cv, bool slow, bool allowZero = false); + float knobTime(int c, Param& knob, Input* cv, bool slow, bool allowZero = false); + float knobAmount(int c, Param& knob, Input* cv) const; }; } // namespace bogaudio diff --git a/src/shaper_core.cpp b/src/shaper_core.cpp @@ -8,11 +8,11 @@ void ShaperCore::reset() { _stageProgress = 0.0; } -void ShaperCore::step() { +void ShaperCore::step(int c, int channels) { bool complete = false; bool slow = _speedParam.getValue() <= 0.0; if ( - _trigger.process(_triggerParam.getValue() + _triggerInput.getVoltage()) || + _trigger.process(_triggerParam.getValue() + _triggerInput.getPolyVoltage(c)) || (_firstStep && _triggerOnLoad && _shouldTriggerOnLoad && _loopParam.getValue() <= 0.0) ) { _stage = ATTACK_STAGE; @@ -24,28 +24,28 @@ void ShaperCore::step() { break; } case ATTACK_STAGE: { - if (stepStage(_attackParam, _attackInput, slow)) { + if (stepStage(_attackParam, _attackInput, slow, c)) { _stage = ON_STAGE; _stageProgress = 0.0; } break; } case ON_STAGE: { - if (stepStage(_onParam, _onInput, slow)) { + if (stepStage(_onParam, _onInput, slow, c)) { _stage = DECAY_STAGE; _stageProgress = 0.0; } break; } case DECAY_STAGE: { - if (stepStage(_decayParam, _decayInput, slow)) { + if (stepStage(_decayParam, _decayInput, slow, c)) { _stage = OFF_STAGE; _stageProgress = 0.0; } break; } case OFF_STAGE: { - if (stepStage(_offParam, _offInput, slow)) { + if (stepStage(_offParam, _offInput, slow, c)) { complete = true; if (_loopParam.getValue() <= 0.0 || _trigger.isHigh()) { _stage = ATTACK_STAGE; @@ -82,42 +82,50 @@ void ShaperCore::step() { } } - float signalLevel = levelParam(_signalParam, _signalCVInput); - _signalOutput.setVoltage(signalLevel * envelope * _signalInput.getVoltageSum()); + float signalLevel = levelParam(_signalParam, _signalCVInput, c); + _signalOutput.setChannels(channels); + _signalOutput.setVoltage(signalLevel * envelope * _signalInput.getPolyVoltage(c), c); - float envLevel = levelParam(_envParam, _envInput); + float envLevel = levelParam(_envParam, _envInput, c); float envOutput = clamp(envLevel * envelope, 0.0f, 10.0f); - _envOutput.setVoltage(envOutput); - _invOutput.setVoltage(10.0 - envOutput); + _envOutput.setChannels(channels); + _envOutput.setVoltage(envOutput, c); + _invOutput.setChannels(channels); + _invOutput.setVoltage(10.0 - envOutput, c); if (complete) { _triggerOuptutPulseGen.trigger(0.001); } - _triggerOutput.setVoltage(_triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0); + _triggerOutput.setChannels(channels); + _triggerOutput.setVoltage(_triggerOuptutPulseGen.process(APP->engine->getSampleTime()) ? 5.0 : 0.0, c); if (_attackOutput) { - _attackOutput->setVoltage(_stage == ATTACK_STAGE ? 5.0 : 0.0); + _attackOutput->setChannels(channels); + _attackOutput->setVoltage(_stage == ATTACK_STAGE ? 5.0 : 0.0, c); } if (_onOutput) { - _onOutput->setVoltage(_stage == ON_STAGE ? 5.0 : 0.0); + _onOutput->setChannels(channels); + _onOutput->setVoltage(_stage == ON_STAGE ? 5.0 : 0.0, c); } if (_decayOutput) { - _decayOutput->setVoltage(_stage == DECAY_STAGE ? 5.0 : 0.0); + _decayOutput->setChannels(channels); + _decayOutput->setVoltage(_stage == DECAY_STAGE ? 5.0 : 0.0, c); } if (_offOutput) { - _offOutput->setVoltage(_stage == OFF_STAGE ? 5.0 : 0.0); + _offOutput->setChannels(channels); + _offOutput->setVoltage(_stage == OFF_STAGE ? 5.0 : 0.0, c); } - _attackLight.value = _stage == ATTACK_STAGE; - _onLight.value = _stage == ON_STAGE; - _decayLight.value = _stage == DECAY_STAGE; - _offLight.value = _stage == OFF_STAGE; + _attackLights[c] = _stage == ATTACK_STAGE; + _onLights[c] = _stage == ON_STAGE; + _decayLights[c] = _stage == DECAY_STAGE; + _offLights[c] = _stage == OFF_STAGE; _firstStep = false; } -bool ShaperCore::stepStage(Param& knob, Input* cv, bool slow) { - float t = levelParam(knob, cv); +bool ShaperCore::stepStage(Param& knob, Input* cv, bool slow, int c) { + float t = levelParam(knob, cv, c); t = pow(t, 2); t = fmaxf(t, 0.001); t *= slow ? 100.0 : 10.0; @@ -125,10 +133,10 @@ bool ShaperCore::stepStage(Param& knob, Input* cv, bool slow) { return _stageProgress > 1.0; } -float ShaperCore::levelParam(Param& knob, Input* cv) const { +float ShaperCore::levelParam(Param& knob, Input* cv, int c) const { float v = clamp(knob.getValue(), 0.0f, 1.0f); if (cv && cv->isConnected()) { - v *= clamp(cv->getVoltage() / 10.0f, 0.0f, 1.0f); + v *= clamp(cv->getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } return v; } diff --git a/src/shaper_core.hpp b/src/shaper_core.hpp @@ -41,10 +41,10 @@ struct ShaperCore { Output* _decayOutput; Output* _offOutput; - Light& _attackLight; - Light& _onLight; - Light& _decayLight; - Light& _offLight; + float* _attackLights; + float* _onLights; + float* _decayLights; + float* _offLights; bool _firstStep = true; bool& _triggerOnLoad; @@ -83,10 +83,10 @@ struct ShaperCore { Output* decayOutput, Output* offOutput, - Light& attackLight, - Light& onLight, - Light& decayLight, - Light& offLight, + float* attackLights, + float* onLights, + float* decayLights, + float* offLights, bool& triggerOnLoad, bool& shouldTriggerOnLoad @@ -118,10 +118,10 @@ struct ShaperCore { , _decayOutput(decayOutput) , _offOutput(offOutput) - , _attackLight(attackLight) - , _onLight(onLight) - , _decayLight(decayLight) - , _offLight(offLight) + , _attackLights(attackLights) + , _onLights(onLights) + , _decayLights(decayLights) + , _offLights(offLights) , _triggerOnLoad(triggerOnLoad) , _shouldTriggerOnLoad(shouldTriggerOnLoad) @@ -130,10 +130,10 @@ struct ShaperCore { } void reset(); - void step(); + void step(int c, int channels); - bool stepStage(Param& knob, Input* cv, bool slow); - float levelParam(Param& knob, Input* cv) const; + bool stepStage(Param& knob, Input* cv, bool slow, int c); + float levelParam(Param& knob, Input* cv, int c) const; }; } // namespace bogaudio