BogaudioModules

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

commit ed22f3221686792f2269e5ed600d931b8685e60b
parent 3a0fa09839b4085258974a39eb1fde240cd845a8
Author: Matt Demanett <matt@demanett.net>
Date:   Thu, 26 Sep 2019 18:47:15 -0400

Poly: ADDITATOR.

Diffstat:
Msrc/Additator.cpp | 182++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/Additator.hpp | 89+++++++++++++++++++++++++++++++++++++++++++------------------------------------
2 files changed, 155 insertions(+), 116 deletions(-)

diff --git a/src/Additator.cpp b/src/Additator.cpp @@ -1,130 +1,149 @@ #include "Additator.hpp" -void Additator::reset() { - _syncTrigger.reset(); - _phase = PHASE_RESET; +void Additator::Engine::reset() { + syncTrigger.reset(); + phase = PHASE_RESET; } -void Additator::sampleRateChange() { +void Additator::Engine::sampleRateChange() { float sampleRate = APP->engine->getSampleRate(); - _oscillator.setSampleRate(sampleRate); - _maxFrequency = 0.475f * sampleRate; - _phase = PHASE_RESET; - _widthSL.setParams(sampleRate, slewLimitTime, maxWidth); - _oddSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew); - _evenSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew); - _amplitudeNormalizationSL.setParams(sampleRate, slewLimitTime, maxAmplitudeNormalization - minAmplitudeNormalization); - _decaySL.setParams(sampleRate, slewLimitTime, maxDecay - minDecay); - _balanceSL.setParams(sampleRate, slewLimitTime, 2.0f); - _filterSL.setParams(sampleRate, slewLimitTime, maxFilter - minFilter); + oscillator.setSampleRate(sampleRate); + maxFrequency = 0.475f * sampleRate; + phase = PHASE_RESET; + widthSL.setParams(sampleRate, slewLimitTime, maxWidth); + oddSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew); + evenSkewSL.setParams(sampleRate, slewLimitTime, 2.0f * maxSkew); + amplitudeNormalizationSL.setParams(sampleRate, slewLimitTime, maxAmplitudeNormalization - minAmplitudeNormalization); + decaySL.setParams(sampleRate, slewLimitTime, maxDecay - minDecay); + balanceSL.setParams(sampleRate, slewLimitTime, 2.0f); + filterSL.setParams(sampleRate, slewLimitTime, maxFilter - minFilter); } -float Additator::cvValue(Input& cv, bool dc) { - if (!cv.isConnected()) { - return dc ? 1.0f : 0.0f; +void Additator::reset() { + for (int c = 0; c < _channels; ++c) { + _engines[c]->reset(); } - if (dc) { - return clamp(cv.getVoltage() / 10.0f, 0.0f, 1.0f); +} + +void Additator::sampleRateChange() { + for (int c = 0; c < _channels; ++c) { + _engines[c]->sampleRateChange(); } - return clamp(cv.getVoltage() / 5.0f, -1.0f, 1.0f); } bool Additator::active() { return outputs[AUDIO_OUTPUT].isConnected(); } -void Additator::modulate() { - float width = _widthSL.next(clamp(params[WIDTH_PARAM].getValue() + (maxWidth / 2.0f) * cvValue(inputs[WIDTH_INPUT]), 0.0f, maxWidth)); - float oddSkew = _oddSkewSL.next(clamp(params[ODD_SKEW_PARAM].getValue() + cvValue(inputs[ODD_SKEW_INPUT]), -maxSkew, maxSkew)); - float evenSkew = _evenSkewSL.next(clamp(params[EVEN_SKEW_PARAM].getValue() + cvValue(inputs[EVEN_SKEW_INPUT]), -maxSkew, maxSkew)); +int Additator::channels() { + return std::max(1, inputs[PITCH_INPUT].getChannels()); +} + +void Additator::addEngine(int c) { + _engines[c] = new Engine(); + _engines[c]->reset(); + _engines[c]->sampleRateChange(); +} + +void Additator::removeEngine(int c) { + delete _engines[c]; + _engines[c] = NULL; +} + +void Additator::modulateChannel(int c) { + Engine& e = *_engines[c]; + + float width = e.widthSL.next(clamp(params[WIDTH_PARAM].getValue() + (maxWidth / 2.0f) * cvValue(c, inputs[WIDTH_INPUT]), 0.0f, maxWidth)); + float oddSkew = e.oddSkewSL.next(clamp(params[ODD_SKEW_PARAM].getValue() + cvValue(c, inputs[ODD_SKEW_INPUT]), -maxSkew, maxSkew)); + float evenSkew = e.evenSkewSL.next(clamp(params[EVEN_SKEW_PARAM].getValue() + cvValue(c, inputs[EVEN_SKEW_INPUT]), -maxSkew, maxSkew)); if ( - _width != width || - _oddSkew != oddSkew || - _evenSkew != evenSkew + e.width != width || + e.oddSkew != oddSkew || + e.evenSkew != evenSkew ) { - _width = width; - _oddSkew = oddSkew; - _evenSkew = evenSkew; + e.width = width; + e.oddSkew = oddSkew; + e.evenSkew = evenSkew; float multiple = 1.0f; - _oscillator.setPartialFrequencyRatio(1, multiple); - _activePartials = 1; - for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) { + e.oscillator.setPartialFrequencyRatio(1, multiple); + e.activePartials = 1; + for (int i = 2, n = e.oscillator.partialCount(); i <= n; ++i) { float ii = i; if (i % 2 == 0) { - ii += _evenSkew; + ii += e.evenSkew; } else { - ii += _oddSkew; + ii += e.oddSkew; } - if (_oscillator.setPartialFrequencyRatio(i, powf(ii, _width))) { - _activePartials = i; + if (e.oscillator.setPartialFrequencyRatio(i, powf(ii, e.width))) { + e.activePartials = i; } } } - int partials = clamp((int)roundf(params[PARTIALS_PARAM].getValue() * cvValue(inputs[PARTIALS_INPUT], true)), 0, maxPartials); - float amplitudeNormalization = _amplitudeNormalizationSL.next(clamp(params[GAIN_PARAM].getValue() + ((maxAmplitudeNormalization - minAmplitudeNormalization) / 2.0f) * cvValue(inputs[GAIN_INPUT]), minAmplitudeNormalization, maxAmplitudeNormalization)); - float decay = _decaySL.next(clamp(params[DECAY_PARAM].getValue() + ((maxDecay - minDecay) / 2.0f) * cvValue(inputs[DECAY_INPUT]), minDecay, maxDecay)); - float balance = _balanceSL.next(clamp(params[BALANCE_PARAM].getValue() + cvValue(inputs[BALANCE_INPUT]), -1.0f, 1.0f)); - float filter = _filterSL.next(clamp(params[FILTER_PARAM].getValue() + cvValue(inputs[FILTER_INPUT]), minFilter, maxFilter)); + int partials = clamp((int)roundf(params[PARTIALS_PARAM].getValue() * cvValue(c, inputs[PARTIALS_INPUT], true)), 0, maxPartials); + float amplitudeNormalization = e.amplitudeNormalizationSL.next(clamp(params[GAIN_PARAM].getValue() + ((maxAmplitudeNormalization - minAmplitudeNormalization) / 2.0f) * cvValue(c, inputs[GAIN_INPUT]), minAmplitudeNormalization, maxAmplitudeNormalization)); + float decay = e.decaySL.next(clamp(params[DECAY_PARAM].getValue() + ((maxDecay - minDecay) / 2.0f) * cvValue(c, inputs[DECAY_INPUT]), minDecay, maxDecay)); + float balance = e.balanceSL.next(clamp(params[BALANCE_PARAM].getValue() + cvValue(c, inputs[BALANCE_INPUT]), -1.0f, 1.0f)); + float filter = e.filterSL.next(clamp(params[FILTER_PARAM].getValue() + cvValue(c, inputs[FILTER_INPUT]), minFilter, maxFilter)); if ( - _partials != partials || - _amplitudeNormalization != amplitudeNormalization || - _decay != decay || - _balance != balance || - _filter != filter + e.partials != partials || + e.amplitudeNormalization != amplitudeNormalization || + e.decay != decay || + e.balance != balance || + e.filter != filter ) { - int envelopes = _partials != partials ? std::max(_partials, partials) : 0; - _partials = partials; - _amplitudeNormalization = amplitudeNormalization; - _decay = decay; - _balance = balance; - _filter = filter; + int envelopes = e.partials != partials ? std::max(e.partials, partials) : 0; + e.partials = partials; + e.amplitudeNormalization = amplitudeNormalization; + e.decay = decay; + e.balance = balance; + e.filter = filter; float as[maxPartials + 1]; float total = as[1] = 1.0f; - filter = log10f(_filter) + 1.0f; - int np = std::min(_partials, _activePartials); - for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) { + filter = log10f(e.filter) + 1.0f; + int np = std::min(e.partials, e.activePartials); + for (int i = 2, n = e.oscillator.partialCount(); i <= n; ++i) { as[i] = 0.0f; if (i <= np) { - as[i] = powf(i, -_decay) * powf(_filter, i); + as[i] = powf(i, -e.decay) * powf(e.filter, i); if (i % 2 == 0) { - if (_balance > 0.0f) { - as[i] *= 1.0f - _balance; + if (e.balance > 0.0f) { + as[i] *= 1.0f - e.balance; } } else { - if (_balance < 0.0f) { - as[i] *= 1.0f + _balance; + if (e.balance < 0.0f) { + as[i] *= 1.0f + e.balance; } } total += as[i]; } } - float norm = std::max(np / (float)_oscillator.partialCount(), 0.1f); - norm = 1.0f + (_amplitudeNormalization - 1.0f) * norm; + float norm = std::max(np / (float)e.oscillator.partialCount(), 0.1f); + norm = 1.0f + (e.amplitudeNormalization - 1.0f) * norm; norm = std::max(total / norm, 0.7f); - for (int i = 1, n = _oscillator.partialCount(); i <= n; ++i) { + for (int i = 1, n = e.oscillator.partialCount(); i <= n; ++i) { as[i] /= norm; - _oscillator.setPartialAmplitude(i, as[i], i <= envelopes); + e.oscillator.setPartialAmplitude(i, as[i], i <= envelopes); } } float frequency = params[FREQUENCY_PARAM].getValue(); frequency += params[FINE_PARAM].getValue() / 12.0f;; if (inputs[PITCH_INPUT].isConnected()) { - frequency += clamp(inputs[PITCH_INPUT].getVoltage(), -5.0f, 5.0f); + frequency += clamp(inputs[PITCH_INPUT].getVoltage(c), -5.0f, 5.0f); } - frequency = clamp(cvToFrequency(frequency), 20.0f, _maxFrequency); - _oscillator.setFrequency(frequency); + frequency = clamp(cvToFrequency(frequency), 20.0f, e.maxFrequency); + e.oscillator.setFrequency(frequency); Phase phase = params[PHASE_PARAM].getValue() > 1.5f ? PHASE_COSINE : PHASE_SINE; - if (_phase != phase) { - _phase = phase; - _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); + if (e.phase != phase) { + e.phase = phase; + e.oscillator.syncToPhase(e.phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); } } @@ -134,11 +153,24 @@ void Additator::always(const ProcessArgs& args) { lights[COSINE_LIGHT].value = phase == PHASE_COSINE; } -void Additator::processChannel(const ProcessArgs& args, int _c) { - if (_syncTrigger.next(inputs[SYNC_INPUT].getVoltage())) { - _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); +void Additator::processChannel(const ProcessArgs& args, int c) { + Engine& e = *_engines[c]; + + if (e.syncTrigger.next(inputs[SYNC_INPUT].getPolyVoltage(c))) { + e.oscillator.syncToPhase(e.phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); + } + outputs[AUDIO_OUTPUT].setChannels(_channels); + outputs[AUDIO_OUTPUT].setVoltage(e.oscillator.next() * 5.0, c); +} + +float Additator::cvValue(int c, Input& cv, bool dc) { + if (!cv.isConnected()) { + return dc ? 1.0f : 0.0f; + } + if (dc) { + return clamp(cv.getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); } - outputs[AUDIO_OUTPUT].setVoltage(_oscillator.next() * 5.0); + return clamp(cv.getPolyVoltage(c) / 5.0f, -1.0f, 1.0f); } struct AdditatorWidget : ModuleWidget { diff --git a/src/Additator.hpp b/src/Additator.hpp @@ -58,41 +58,48 @@ struct Additator : BGModule { PHASE_COSINE }; - const int maxPartials = 100; - const float maxWidth = 2.0f; - const float maxSkew = 0.99f; - const float minAmplitudeNormalization = 1.0f; - const float maxAmplitudeNormalization = 5.0f; - const float minDecay = -1.0f; - const float maxDecay = 3.0f; - const float minFilter = 0.1; - const float maxFilter = 1.9; - const float slewLimitTime = 1.0f; - - int _partials = 0; - float _width = 0.0f; - float _oddSkew = 0.0f; - float _evenSkew = 0.0f; - float _amplitudeNormalization = 0.0f; - float _decay = 0.0f; - float _balance = 0.0f; - float _filter = 0.0f; - Phase _phase = PHASE_RESET; - float _maxFrequency = 0.0f; - int _activePartials = 1; - SineBankOscillator _oscillator; - PositiveZeroCrossing _syncTrigger; - bogaudio::dsp::SlewLimiter _widthSL; - bogaudio::dsp::SlewLimiter _oddSkewSL; - bogaudio::dsp::SlewLimiter _evenSkewSL; - bogaudio::dsp::SlewLimiter _amplitudeNormalizationSL; - bogaudio::dsp::SlewLimiter _decaySL; - bogaudio::dsp::SlewLimiter _balanceSL; - bogaudio::dsp::SlewLimiter _filterSL; - - Additator() - : _oscillator(1000.0f, 100.0f, maxPartials) - { + static constexpr int maxPartials = 100; + static constexpr float maxWidth = 2.0f; + static constexpr float maxSkew = 0.99f; + static constexpr float minAmplitudeNormalization = 1.0f; + static constexpr float maxAmplitudeNormalization = 5.0f; + static constexpr float minDecay = -1.0f; + static constexpr float maxDecay = 3.0f; + static constexpr float minFilter = 0.1; + static constexpr float maxFilter = 1.9; + static constexpr float slewLimitTime = 1.0f; + + struct Engine { + int partials = 0; + float width = 0.0f; + float oddSkew = 0.0f; + float evenSkew = 0.0f; + float amplitudeNormalization = 0.0f; + float decay = 0.0f; + float balance = 0.0f; + float filter = 0.0f; + Phase phase = PHASE_RESET; + float maxFrequency = 0.0f; + int activePartials = 1; + SineBankOscillator oscillator; + PositiveZeroCrossing syncTrigger; + bogaudio::dsp::SlewLimiter widthSL; + bogaudio::dsp::SlewLimiter oddSkewSL; + bogaudio::dsp::SlewLimiter evenSkewSL; + bogaudio::dsp::SlewLimiter amplitudeNormalizationSL; + bogaudio::dsp::SlewLimiter decaySL; + bogaudio::dsp::SlewLimiter balanceSL; + bogaudio::dsp::SlewLimiter filterSL; + + Engine() : oscillator(1000.0f, 100.0f, maxPartials) {} + + void reset(); + void sampleRateChange(); + }; + + Engine* _engines[maxChannels] {}; + + Additator() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); configParam<FrequencyParamQuantity>(FREQUENCY_PARAM, -3.0f, 6.0f, 0.0f, "Frequency", " Hz"); configParam(PARTIALS_PARAM, 1.0f, Additator::maxPartials, Additator::maxPartials / 5.0f, "Partials"); @@ -105,18 +112,18 @@ struct Additator : BGModule { configParam(BALANCE_PARAM, -1.0f, 1.0f, 0.0f, "Balance", "%", 0.0f, 100.0f); configParam(FILTER_PARAM, minFilter, maxFilter, (maxFilter - minFilter) / 2.0 + minFilter, "Filter"); configParam(PHASE_PARAM, 1.0f, 2.0f, 1.0f, "Phase"); - - reset(); - sampleRateChange(); } void reset() override; void sampleRateChange() override; bool active() override; - void modulate() override; - float cvValue(Input& cv, bool dc = false); + 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 processChannel(const ProcessArgs& args, int c) override; + float cvValue(int c, Input& cv, bool dc = false); }; } // namespace bogaudio