BogaudioModules

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

commit 69c0033c128595e60d5cd81206ce9d41ebec2183
parent e39adffd1d38ab488d34d2b68346008517762758
Author: Matt Demanett <matt@demanett.net>
Date:   Thu, 22 Feb 2018 01:14:29 -0500

Additator: more work.

Diffstat:
Mres-src/Additator-src.svg | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mres/Additator.svg | 0
Msrc/Additator.cpp | 159++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/Additator.hpp | 44+++++++++++++++++++++++++++++++++++++-------
4 files changed, 243 insertions(+), 57 deletions(-)

diff --git a/res-src/Additator-src.svg b/res-src/Additator-src.svg @@ -52,6 +52,16 @@ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" /> </g> </symbol> + + <symbol id="button-small" viewBox="0 0 9px 9px"> + <g transform="translate(4.5 4.5)"> + <circle r="4" stroke-width="1" stroke="#00f" fill="#f00" /> + </g> + </symbol> + + <symbol id="light" viewBox="0 0 1.1px 1.1px"> + <rect width="3.2" height="3.2" fill="#0f0" /> + </symbol> </defs> <rect width="100%" height="100%" fill="#ddd" /> @@ -84,31 +94,88 @@ <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)"> + <g transform="translate(220 -20)"> <!-- <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)" /> + <use id="ODD_SKEW_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + </g> + <g transform="translate(220 20)"> + <!-- <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_SKEW_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 36) rotate(270)">GAIN</text> + <use id="GAIN_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + <g transform="translate(90 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)"> + <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 36) rotate(270)">ODDS</text> - <use id="ODD_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">BALANCE</text> + <use id="BALANCE_PARAM" xlink:href="#knob" transform="translate(20 0)" /> </g> - <g transform="translate(150 0)"> + <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 38) rotate(270)">EVENS</text> - <use id="EVEN_SCALE_PARAM" xlink:href="#knob-medium" transform="translate(20 6)" /> + <!-- <text font-size="8pt" letter-spacing="2px" transform="translate(8 50) rotate(270)">BALANCE</text> --> + <use id="FILTER_PARAM" xlink:href="#knob" transform="translate(20 0)" /> + </g> + </g> + + <g transform="translate(0 200)"> + <g transform="translate(10 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="PARTIALS_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="1px" transform="translate(9 36)">PS</text> + </g> + <g transform="translate(50 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="WIDTH_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">W</text> + </g> + <g transform="translate(90 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="ODD_SKEW_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(9 36)">OS</text> + </g> + <g transform="translate(130 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="EVEN_SKEW_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(9 36)">ES</text> + </g> + </g> + + <g transform="translate(0 260)"> + <g transform="translate(10 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="GAIN_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="1px" transform="translate(11.5 36)">G</text> + </g> + <g transform="translate(50 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="DECAY_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">D</text> + </g> + <g transform="translate(90 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="BALANCE_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">B</text> + </g> + <g transform="translate(130 0)"> + <rect width="30" height="40" rx="5" fill="#fafafa" /> + <use id="FILTER_INPUT" xlink:href="#input" transform="translate(3 3)" /> + <text font-size="6pt" letter-spacing="2px" transform="translate(11.5 36)">F</text> </g> </g> + <!-- <polyline points="0,0 300,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 339)" /> --> <g transform="translate(0 320)"> <g transform="translate(10 0)"> <rect width="30" height="40" rx="5" fill="#fafafa" /> @@ -120,10 +187,16 @@ <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 transform="translate(111 0)"> + <g transform="translate(0 17.5)"> + <use id="SINE_LIGHT" xlink:href="#light" transform="translate(0 0)" /> + <text font-size="7pt" letter-spacing="2px" transform="translate(7 5)">SIN</text> + </g> + <g transform="translate(32 17.5)"> + <use id="COSINE_LIGHT" xlink:href="#light" transform="translate(0 0)" /> + <text font-size="7pt" letter-spacing="2px" transform="translate(7 5)">COS</text> + </g> + <use id="PHASE_PARAM" xlink:href="#button-small" transform="translate(68 14.5)" /> </g> <g transform="translate(260 0)"> <rect width="30" height="40" rx="5" fill="#bbb" /> diff --git a/res/Additator.svg b/res/Additator.svg Binary files differ. diff --git a/src/Additator.cpp b/src/Additator.cpp @@ -3,53 +3,93 @@ void Additator::onReset() { _syncTrigger.reset(); - _phase = -10.0f; // trigger a phase sync. + _phase = PHASE_RESET; } void Additator::onSampleRateChange() { _oscillator.setSampleRate(engineGetSampleRate()); - _phase = -10.0f; // trigger a phase sync. + _phase = PHASE_RESET; +} + +float Additator::cvValue(Input& cv) { + if (!cv.active) { + return 0.0f; + } + return clamp(cv.value / 5.0f, -1.0f, 1.0f); } void Additator::step() { - float width = params[WIDTH_PARAM].value; - float evenWidth = params[EVEN_WIDTH_PARAM].value; - if (_width != width || evenWidth != _evenWidth) { + lights[SINE_LIGHT].value = _phase == PHASE_SINE; + lights[COSINE_LIGHT].value = _phase == PHASE_COSINE; + if (!outputs[AUDIO_OUTPUT].active) { + return; + } + + float width = clamp(params[WIDTH_PARAM].value + (maxWidth / 2.0f) * cvValue(inputs[WIDTH_INPUT]), 0.0f, maxWidth); + float oddSkew = clamp(params[ODD_SKEW_PARAM].value + cvValue(inputs[ODD_SKEW_INPUT]), -maxSkew, maxSkew); + float evenSkew = clamp(params[EVEN_SKEW_PARAM].value + cvValue(inputs[EVEN_SKEW_INPUT]), -maxSkew, maxSkew); + if ( + _width != width || + _oddSkew != oddSkew || + _evenSkew != evenSkew + ) { _width = width; - _evenWidth = evenWidth; + _oddSkew = oddSkew; + _evenSkew = evenSkew; 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; + ii += _evenSkew; + } + else { + ii += _oddSkew; } _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) { + int partials = clamp((int)roundf(params[PARTIALS_PARAM].value + (maxPartials / 2.0f) * cvValue(inputs[PARTIALS_INPUT])), 0, maxPartials); + float amplitudeNormalization = clamp(params[GAIN_PARAM].value + ((maxAmplitudeNormalization - minAmplitudeNormalization) / 2.0f) * cvValue(inputs[GAIN_INPUT]), minAmplitudeNormalization, maxAmplitudeNormalization); + float decay = clamp(params[DECAY_PARAM].value + ((maxDecay - minDecay) / 2.0f) * cvValue(inputs[DECAY_INPUT]), minDecay, maxDecay); + float balance = clamp(params[BALANCE_PARAM].value + cvValue(inputs[BALANCE_INPUT]), -1.0f, 1.0f); + float filter = clamp(params[FILTER_PARAM].value + cvValue(inputs[FILTER_INPUT]), minFilter, maxFilter); + if ( + _partials != partials || + _amplitudeNormalization != amplitudeNormalization || + _decay != decay || + _balance != balance || + _filter != filter + ) { _partials = partials; + _amplitudeNormalization = amplitudeNormalization; _decay = decay; - _oddScale = oddScale; - _evenScale = evenScale; + _balance = balance; + _filter = filter; float as[maxPartials + 1]; float total = as[1] = 1.0f; + filter = log10f(_filter) + 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; + as[i] = powf(i, -_decay) * powf(_filter, i); + if (i % 2 == 0) { + if (_balance > 0.0f) { + as[i] *= 1.0f - _balance; + } + } + else { + if (_balance < 0.0f) { + as[i] *= 1.0f + _balance; + } + } total += as[i]; } } - total /= 3.0f; // pretty arbitrary, this. + total /= _amplitudeNormalization; for (int i = 1, n = _oscillator.partialCount(); i <= n; ++i) { as[i] /= total; _oscillator.setPartialAmplitude(i, as[i]); @@ -64,12 +104,12 @@ void Additator::step() { _oscillator.setFrequency(frequency); if (_syncTrigger.process(inputs[SYNC_INPUT].value)) { - _oscillator.syncToPhase(_phase * M_PI / 2.0f); + _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); } - float phase = params[PHASE_PARAM].value; + Phase phase = ((int)params[PHASE_PARAM].value) == 2 ? PHASE_COSINE : PHASE_SINE; if (_phase != phase) { _phase = phase; - _oscillator.syncToPhase(phase * M_PI / 2.0f); + _oscillator.syncToPhase(_phase == PHASE_SINE ? 0.0f : M_PI / 2.0f); } outputs[AUDIO_OUTPUT].value = _oscillator.next() * 5.0; @@ -95,35 +135,78 @@ struct AdditatorWidget : ModuleWidget { 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 oddSkewParamPosition = Vec(250.5, 36.5); + auto evenSkewParamPosition = Vec(250.5, 76.5); + auto gainParamPosition = Vec(40.5, 130.5); + auto decayParamPosition = Vec(120.5, 130.5); + auto balanceParamPosition = Vec(200.5, 130.5); + auto filterParamPosition = Vec(250.5, 130.5); + auto phaseParamPosition = Vec(179.0, 334.5); + + auto partialsInputPosition = Vec(13.0, 203.0); + auto widthInputPosition = Vec(53.0, 203.0); + auto oddSkewInputPosition = Vec(93.0, 203.0); + auto evenSkewInputPosition = Vec(133.0, 203.0); + auto gainInputPosition = Vec(13.0, 263.0); + auto decayInputPosition = Vec(53.0, 263.0); + auto balanceInputPosition = Vec(93.0, 263.0); + auto filterInputPosition = Vec(133.0, 263.0); auto pitchInputPosition = Vec(13.0, 323.0); auto syncInputPosition = Vec(53.0, 323.0); auto audioOutputPosition = Vec(263.0, 323.0); + + auto sineLightPosition = Vec(111.0, 337.5); + auto cosineLightPosition = Vec(143.0, 337.5); // 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); - } - + addParam(ParamWidget::create<Knob38>(partialsParamPosition, module, Additator::PARTIALS_PARAM, 1.0, module->maxPartials, module->maxPartials / 10.0f)); + addParam(ParamWidget::create<Knob38>(widthParamPosition, module, Additator::WIDTH_PARAM, 0.0, module->maxWidth, module->maxWidth / 2.0f)); + addParam(ParamWidget::create<Knob26>(oddSkewParamPosition, module, Additator::ODD_SKEW_PARAM, -module->maxSkew, module->maxSkew, 0.0)); + addParam(ParamWidget::create<Knob26>(evenSkewParamPosition, module, Additator::EVEN_SKEW_PARAM, -module->maxSkew, module->maxSkew, 0.0)); + addParam(ParamWidget::create<Knob38>( + gainParamPosition, + module, + Additator::GAIN_PARAM, + module->minAmplitudeNormalization, + module->maxAmplitudeNormalization, + (module->maxAmplitudeNormalization - module->minAmplitudeNormalization) / 2.0f + module->minAmplitudeNormalization + )); + addParam(ParamWidget::create<Knob38>( + decayParamPosition, + module, + Additator::DECAY_PARAM, + module->minDecay, + module->maxDecay, + (module->maxDecay - module->minDecay) / 2.0f + module->minDecay + )); + addParam(ParamWidget::create<Knob26>(balanceParamPosition, module, Additator::BALANCE_PARAM, -1.0, 1.0, 0.0)); + addParam(ParamWidget::create<Knob26>( + filterParamPosition, + module, + Additator::FILTER_PARAM, + module->minFilter, + module->maxFilter, + (module->maxFilter - module->minFilter) / 2.0f + module->minFilter + )); + addParam(ParamWidget::create<StatefulButton9>(phaseParamPosition, module, Additator::PHASE_PARAM, 1.0, 2.0, 1.0)); + + addInput(Port::create<Port24>(partialsInputPosition, Port::INPUT, module, Additator::PARTIALS_INPUT)); + addInput(Port::create<Port24>(widthInputPosition, Port::INPUT, module, Additator::WIDTH_INPUT)); + addInput(Port::create<Port24>(oddSkewInputPosition, Port::INPUT, module, Additator::ODD_SKEW_INPUT)); + addInput(Port::create<Port24>(evenSkewInputPosition, Port::INPUT, module, Additator::EVEN_SKEW_INPUT)); + addInput(Port::create<Port24>(gainInputPosition, Port::INPUT, module, Additator::GAIN_INPUT)); + addInput(Port::create<Port24>(decayInputPosition, Port::INPUT, module, Additator::DECAY_INPUT)); + addInput(Port::create<Port24>(balanceInputPosition, Port::INPUT, module, Additator::BALANCE_INPUT)); + addInput(Port::create<Port24>(filterInputPosition, Port::INPUT, module, Additator::FILTER_INPUT)); 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)); + + addChild(ModuleLightWidget::create<TinyLight<GreenLight>>(sineLightPosition, module, Additator::SINE_LIGHT)); + addChild(ModuleLightWidget::create<TinyLight<GreenLight>>(cosineLightPosition, module, Additator::COSINE_LIGHT)); } }; diff --git a/src/Additator.hpp b/src/Additator.hpp @@ -15,10 +15,12 @@ struct Additator : Module { FREQUENCY_PARAM, PARTIALS_PARAM, WIDTH_PARAM, - EVEN_WIDTH_PARAM, + ODD_SKEW_PARAM, + EVEN_SKEW_PARAM, + GAIN_PARAM, DECAY_PARAM, - ODD_SCALE_PARAM, - EVEN_SCALE_PARAM, + BALANCE_PARAM, + FILTER_PARAM, PHASE_PARAM, NUM_PARAMS }; @@ -26,6 +28,14 @@ struct Additator : Module { enum InputsIds { PITCH_INPUT, SYNC_INPUT, + PARTIALS_INPUT, + WIDTH_INPUT, + ODD_SKEW_INPUT, + EVEN_SKEW_INPUT, + GAIN_INPUT, + DECAY_INPUT, + BALANCE_INPUT, + FILTER_INPUT, NUM_INPUTS }; @@ -35,17 +45,36 @@ struct Additator : Module { }; enum LightsIds { + SINE_LIGHT, + COSINE_LIGHT, NUM_LIGHTS }; + enum Phase { + PHASE_RESET, + PHASE_SINE, + 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; + int _partials = 0; float _width = 0.0f; - float _evenWidth = 0.0f; + float _oddSkew = 0.0f; + float _evenSkew = 0.0f; + float _amplitudeNormalization = 0.0f; float _decay = 0.0f; - float _oddScale = 0.0f; - float _evenScale = 0.0f; - float _phase = 0.0f; + float _balance = 0.0f; + float _filter = 0.0f; + Phase _phase = PHASE_RESET; SineBankOscillator _oscillator; SchmittTrigger _syncTrigger; @@ -58,6 +87,7 @@ struct Additator : Module { virtual void onReset() override; virtual void onSampleRateChange() override; + float cvValue(Input& cv); virtual void step() override; };