BogaudioModules

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

commit be39f3b287750f187a13cbd1c8b8aca7fda94bf3
parent 4b37577c4a451c41c5784b2d6c2b1ba3368bd94a
Author: Matt Demanett <matt@demanett.net>
Date:   Tue, 17 Dec 2019 19:06:44 -0500

EDGE: edge detector, gate-to-trigger, etc. #90

Diffstat:
Mplugin.json | 10++++++++++
Ares-src/Edge-src.svg | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares/Edge.svg | 0
Asrc/Edge.cpp | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Edge.hpp | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/bogaudio.cpp | 2++
Msrc/dsp/signal.cpp | 2+-
Msrc/param_quantities.hpp | 2++
8 files changed, 441 insertions(+), 1 deletion(-)

diff --git a/plugin.json b/plugin.json @@ -178,6 +178,16 @@ ] }, { + "slug": "Bogaudio-Edge", + "name": "EDGE", + "description": "Edge detector, gate-to-trigger, comparator", + "tags": [ + "Logic", + "Utility", + "Polyphonic" + ] + }, + { "slug": "Bogaudio-Noise", "name": "NOISE", "description": "Noise source with multiple flavors & absolute value", diff --git a/res-src/Edge-src.svg b/res-src/Edge-src.svg @@ -0,0 +1,216 @@ +<svg + version="1.1" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + width="45" + height="380" + viewBox="0 0 45 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 45px 45px"> + <g transform="translate(22.5 22.5)"> + <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="14" stroke-width="1" stroke="#00f" fill="none" /> + </g> + </symbol> + + <symbol id="knobguide" viewBox="0 0 45px 45px"> + <g transform="translate(22.5 22.5)"> + <g transform="rotate(-240) translate(17 0)"> + <polyline points="0,0 4,0" stroke-width="1.5" stroke="#333" /> + </g> + <g transform="rotate(-225) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-210) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-195) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-180) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-165) translate(17 0)"> + <polyline points="0,0 4,0" stroke-width="1.5" stroke="#333" /> + </g> + <g transform="rotate(-150) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-135) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-120) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-105) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + + <g transform="rotate(-90) translate(17 0)"> + <!-- <polyline points="0,0 4,0" stroke-width="0.5" stroke="#333" /> --> + <g transform="translate(2.4 0) rotate(90)"> + <text font-size="5pt" transform="translate(-1.9 2)">0</text> + </g> + </g> + + <g transform="rotate(-75) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-60) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-45) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-30) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(-15) translate(17 0)"> + <polyline points="0,0 4,0" stroke-width="1.5" stroke="#333" /> + </g> + <g transform="rotate(0) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(15) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(30) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(45) translate(17 0)"> + <polyline points="0,0 3,0" stroke-width="0.3" stroke="#333" /> + </g> + <g transform="rotate(60) translate(17 0)"> + <polyline points="0,0 4,0" stroke-width="1.5" stroke="#333" /> + </g> + + <g transform="rotate(-225) translate(22 0)"> + <!-- <polyline points="-10,0 10,0" stroke-width="0.5" stroke="#0f0" /> --> + <g transform="translate(2.5 0) rotate(225)"> + <text font-size="5pt" transform="translate(-3 7)">-10</text> + </g> + </g> + <g transform="rotate(45) translate(22 0)"> + <!-- <polyline points="-10,0 10,0" stroke-width="0.5" stroke="#0f0" /> --> + <g transform="translate(2.5 0) rotate(-45)"> + <text font-size="5pt" transform="translate(-7 7)">10</text> + </g> + </g> + </g> + </symbol> + + <symbol id="knob-smallest" viewBox="0 0 16px 16px"> + <g transform="translate(8 8)"> + <polyline points="-3,0 3,0" stroke-width="1" stroke="#00f" /> + <polyline points="0,-3 0,3" stroke-width="1" stroke="#00f" /> + <circle r="7.5" stroke-width="1" stroke="#00f" fill="none" /> + </g> + </symbol> + + <symbol id="knobguide-hold" viewBox="0 0 40px 40px"> + <g transform="translate(20 20)"> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-240) translate(10 0)" /> + <text font-size="5pt" transform="rotate(-240) translate(15 0) rotate(240) translate(-3.5 2)">0</text> + <polyline points="0,0 1.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-172.92) translate(10 0)" /> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-145.13) translate(10 0)" /> + <text font-size="5.0pt" transform="rotate(-145.13) translate(18 0) rotate(145.13) translate(-2 2.2)">0.1</text> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-90) translate(10 0)" /> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-27.87) translate(10 0)" /> + <text font-size="5.0pt" transform="rotate(-27.87) translate(15 0) rotate(27.87) translate(-5 0)">0.5</text> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(19.8) translate(10 0)" /> + <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(60) translate(10 0)" /> + <text font-size="5pt" letter-spacing="1" transform="rotate(60) translate(15 0) rotate(-60) translate(-0.5 2)">1s</text> + </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> + + <symbol id="light" viewBox="0 0 6.4px 6.4px"> + <rect width="6.4" height="6.4" fill="#0f0" /> + </symbol> + </defs> + + <rect width="100%" height="100%" fill="#ddd" /> + <polyline points="1,1 44,1 44,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" /> + <polyline points="0.5,0.5 44.5,0.5 44.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" /> + <polyline points="0,0 45,0 45,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" /> + + <g transform="rotate(-90) translate(-376 13)"> + <text class="title" font-size="7pt" letter-spacing="2.5px">EDGE</text> + <g transform="translate(0 12)"> + <text class="brand" font-size="7pt" letter-spacing="2px">BGA</text> + <rect width="3.0" height="3" fill="#ddd" transform="translate(11.5 -5)" /> + </g> + </g> + + <g transform="translate(0 25)"> + <text font-size="6pt" letter-spacing="2px" transform="translate(11 0)">RISE</text> + <use id="RISE_PARAM" xlink:href="#knob" transform="translate(0 5)" /> + <use xlink:href="#knobguide" transform="scale(1) translate(0 5)" /> + <!-- <rect width="45" height="6" fill="#0f0" transform="translate(0 52)" /> --> + </g> + + <g transform="translate(0 89)"> + <text font-size="6pt" letter-spacing="2px" transform="translate(10.5 0)">FALL</text> + <use id="FALL_PARAM" xlink:href="#knob" transform="translate(0 5)" /> + <use xlink:href="#knobguide" transform="scale(1) translate(0 5)" /> + <!-- <rect width="45" height="6" fill="#0f0" transform="translate(0 52)" /> --> + </g> + + <g transform="translate(0 153)"> + <text font-size="6pt" letter-spacing="2px" transform="translate(10.5 0)">HOLD</text> + <use id="HOLD_PARAM" xlink:href="#knob-smallest" transform="translate(14.5 9)" /> + <use xlink:href="#knobguide-hold" transform="scale(1) translate(2.5 -3)" /> + <!-- <rect width="45" height="6" fill="#0f0" transform="translate(0 32)" /> --> + </g> + + <g transform="translate(0 191)"> + <g transform="translate(5.5 0)"> + <rect width="34" height="38" rx="5" fill="#fafafa" /> + <rect width="34" height="10" fill="#fafafa" transform="translate(0 28)" /> + <use id="IN_INPUT" xlink:href="#input" transform="translate(5 3)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(12.5 35)">IN</text> + </g> + <g transform="translate(5.5 38)"> + <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" /> + <rect width="34" height="109" rx="5" fill="#bbb" /> + <use id="HIGH_OUTPUT" xlink:href="#output" transform="translate(5 3)" /> + <text font-size="5pt" letter-spacing="1.2px" transform="translate(11 35)">HIGH</text> + <use id="HIGH_LIGHT" xlink:href="#light" transform="translate(2 29.3)" /> + <use id="RISE_OUTPUT" xlink:href="#output" transform="translate(5 38)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(6.9 70)">RISE</text> + <use id="FALL_OUTPUT" xlink:href="#output" transform="translate(5 73)" /> + <text font-size="5pt" letter-spacing="2px" transform="translate(6.6 105)">FALL</text> + </g> + </g> +</svg> diff --git a/res/Edge.svg b/res/Edge.svg Binary files differ. diff --git a/src/Edge.cpp b/src/Edge.cpp @@ -0,0 +1,140 @@ + +#include "Edge.hpp" + +void Edge::reset() { + for (int c = 0; c < _channels; ++c) { + _state[c] = LOW_STATE; + _timer[c].reset(); + _riseOutputPulseGen[c].process(10.0f); + _fallOutputPulseGen[c].process(10.0f); + } +} + +int Edge::channels() { + return inputs[IN_INPUT].getChannels(); +} + +void Edge::addChannel(int c) { + _state[c] = LOW_STATE; +} + +void Edge::modulate() { + _riseThreshold = params[RISE_PARAM].getValue() * 10.0f; + _fallThreshold = params[FALL_PARAM].getValue() * 10.0f; + _holdSeconds = params[HOLD_PARAM].getValue(); + _holdSeconds *= _holdSeconds; +} + +void Edge::processAll(const ProcessArgs& args) { + _highLightSum = 0; + + outputs[HIGH_OUTPUT].setChannels(_channels); + outputs[RISE_OUTPUT].setChannels(_channels); + outputs[FALL_OUTPUT].setChannels(_channels); +} + +void Edge::processChannel(const ProcessArgs& args, int c) { + static int i = 0; + ++i; + float in = inputs[IN_INPUT].getPolyVoltage(c); + + switch (_state[c]) { + case LOW_STATE: { + if (in >= _riseThreshold) { + _state[c] = HIGH_STATE; + _timer[c].reset(); + _timer[c].setParams(APP->engine->getSampleRate(), _holdSeconds); + _riseOutputPulseGen[c].trigger(0.001f); + } + break; + } + + case HIGH_STATE: { + bool expired = !_timer[c].next(); + ++_highLightSum; + + if (_fallThreshold > _riseThreshold && in > _fallThreshold) { + _state[c] = HIGH2_STATE; + } + else if (in < std::min(_riseThreshold, _fallThreshold) && expired) { + _state[c] = _riseThreshold > _fallThreshold ? LOW_STATE : LOW2_STATE; + _fallOutputPulseGen[c].trigger(0.001f); + } + break; + } + + case HIGH2_STATE: { + bool expired = !_timer[c].next(); + ++_highLightSum; + + if (in < std::max(_riseThreshold, _fallThreshold) && expired) { + _state[c] = _riseThreshold > _fallThreshold ? LOW_STATE : LOW2_STATE; + _fallOutputPulseGen[c].trigger(0.001f); + } + break; + } + + case LOW2_STATE: { + if (in < std::min(_riseThreshold, _fallThreshold)) { + _state[c] = LOW_STATE; + } + break; + } + } + + outputs[HIGH_OUTPUT].setVoltage((_state[c] == HIGH_STATE || _state[c] == HIGH2_STATE) * 5.0f, c); + float st = APP->engine->getSampleTime(); + outputs[RISE_OUTPUT].setVoltage(_riseOutputPulseGen[c].process(st) * 5.0f, c); + outputs[FALL_OUTPUT].setVoltage(_fallOutputPulseGen[c].process(st) * 5.0f, c); +} + +void Edge::postProcess(const ProcessArgs& args) { + lights[HIGH_LIGHT].value = _highLightSum / (float)_channels; +} + +struct EdgeWidget : ModuleWidget { + static constexpr int hp = 3; + + EdgeWidget(Edge* module) { + setModule(module); + box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); + + { + SvgPanel *panel = new SvgPanel(); + panel->box.size = box.size; + panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Edge.svg"))); + addChild(panel); + } + + addChild(createWidget<ScrewSilver>(Vec(0, 0))); + addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365))); + + // generated by svg_widgets.rb + auto riseParamPosition = Vec(8.0, 38.0); + auto fallParamPosition = Vec(8.0, 102.0); + auto holdParamPosition = Vec(14.5, 162.0); + + auto inInputPosition = Vec(10.5, 194.0); + + auto highOutputPosition = Vec(10.5, 232.0); + auto riseOutputPosition = Vec(10.5, 267.0); + auto fallOutputPosition = Vec(10.5, 302.0); + + auto highLightPosition = Vec(7.5, 258.3); + // end generated by svg_widgets.rb + + addParam(createParam<Knob29>(riseParamPosition, module, Edge::RISE_PARAM)); + addParam(createParam<Knob29>(fallParamPosition, module, Edge::FALL_PARAM)); + addParam(createParam<Knob16>(holdParamPosition, module, Edge::HOLD_PARAM)); + + addInput(createInput<Port24>(inInputPosition, module, Edge::IN_INPUT)); + + addOutput(createOutput<Port24>(highOutputPosition, module, Edge::HIGH_OUTPUT)); + addOutput(createOutput<Port24>(riseOutputPosition, module, Edge::RISE_OUTPUT)); + addOutput(createOutput<Port24>(fallOutputPosition, module, Edge::FALL_OUTPUT)); + + addChild(createLight<SmallLight<GreenLight>>(highLightPosition, module, Edge::HIGH_LIGHT)); + } +}; + +Model* modelEdge = createModel<Edge, EdgeWidget>("Bogaudio-Edge", "EDGE", "Edge detector, gate-to-trigger, comparator", "Logic", "Utility", "Polyphonic"); diff --git a/src/Edge.hpp b/src/Edge.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "bogaudio.hpp" +#include "signal.hpp" + +using namespace bogaudio::dsp; + +extern Model* modelEdge; + +namespace bogaudio { + +struct Edge : BGModule { + enum ParamsIds { + RISE_PARAM, + FALL_PARAM, + HOLD_PARAM, + NUM_PARAMS + }; + + enum InputsIds { + IN_INPUT, + NUM_INPUTS + }; + + enum OutputsIds { + HIGH_OUTPUT, + RISE_OUTPUT, + FALL_OUTPUT, + NUM_OUTPUTS + }; + + enum LightsIds { + HIGH_LIGHT, + NUM_LIGHTS + }; + + enum State { + LOW_STATE, + LOW2_STATE, + HIGH_STATE, + HIGH2_STATE + }; + + float _riseThreshold = 0.0f; + float _fallThreshold = 0.0f; + float _holdSeconds = 0.0f; + State _state[maxChannels] {}; + Timer _timer[maxChannels]; + rack::dsp::PulseGenerator _riseOutputPulseGen[maxChannels]; + rack::dsp::PulseGenerator _fallOutputPulseGen[maxChannels]; + int _highLightSum = 0; + + Edge() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + configParam(RISE_PARAM, -1.0f, 1.0f, 0.1f, "Rising threshold", " V", 0.0f, 10.0f); + configParam(FALL_PARAM, -1.0f, 1.0f, 0.01f, "Falling threshold", " V", 0.0f, 10.0f); + configParam<ScaledSquaringParamQuantity<1000>>(HOLD_PARAM, 0.0f, 1.0f, 0.031623f, "Hold/reset time", " ms"); + reset(); + } + + void reset() override; + int channels() override; + void addChannel(int c) override; + void modulate() override; + void processAll(const ProcessArgs& args) override; + void processChannel(const ProcessArgs& args, int c) override; + void postProcess(const ProcessArgs& args) override; +}; + +} // namespace bogaudio diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp @@ -20,6 +20,7 @@ #include "DADSRHPlus.hpp" #include "DGate.hpp" #include "Detune.hpp" +#include "Edge.hpp" #include "EightFO.hpp" #include "EightOne.hpp" #include "FMOp.hpp" @@ -101,6 +102,7 @@ void init(rack::Plugin *p) { p->addModel(modelASR); p->addModel(modelADSR); p->addModel(modelFollow); + p->addModel(modelEdge); p->addModel(modelNoise); p->addModel(modelSampleHold); diff --git a/src/dsp/signal.cpp b/src/dsp/signal.cpp @@ -499,7 +499,7 @@ float NoiseGate::compressionDb(float detectorDb, float thresholdDb, float ratio, void Timer::setParams(float sampleRate, float time) { assert(sampleRate > 0.0f); - assert(time > 0.0f); + assert(time >= 0.0f); // FIXME: if the timer is running, should set the duration to reflect the time elapsed so far, adjusting it for the delta samplerate. _durationSteps = time * sampleRate; } diff --git a/src/param_quantities.hpp b/src/param_quantities.hpp @@ -40,6 +40,8 @@ struct ScaledSquaringParamQuantity : ParamQuantity { } }; +typedef ScaledSquaringParamQuantity<1> OneXSquaringParamQuantity; + typedef ScaledSquaringParamQuantity<10> TenXSquaringParamQuantity; typedef TenXSquaringParamQuantity EnvelopeSegmentParamQuantity;