BogaudioModules

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

commit a44057ecb3821dd895c141499c4f42dded8a33a3
parent 05d0f3ffc5e9e063e5bba9f80050230eaa18cce7
Author: Matt Demanett <matt@demanett.net>
Date:   Tue, 20 Feb 2018 01:27:10 -0500

Work-in-progress additive oscillator.

Diffstat:
Ares-src/Additator-src.svg | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares/Additator.svg | 0
Asrc/Additator.cpp | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Additator.hpp | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Test.hpp | 16+++++++---------
Msrc/bogaudio.cpp | 4++++
Msrc/dsp/oscillator.cpp | 33+++++++++++++++++++--------------
Msrc/dsp/oscillator.hpp | 12+++++++-----
8 files changed, 365 insertions(+), 28 deletions(-)

diff --git a/res-src/Additator-src.svg b/res-src/Additator-src.svg @@ -0,0 +1,134 @@ +<svg + version="1.1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + width="300" + height="380" + viewBox="0 0 300 380" +> + <style> + text { + fill: #333; + font-family: 'Roboto', sans-serif; + font-weight: bold; + } + text.title { + font-family: 'Comfortaa', sans-serif; + font-weight: normal; + } + text.brand { + font-family: 'Audiowide', sans-serif; + font-weight: bold; + } + </style> + + <defs> + <symbol id="knob" viewBox="0 0 38px 38px"> + <g transform="translate(19 19)"> + <polyline points="-5,0 5,0" stroke-width="1" stroke="#00f" /> + <polyline points="0,-5 0,5" stroke-width="1" stroke="#00f" /> + <circle cx="0" cy="0" r="18" stroke-width="1" stroke="#00f" fill="none" /> + </g> + </symbol> + + <symbol id="knob-medium" viewBox="0 0 26px 26px"> + <g transform="translate(13 13)"> + <polyline points="-3,0 3,0" stroke-width="1" stroke="#00f" /> + <polyline points="0,-3 0,3" stroke-width="1" stroke="#00f" /> + <circle cx="0" cy="0" r="12" stroke-width="1" stroke="#00f" fill="none" /> + </g> + </symbol> + + <symbol id="input" viewBox="0 0 24px 24px"> + <g transform="translate(12 12)"> + <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" /> + <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" /> + </g> + </symbol> + + <symbol id="output" viewBox="0 0 24px 24px"> + <g transform="translate(12 12)"> + <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" /> + <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" /> + </g> + </symbol> + </defs> + + <rect width="100%" height="100%" fill="#ddd" /> + <polyline points="1,1 299,1 299,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" /> + <polyline points="0.5,0.5 299.5,0.5 299.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" /> + <polyline points="0,0 300,0 300,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" /> + + <!-- <rect width="80" height="20" fill="#0f0" transform="translate(0 0)" /> --> + <!-- <rect width="80" height="20" fill="#0f0" transform="translate(220 0)" /> --> + + <text class="title" x="86" y="19" font-size="12pt" letter-spacing="4px">ADDITATOR</text> + <g transform="translate(110 374)"> + <text class="brand" font-size="8pt" letter-spacing="2px">BOGAUDIO</text> + <rect width="3.0" height="3" fill="#ddd" transform="translate(24 -5)" /> + </g> + + <g transform="translate(10 50)"> + <g transform="translate(10 0)"> + <!-- <polyline points="0,19 70,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 57) rotate(270)">FREQUENCY</text> + <use id="FREQUENCY_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + <g transform="translate(90 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">PARTIALS</text> + <use id="PARTIALS_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + <g transform="translate(170 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 39) rotate(270)">WIDTH</text> + <use id="WIDTH_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + <g transform="translate(220 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <!-- <text font-size="8pt" letter-spacing="2px" transform="translate(8 39) rotate(270)">WIDTH</text> --> + <use id="EVEN_WIDTH_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + </g> + </g> + + <g transform="translate(10 130)"> + <g transform="translate(10 0)"> + <!-- <polyline points="0,19 70,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 40) rotate(270)">DECAY</text> + <use id="DECAY_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + <g transform="translate(90 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 36) rotate(270)">ODDS</text> + <use id="ODD_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + </g> + <g transform="translate(150 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 38) rotate(270)">EVENS</text> + <use id="EVEN_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + </g> + </g> + + <g transform="translate(0 320)"> + <g transform="translate(10 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="PITCH_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="1px" transform="translate(1 36)">V/OCT</text> + </g> + <g transform="translate(50 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="SYNC_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(1 36)">SYNC</text> + </g> + <g transform="translate(150 0)"> + <!-- <polyline points="0,19 68,19" stroke="#0f0" stroke-width="1" fill="none" /> --> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 40) rotate(270)">PHASE</text> + <use id="PHASE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + </g> + <g transform="translate(260 0)"> + <rect width="30" height="40" rx="5" fill="#bbb" /> + <use id="AUDIO_OUTPUT" xlink:href="#output" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(5 36)">OUT</text> + </g> + </g> +</svg> diff --git a/res/Additator.svg b/res/Additator.svg Binary files differ. diff --git a/src/Additator.cpp b/src/Additator.cpp @@ -0,0 +1,130 @@ + +#include "Additator.hpp" + +void Additator::onReset() { + _syncTrigger.reset(); + _phase = -10.0f; // trigger a phase sync. +} + +void Additator::onSampleRateChange() { + _oscillator.setSampleRate(engineGetSampleRate()); + _phase = -10.0f; // trigger a phase sync. +} + +void Additator::step() { + float width = params[WIDTH_PARAM].value; + float evenWidth = params[EVEN_WIDTH_PARAM].value; + if (_width != width || evenWidth != _evenWidth) { + _width = width; + _evenWidth = evenWidth; + + float multiple = 1.0f; + _oscillator.setPartialFrequencyRatio(1, multiple); + for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) { + float ii = i; + if (i % 2 == 0) { + ii += _evenWidth; + } + _oscillator.setPartialFrequencyRatio(i, powf(ii, _width)); + } + } + + int partials = roundf(params[PARTIALS_PARAM].value); + float decay = params[DECAY_PARAM].value; + float oddScale = params[ODD_SCALE_PARAM].value; + float evenScale = params[EVEN_SCALE_PARAM].value; + if (_partials != partials || _decay != decay || _oddScale != oddScale || _evenScale != evenScale) { + _partials = partials; + _decay = decay; + _oddScale = oddScale; + _evenScale = evenScale; + + float as[maxPartials + 1]; + float total = as[1] = 1.0f; + for (int i = 2, n = _oscillator.partialCount(); i <= n; ++i) { + as[i] = 0.0f; + if (i <= _partials) { + as[i] = 1.0f / powf(i, _decay); + as[i] *= i % 2 == 1 ? _oddScale : _evenScale; + total += as[i]; + } + } + total /= 3.0f; // pretty arbitrary, this. + for (int i = 1, n = _oscillator.partialCount(); i <= n; ++i) { + as[i] /= total; + _oscillator.setPartialAmplitude(i, as[i]); + } + } + + float frequency = params[FREQUENCY_PARAM].value; + if (inputs[PITCH_INPUT].active) { + frequency += inputs[PITCH_INPUT].value; + } + frequency = clamp(cvToFrequency(frequency), 50.0f, 10000.0f); + _oscillator.setFrequency(frequency); + + if (_syncTrigger.process(inputs[SYNC_INPUT].value)) { + _oscillator.syncToPhase(_phase * M_PI / 2.0f); + } + float phase = params[PHASE_PARAM].value; + if (_phase != phase) { + _phase = phase; + _oscillator.syncToPhase(phase * M_PI / 2.0f); + } + + outputs[AUDIO_OUTPUT].value = _oscillator.next() * 5.0; +} + +struct AdditatorWidget : ModuleWidget { + AdditatorWidget(Additator* module) : ModuleWidget(module) { + box.size = Vec(RACK_GRID_WIDTH * 20, RACK_GRID_HEIGHT); + + { + SVGPanel *panel = new SVGPanel(); + panel->box.size = box.size; + panel->setBackground(SVG::load(assetPlugin(plugin, "res/Additator.svg"))); + addChild(panel); + } + + addChild(Widget::create<ScrewSilver>(Vec(15, 0))); + addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0))); + addChild(Widget::create<ScrewSilver>(Vec(15, 365))); + addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365))); + + // generated by svg_widgets.rb + auto frequencyParamPosition = Vec(40.5, 50.5); + auto partialsParamPosition = Vec(120.5, 50.5); + auto widthParamPosition = Vec(200.5, 50.5); + auto evenWidthParamPosition = Vec(250.5, 56.5); + auto decayParamPosition = Vec(40.5, 130.5); + auto oddScaleParamPosition = Vec(120.5, 136.5); + auto evenScaleParamPosition = Vec(180.5, 136.5); + auto phaseParamPosition = Vec(170.5, 326.5); + + auto pitchInputPosition = Vec(13.0, 323.0); + auto syncInputPosition = Vec(53.0, 323.0); + + auto audioOutputPosition = Vec(263.0, 323.0); + // end generated by svg_widgets.rb + + addParam(ParamWidget::create<Knob38>(frequencyParamPosition, module, Additator::FREQUENCY_PARAM, -5.0, 5.0, 0.0)); + addParam(ParamWidget::create<Knob38>(partialsParamPosition, module, Additator::PARTIALS_PARAM, 1.0, module->maxPartials, module->maxPartials / 2.0f)); + addParam(ParamWidget::create<Knob38>(widthParamPosition, module, Additator::WIDTH_PARAM, 0.0, 2.0, 1.0)); + addParam(ParamWidget::create<Knob26>(evenWidthParamPosition, module, Additator::EVEN_WIDTH_PARAM, -0.99, 0.99, 0.0)); + addParam(ParamWidget::create<Knob38>(decayParamPosition, module, Additator::DECAY_PARAM, -1.0, 3.0, 1.0)); + addParam(ParamWidget::create<Knob26>(oddScaleParamPosition, module, Additator::ODD_SCALE_PARAM, 0.0, 1.0, 1.0)); + addParam(ParamWidget::create<Knob26>(evenScaleParamPosition, module, Additator::EVEN_SCALE_PARAM, 0.0, 1.0, 1.0)); + { + auto w = ParamWidget::create<Knob26>(phaseParamPosition, module, Additator::PHASE_PARAM, 0.0, 3.0, 0.0); + dynamic_cast<Knob*>(w)->snap = true; + addParam(w); + } + + addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, Additator::PITCH_INPUT)); + addInput(Port::create<Port24>(syncInputPosition, Port::INPUT, module, Additator::SYNC_INPUT)); + + addOutput(Port::create<Port24>(audioOutputPosition, Port::OUTPUT, module, Additator::AUDIO_OUTPUT)); + } +}; + +Model* modelAdditator = Model::create<Additator, AdditatorWidget>("Bogaudio", "Bogaudio-Additator", "Additator"); diff --git a/src/Additator.hpp b/src/Additator.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "bogaudio.hpp" +#include "dsp/oscillator.hpp" +#include "dsp/pitch.hpp" + +using namespace bogaudio::dsp; + +extern Model* modelAdditator; + +namespace bogaudio { + +struct Additator : Module { + enum ParamsIds { + FREQUENCY_PARAM, + PARTIALS_PARAM, + WIDTH_PARAM, + EVEN_WIDTH_PARAM, + DECAY_PARAM, + ODD_SCALE_PARAM, + EVEN_SCALE_PARAM, + PHASE_PARAM, + NUM_PARAMS + }; + + enum InputsIds { + PITCH_INPUT, + SYNC_INPUT, + NUM_INPUTS + }; + + enum OutputsIds { + AUDIO_OUTPUT, + NUM_OUTPUTS + }; + + enum LightsIds { + NUM_LIGHTS + }; + + const int maxPartials = 100; + int _partials = 0; + float _width = 0.0f; + float _evenWidth = 0.0f; + float _decay = 0.0f; + float _oddScale = 0.0f; + float _evenScale = 0.0f; + float _phase = 0.0f; + SineBankOscillator _oscillator; + SchmittTrigger _syncTrigger; + + Additator() + : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) + , _oscillator(engineGetSampleRate(), 1000.0, maxPartials) + { + onReset(); + } + + virtual void onReset() override; + virtual void onSampleRateChange() override; + virtual void step() override; +}; + +} // namespace bogaudio diff --git a/src/Test.hpp b/src/Test.hpp @@ -94,10 +94,10 @@ struct Test : Module { switch (5) { case 1: { // saw - float phase = M_PI; for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { - _sineBank.setPartial(i, i, baseAmplitude / (float)i, &phase); + _sineBank.setPartial(i, i, baseAmplitude / (float)i); } + _sineBank.syncToPhase(M_PI); break; } @@ -112,17 +112,16 @@ struct Test : Module { case 3: { // triangle if (false) { - float phase = M_PI / 2.0; for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { - _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)(i * i) : 0.0, &phase); + _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)(i * i) : 0.0); } + _sineBank.syncToPhase(M_PI / 2.0); } else { - float phase = 0.0f; - _sineBank.setPartial(1, 1.0f, baseAmplitude, &phase); + _sineBank.setPartial(1, 1.0f, baseAmplitude); for (int i = 2, n = _sineBank.partialCount(); i < n; ++i) { float k = 2*i - 1; - _sineBank.setPartial(i, k, powf(-1.0f, k) * baseAmplitude/(k * k), &phase); + _sineBank.setPartial(i, k, powf(-1.0f, k) * baseAmplitude/(k * k)); } } break; @@ -138,12 +137,11 @@ struct Test : Module { case 5: { // ? - float phase = 0.0; float factor = 0.717; float factor2 = factor; float multiple = 1.0; for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { - _sineBank.setPartial(i, multiple, baseAmplitude / multiple, &phase); + _sineBank.setPartial(i, multiple, baseAmplitude / multiple); multiple += i % 2 == 1 ? factor : factor2; } break; diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp @@ -1,6 +1,8 @@ #include "bogaudio.hpp" +#include "Additator.hpp" + #include "Shaper.hpp" #include "ShaperPlus.hpp" #include "DADSRH.hpp" @@ -33,6 +35,8 @@ void init(rack::Plugin *p) { p->website = "https://github.com/bogaudio/BogaudioModules"; p->manual = "https://github.com/bogaudio/BogaudioModules/blob/master/README.md"; + p->addModel(modelAdditator); + p->addModel(modelShaper); p->addModel(modelShaperPlus); p->addModel(modelDADSRH); diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp @@ -94,24 +94,29 @@ float TriangleOscillator::_next() { } -void SineBankOscillator::setPartial(int i, float frequencyRatio, float amplitude, float* phase) { - if (i > (int)_partials.size()) { - return; - } +void SineBankOscillator::setPartial(int i, float frequencyRatio, float amplitude) { + setPartialFrequencyRatio(i, frequencyRatio); + setPartialAmplitude(i, amplitude); +} - Partial& p = _partials[i - 1]; - if (amplitude > 0.01f || amplitude < -0.01f) { - p.enabled = true; +void SineBankOscillator::setPartialFrequencyRatio(int i, float frequencyRatio) { + if (i <= (int)_partials.size()) { + Partial& p = _partials[i - 1]; p.frequencyRatio = frequencyRatio; p.frequency = _frequency * frequencyRatio; p.sine.setFrequency((double)_frequency * (double)frequencyRatio); - p.amplitude = amplitude; - if (phase) { - p.sine.setPhase(*phase); - } } - else { - p.enabled = false; +} + +void SineBankOscillator::setPartialAmplitude(int i, float amplitude) { + if (i <= (int)_partials.size()) { + _partials[i - 1].amplitude = amplitude; + } +} + +void SineBankOscillator::syncToPhase(float phase) { + for (Partial& p : _partials) { + p.sine.setPhase(phase); } } @@ -132,7 +137,7 @@ void SineBankOscillator::_frequencyChanged() { float SineBankOscillator::_next() { float next = 0.0; for (Partial& p : _partials) { - if (p.enabled && p.frequency < _maxPartialFrequency) { + if (p.frequency < _maxPartialFrequency && (p.amplitude > 0.001 || p.amplitude < -0.001)) { next += p.sine.next() * p.amplitude; } else { diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -158,15 +158,13 @@ struct TriangleOscillator : Phasor { struct SineBankOscillator : OscillatorGenerator { struct Partial { - bool enabled; float frequency; float frequencyRatio; float amplitude; SineOscillator sine; Partial() - : enabled(false) - , frequency(0.0) + : frequency(0.0) , frequencyRatio(0.0) , amplitude(0.0) , sine(0.0, 0.0, 1.0) @@ -193,8 +191,12 @@ struct SineBankOscillator : OscillatorGenerator { return _partials.size(); } - // one-based index. - void setPartial(int i, float frequencyRatio, float amplitude, float* phase = NULL); + // one-based indexes. + void setPartial(int i, float frequencyRatio, float amplitude); + void setPartialFrequencyRatio(int i, float frequencyRatio); + void setPartialAmplitude(int i, float amplitude); + + void syncToPhase(float phase); virtual void _sampleRateChanged() override; virtual void _frequencyChanged() override;