BogaudioModules

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

commit df847e4aec2584f1ef30cd6eb70c0e661066e1a5
parent 53d8f31000d0c9e12c3733d6a2c65b3a0e1569c2
Author: Matt Demanett <matt@demanett.net>
Date:   Sun, 19 Apr 2020 01:38:18 -0400

Speed up MIX4/8; drastically speed up their expanders.

Diffstat:
Msrc/Mix4.cpp | 122++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/Mix4.hpp | 4+++-
Msrc/Mix8.cpp | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/Mix8.hpp | 4+++-
Msrc/mixer.cpp | 46++++------------------------------------------
Msrc/mixer.hpp | 63+--------------------------------------------------------------
Asrc/mixer_expander.cpp | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mixer_expander.hpp | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 312 insertions(+), 183 deletions(-)

diff --git a/src/Mix4.cpp b/src/Mix4.cpp @@ -34,6 +34,24 @@ void Mix4::processAll(const ProcessArgs& args) { fromExp = fromExpander(); } + if (!( + inputs[IN1_INPUT].isConnected() || + inputs[IN2_INPUT].isConnected() || + inputs[IN3_INPUT].isConnected() || + inputs[IN4_INPUT].isConnected() + )) { + if (_wasActive > 0) { + --_wasActive; + for (int i = 0; i < 4; ++i) { + _channels[i]->reset(); + toExp->active[i] = false; + } + _rmsLevel = 0.0f; + } + return; + } + _wasActive = 2; + bool solo = params[MUTE1_PARAM].getValue() > 1.5f || params[MUTE2_PARAM].getValue() > 1.5f || @@ -48,18 +66,23 @@ void Mix4::processAll(const ProcessArgs& args) { sample = inputs[IN1_INPUT].getVoltageSum(); } _channels[0]->next(sample, solo); - toExp->preFader[0] = sample; + toExp->active[0] = inputs[IN1_INPUT].isConnected(); for (int i = 1; i < 4; ++i) { float sample = 0.0f; + bool channelActive = false; if (inputs[IN1_INPUT + 3 * i].isConnected()) { sample = inputs[IN1_INPUT + 3 * i].getVoltageSum(); + _channels[i]->next(sample, solo); + channelActive = true; } else if (_polyChannelOffset >= 0) { sample = inputs[IN1_INPUT].getPolyVoltage(_polyChannelOffset + i); + _channels[i]->next(sample, solo); + channelActive = true; } - _channels[i]->next(sample, solo); toExp->preFader[i] = sample; + toExp->active[i] = channelActive; } } @@ -243,6 +266,12 @@ void Mix4x::sampleRateChange() { _returnBSL.setParams(sr, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); } +void Mix4x::modulate() { + for (int i = 0; i < 4; ++i) { + _channels[i]->modulate(); + } +} + void Mix4x::processAll(const ProcessArgs& args) { if (!baseConnected()) { outputs[SEND_A_OUTPUT].setVoltage(0.0f); @@ -254,50 +283,65 @@ void Mix4x::processAll(const ProcessArgs& args) { Mix4ExpanderMessage* to = toBase(); float sendA = 0.0f; float sendB = 0.0f; + bool sendAActive = outputs[SEND_A_OUTPUT].isConnected(); + bool sendBActive = outputs[SEND_B_OUTPUT].isConnected(); for (int i = 0; i < 4; ++i) { - _channels[i]->next(from->preFader[i], from->postFader[i]); - to->postEQ[i] = _channels[i]->postEQ; - sendA += _channels[i]->sendA; - sendB += _channels[i]->sendB; + if (from->active[i]) { + _channels[i]->next(from->preFader[i], from->postFader[i], sendAActive, sendBActive); + to->postEQ[i] = _channels[i]->postEQ; + sendA += _channels[i]->sendA; + sendB += _channels[i]->sendB; + } + else { + to->postEQ[i] = from->preFader[i]; + } } outputs[SEND_A_OUTPUT].setVoltage(_saturatorA.next(sendA)); outputs[SEND_B_OUTPUT].setVoltage(_saturatorA.next(sendB)); - float levelA = clamp(params[LEVEL_A_PARAM].getValue(), 0.0f, 1.0f); - if (inputs[LEVEL_A_INPUT].isConnected()) { - levelA *= clamp(inputs[LEVEL_A_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); - } - levelA = 1.0f - levelA; - levelA *= Amplifier::minDecibels; - _returnAAmp.setLevel(_returnASL.next(levelA)); - if (inputs[L_A_INPUT].isConnected()) { - to->returnA[0] = _returnAAmp.next(inputs[L_A_INPUT].getVoltage()); - } - else { - to->returnA[0] = 0.0f; - } - if (inputs[R_A_INPUT].isConnected()) { - to->returnA[1] = _returnAAmp.next(inputs[R_A_INPUT].getVoltage()); - } - else { - to->returnA[1] = to->returnA[0]; + bool lAActive = inputs[L_A_INPUT].isConnected(); + bool rAActive = inputs[R_A_INPUT].isConnected(); + if (lAActive || rAActive) { + float levelA = clamp(params[LEVEL_A_PARAM].getValue(), 0.0f, 1.0f); + if (inputs[LEVEL_A_INPUT].isConnected()) { + levelA *= clamp(inputs[LEVEL_A_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); + } + levelA = 1.0f - levelA; + levelA *= Amplifier::minDecibels; + _returnAAmp.setLevel(_returnASL.next(levelA)); + if (lAActive) { + to->returnA[0] = _returnAAmp.next(inputs[L_A_INPUT].getVoltage()); + } + else { + to->returnA[0] = 0.0f; + } + if (rAActive) { + to->returnA[1] = _returnAAmp.next(inputs[R_A_INPUT].getVoltage()); + } + else { + to->returnA[1] = to->returnA[0]; + } } - float levelB = clamp(params[LEVEL_B_PARAM].getValue(), 0.0f, 1.0f); - levelB = 1.0f - levelB; - levelB *= Amplifier::minDecibels; - _returnBAmp.setLevel(_returnBSL.next(levelB)); - if (inputs[L_B_INPUT].isConnected()) { - to->returnB[0] = _returnBAmp.next(inputs[L_B_INPUT].getVoltage()); - } - else { - to->returnB[0] = 0.0f; - } - if (inputs[R_B_INPUT].isConnected()) { - to->returnB[1] = _returnBAmp.next(inputs[R_B_INPUT].getVoltage()); - } - else { - to->returnB[1] = to->returnB[0]; + bool lBActive = inputs[L_B_INPUT].isConnected(); + bool rBActive = inputs[R_B_INPUT].isConnected(); + if (lBActive || rBActive) { + float levelB = clamp(params[LEVEL_B_PARAM].getValue(), 0.0f, 1.0f); + levelB = 1.0f - levelB; + levelB *= Amplifier::minDecibels; + _returnBAmp.setLevel(_returnBSL.next(levelB)); + if (lBActive) { + to->returnB[0] = _returnBAmp.next(inputs[L_B_INPUT].getVoltage()); + } + else { + to->returnB[0] = 0.0f; + } + if (rBActive) { + to->returnB[1] = _returnBAmp.next(inputs[R_B_INPUT].getVoltage()); + } + else { + to->returnB[1] = to->returnB[0]; + } } } diff --git a/src/Mix4.hpp b/src/Mix4.hpp @@ -1,7 +1,7 @@ #pragma once #include "bogaudio.hpp" -#include "mixer.hpp" +#include "mixer_expander.hpp" #include "dsp/signal.hpp" using namespace bogaudio::dsp; @@ -67,6 +67,7 @@ struct Mix4 : ExpandableModule<Mix4ExpanderMessage, BGModule> { RootMeanSquare _rms; float _rmsLevel = 0.0f; Mix4ExpanderMessage _dummyExpanderMessage; + int _wasActive = 0; Mix4() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); @@ -217,6 +218,7 @@ struct Mix4x : ExpanderModule<Mix4ExpanderMessage, BGModule> { } void sampleRateChange() override; + void modulate() override; void processAll(const ProcessArgs& args) override; }; diff --git a/src/Mix8.cpp b/src/Mix8.cpp @@ -34,6 +34,28 @@ void Mix8::processAll(const ProcessArgs& args) { fromExp = fromExpander(); } + if (!( + inputs[IN1_INPUT].isConnected() || + inputs[IN2_INPUT].isConnected() || + inputs[IN3_INPUT].isConnected() || + inputs[IN4_INPUT].isConnected() || + inputs[IN5_INPUT].isConnected() || + inputs[IN6_INPUT].isConnected() || + inputs[IN7_INPUT].isConnected() || + inputs[IN8_INPUT].isConnected() + )) { + if (_wasActive > 0) { + --_wasActive; + for (int i = 0; i < 8; ++i) { + _channels[i]->reset(); + toExp->active[i] = false; + } + _rmsLevel = 0.0f; + } + return; + } + _wasActive = 2; + bool solo = params[MUTE1_PARAM].getValue() > 1.5f || params[MUTE2_PARAM].getValue() > 1.5f || @@ -53,17 +75,27 @@ void Mix8::processAll(const ProcessArgs& args) { } _channels[0]->next(sample, solo); toExp->preFader[0] = sample; + toExp->active[0] = inputs[IN1_INPUT].isConnected(); for (int i = 1; i < 8; ++i) { float sample = 0.0f; + bool channelActive = false; if (inputs[IN1_INPUT + 3 * i].isConnected()) { sample = inputs[IN1_INPUT + 3 * i].getVoltageSum(); + _channels[i]->next(sample, solo); + channelActive = true; } else if (_polyChannelOffset >= 0) { sample = inputs[IN1_INPUT].getPolyVoltage(_polyChannelOffset + i); + _channels[i]->next(sample, solo); + channelActive = true; + } + else { + _channels[i]->out = 0.0f; + _channels[i]->rms = 0.0f; } - _channels[i]->next(sample, solo); toExp->preFader[i] = sample; + toExp->active[i] = channelActive; } } @@ -293,6 +325,12 @@ void Mix8x::sampleRateChange() { _returnBSL.setParams(sr, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); } +void Mix8x::modulate() { + for (int i = 0; i < 8; ++i) { + _channels[i]->modulate(); + } +} + void Mix8x::processAll(const ProcessArgs& args) { if (!baseConnected()) { outputs[SEND_A_OUTPUT].setVoltage(0.0f); @@ -304,50 +342,65 @@ void Mix8x::processAll(const ProcessArgs& args) { Mix8ExpanderMessage* to = toBase(); float sendA = 0.0f; float sendB = 0.0f; + bool sendAActive = outputs[SEND_A_OUTPUT].isConnected(); + bool sendBActive = outputs[SEND_B_OUTPUT].isConnected(); for (int i = 0; i < 8; ++i) { - _channels[i]->next(from->preFader[i], from->postFader[i]); - to->postEQ[i] = _channels[i]->postEQ; - sendA += _channels[i]->sendA; - sendB += _channels[i]->sendB; + if (from->active[i]) { + _channels[i]->next(from->preFader[i], from->postFader[i], sendAActive, sendBActive); + to->postEQ[i] = _channels[i]->postEQ; + sendA += _channels[i]->sendA; + sendB += _channels[i]->sendB; + } + else { + to->postEQ[i] = from->preFader[i]; + } } outputs[SEND_A_OUTPUT].setVoltage(_saturatorA.next(sendA)); outputs[SEND_B_OUTPUT].setVoltage(_saturatorA.next(sendB)); - float levelA = clamp(params[LEVEL_A_PARAM].getValue(), 0.0f, 1.0f); - if (inputs[LEVEL_A_INPUT].isConnected()) { - levelA *= clamp(inputs[LEVEL_A_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); - } - levelA = 1.0f - levelA; - levelA *= Amplifier::minDecibels; - _returnAAmp.setLevel(_returnASL.next(levelA)); - if (inputs[L_A_INPUT].isConnected()) { - to->returnA[0] = _returnAAmp.next(inputs[L_A_INPUT].getVoltage()); - } - else { - to->returnA[0] = 0.0f; - } - if (inputs[R_A_INPUT].isConnected()) { - to->returnA[1] = _returnAAmp.next(inputs[R_A_INPUT].getVoltage()); - } - else { - to->returnA[1] = to->returnA[0]; + bool lAActive = inputs[L_A_INPUT].isConnected(); + bool rAActive = inputs[R_A_INPUT].isConnected(); + if (lAActive || rAActive) { + float levelA = clamp(params[LEVEL_A_PARAM].getValue(), 0.0f, 1.0f); + if (inputs[LEVEL_A_INPUT].isConnected()) { + levelA *= clamp(inputs[LEVEL_A_INPUT].getVoltage() / 10.0f, 0.0f, 1.0f); + } + levelA = 1.0f - levelA; + levelA *= Amplifier::minDecibels; + _returnAAmp.setLevel(_returnASL.next(levelA)); + if (lAActive) { + to->returnA[0] = _returnAAmp.next(inputs[L_A_INPUT].getVoltage()); + } + else { + to->returnA[0] = 0.0f; + } + if (rAActive) { + to->returnA[1] = _returnAAmp.next(inputs[R_A_INPUT].getVoltage()); + } + else { + to->returnA[1] = to->returnA[0]; + } } - float levelB = clamp(params[LEVEL_B_PARAM].getValue(), 0.0f, 1.0f); - levelB = 1.0f - levelB; - levelB *= Amplifier::minDecibels; - _returnBAmp.setLevel(_returnBSL.next(levelB)); - if (inputs[L_B_INPUT].isConnected()) { - to->returnB[0] = _returnBAmp.next(inputs[L_B_INPUT].getVoltage()); - } - else { - to->returnB[0] = 0.0f; - } - if (inputs[R_B_INPUT].isConnected()) { - to->returnB[1] = _returnBAmp.next(inputs[R_B_INPUT].getVoltage()); - } - else { - to->returnB[1] = to->returnB[0]; + bool lBActive = inputs[L_B_INPUT].isConnected(); + bool rBActive = inputs[R_B_INPUT].isConnected(); + if (lBActive || rBActive) { + float levelB = clamp(params[LEVEL_B_PARAM].getValue(), 0.0f, 1.0f); + levelB = 1.0f - levelB; + levelB *= Amplifier::minDecibels; + _returnBAmp.setLevel(_returnBSL.next(levelB)); + if (lBActive) { + to->returnB[0] = _returnBAmp.next(inputs[L_B_INPUT].getVoltage()); + } + else { + to->returnB[0] = 0.0f; + } + if (rBActive) { + to->returnB[1] = _returnBAmp.next(inputs[R_B_INPUT].getVoltage()); + } + else { + to->returnB[1] = to->returnB[0]; + } } } diff --git a/src/Mix8.hpp b/src/Mix8.hpp @@ -1,7 +1,7 @@ #pragma once #include "bogaudio.hpp" -#include "mixer.hpp" +#include "mixer_expander.hpp" #include "dsp/signal.hpp" using namespace bogaudio::dsp; @@ -91,6 +91,7 @@ struct Mix8 : ExpandableModule<Mix8ExpanderMessage, BGModule> { RootMeanSquare _rms; float _rmsLevel = 0.0f; Mix8ExpanderMessage _dummyExpanderMessage; + int _wasActive = 0; Mix8() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); @@ -325,6 +326,7 @@ struct Mix8x : ExpanderModule<Mix8ExpanderMessage, BGModule> { } void sampleRateChange() override; + void modulate() override; void processAll(const ProcessArgs& args) override; }; diff --git a/src/mixer.cpp b/src/mixer.cpp @@ -10,6 +10,10 @@ void MixerChannel::setSampleRate(float sampleRate) { _rms.setSampleRate(sampleRate); } +void MixerChannel::reset() { + out = rms = 0.0f; +} + void MixerChannel::next(float sample, bool solo, int c) { float mute = _muteParam.getValue(); if (_muteInput) { @@ -34,48 +38,6 @@ void MixerChannel::next(float sample, bool solo, int c) { } -void MixerExpanderChannel::setSampleRate(float sampleRate) { - _sendASL.setParams(sampleRate, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); - _sendBSL.setParams(sampleRate, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); -} - -float MixerExpanderChannel::knobToDb(Param& p) { - float v = clamp(p.getValue(), -1.0f, 1.0f); - if (v < 0.0f) { - return -v * Equalizer::cutDb; - } - return v * Equalizer::gainDb; -} - -void MixerExpanderChannel::next(float preFader, float postFader) { - _eq.setParams( - APP->engine->getSampleRate(), - knobToDb(_lowParam), - knobToDb(_midParam), - knobToDb(_highParam) - ); - postEQ = _eq.next(postFader); - - float level = clamp(_sendAParam.getValue(), 0.0f, 1.0f); - if (_sendAInput.isConnected()) { - level *= clamp(_sendAInput.getVoltage() / 10.0f, 0.0f, 1.0f); - } - level = 1.0f - level; - level *= Amplifier::minDecibels; - _sendAAmp.setLevel(_sendASL.next(level)); - sendA = _sendAAmp.next(_preAParam.getValue() > 0.5f ? preFader : postEQ); - - level = clamp(_sendBParam.getValue(), 0.0f, 1.0f); - if (_sendBInput.isConnected()) { - level *= clamp(_sendBInput.getVoltage() / 10.0f, 0.0f, 1.0f); - } - level = 1.0f - level; - level *= Amplifier::minDecibels; - _sendBAmp.setLevel(_sendBSL.next(level)); - sendB = _sendBAmp.next(_preBParam.getValue() > 0.5f ? preFader : postEQ); -} - - void MuteButton::onButton(const event::Button& e) { if (!(e.action == GLFW_PRESS && (e.mods & RACK_MOD_MASK) == 0)) { return; diff --git a/src/mixer.hpp b/src/mixer.hpp @@ -1,8 +1,6 @@ #pragma once #include "bogaudio.hpp" -#include "expanders.hpp" -#include "dsp/filters/equalizer.hpp" #include "dsp/signal.hpp" using namespace bogaudio::dsp; @@ -11,15 +9,6 @@ namespace bogaudio { #define MIXER_PAN_SLEW_MS 10.0f -template<int N> -struct MixerExpanderMessage : ExpanderMessage { - float preFader[N] {}; - float postFader[N] {}; - float postEQ[N] {}; - float returnA[2] {}; - float returnB[2] {}; -}; - struct MixerChannel { static const float maxDecibels; static const float minDecibels; @@ -54,60 +43,10 @@ struct MixerChannel { } void setSampleRate(float sampleRate); + void reset(); void next(float sample, bool solo, int c = 0); // outputs on members out, rms. }; -struct MixerExpanderChannel { - Equalizer _eq; - Amplifier _sendAAmp; - Amplifier _sendBAmp; - bogaudio::dsp::SlewLimiter _sendASL; - bogaudio::dsp::SlewLimiter _sendBSL; - - Param& _lowParam; - Param& _midParam; - Param& _highParam; - Param& _sendAParam; - Param& _sendBParam; - Param& _preAParam; - Param& _preBParam; - Input& _sendAInput; - Input& _sendBInput; - - float postEQ = 0.0f; - float sendA = 0.0f; - float sendB = 0.0f; - - MixerExpanderChannel( - Param& low, - Param& mid, - Param& high, - Param& sendA, - Param& sendB, - Param& preA, - Param& preB, - Input& cvA, - Input& cvB, - float sampleRate = 1000.0f - ) - : _lowParam(low) - , _midParam(mid) - , _highParam(high) - , _sendAParam(sendA) - , _sendBParam(sendB) - , _preAParam(preA) - , _preBParam(preB) - , _sendAInput(cvA) - , _sendBInput(cvB) - { - setSampleRate(sampleRate); - } - - void setSampleRate(float sampleRate); - float knobToDb(Param& p); - void next(float preFader, float postFader); // outputs on members postEQ, sendA, sendB -}; - struct MuteButton : ToggleButton { MuteButton() { addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/button_18px_0.svg"))); diff --git a/src/mixer_expander.cpp b/src/mixer_expander.cpp @@ -0,0 +1,56 @@ + +#include "mixer_expander.hpp" + +void MixerExpanderChannel::setSampleRate(float sampleRate) { + _sendASL.setParams(sampleRate, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); + _sendBSL.setParams(sampleRate, MixerChannel::levelSlewTimeMS, MixerChannel::maxDecibels - MixerChannel::minDecibels); +} + +float MixerExpanderChannel::knobToDb(Param& p) { + float v = clamp(p.getValue(), -1.0f, 1.0f); + if (v < 0.0f) { + return -v * Equalizer::cutDb; + } + return v * Equalizer::gainDb; +} + +void MixerExpanderChannel::modulate() { + _eq.setParams( + APP->engine->getSampleRate(), + knobToDb(_lowParam), + knobToDb(_midParam), + knobToDb(_highParam) + ); +} + +void MixerExpanderChannel::next(float preFader, float postFader, bool sendAActive, bool sendBActive) { + postEQ = _eq.next(postFader); + + if (sendAActive) { + float level = clamp(_sendAParam.getValue(), 0.0f, 1.0f); + if (_sendAInput.isConnected()) { + level *= clamp(_sendAInput.getVoltage() / 10.0f, 0.0f, 1.0f); + } + level = 1.0f - level; + level *= Amplifier::minDecibels; + _sendAAmp.setLevel(_sendASL.next(level)); + sendA = _sendAAmp.next(_preAParam.getValue() > 0.5f ? preFader : postEQ); + } + else { + sendA = 0.0f; + } + + if (sendBActive) { + float level = clamp(_sendBParam.getValue(), 0.0f, 1.0f); + if (_sendBInput.isConnected()) { + level *= clamp(_sendBInput.getVoltage() / 10.0f, 0.0f, 1.0f); + } + level = 1.0f - level; + level *= Amplifier::minDecibels; + _sendBAmp.setLevel(_sendBSL.next(level)); + sendB = _sendBAmp.next(_preBParam.getValue() > 0.5f ? preFader : postEQ); + } + else { + sendB = 0.0f; + } +} diff --git a/src/mixer_expander.hpp b/src/mixer_expander.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "mixer.hpp" +#include "expanders.hpp" +#include "dsp/filters/equalizer.hpp" + +namespace bogaudio { + +template<int N> +struct MixerExpanderMessage : ExpanderMessage { + bool active[N] {}; + float preFader[N] {}; + float postFader[N] {}; + float postEQ[N] {}; + float returnA[2] {}; + float returnB[2] {}; +}; + +struct MixerExpanderChannel { + Equalizer _eq; + Amplifier _sendAAmp; + Amplifier _sendBAmp; + bogaudio::dsp::SlewLimiter _sendASL; + bogaudio::dsp::SlewLimiter _sendBSL; + + Param& _lowParam; + Param& _midParam; + Param& _highParam; + Param& _sendAParam; + Param& _sendBParam; + Param& _preAParam; + Param& _preBParam; + Input& _sendAInput; + Input& _sendBInput; + + float postEQ = 0.0f; + float sendA = 0.0f; + float sendB = 0.0f; + + MixerExpanderChannel( + Param& low, + Param& mid, + Param& high, + Param& sendA, + Param& sendB, + Param& preA, + Param& preB, + Input& cvA, + Input& cvB, + float sampleRate = 1000.0f + ) + : _lowParam(low) + , _midParam(mid) + , _highParam(high) + , _sendAParam(sendA) + , _sendBParam(sendB) + , _preAParam(preA) + , _preBParam(preB) + , _sendAInput(cvA) + , _sendBInput(cvB) + { + setSampleRate(sampleRate); + } + + void setSampleRate(float sampleRate); + float knobToDb(Param& p); + void modulate(); + void next(float preFader, float postFader, bool sendAActive, bool sendBActive); // outputs on members postEQ, sendA, sendB +}; + +} // namespace bogaudio