BogaudioModules

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

commit 78a897e596ecb995acfc2b3e6ca33b042efb46a4
parent 82c2803bc29d1bf9778bdb4c769c37d5610791de
Author: Matt Demanett <matt@demanett.net>
Date:   Wed, 12 Aug 2020 01:17:48 -0400

RGATE: clock-relative gate generator and clock divider/muliplier.

Diffstat:
Mplugin.json | 10++++++++++
Ares-pp/RGate-dark-pp.svg | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares-pp/RGate-lowcontrast-pp.svg | 273+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares-pp/RGate-pp.svg | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares-src/RGate-src.svg | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ares/RGate-dark.svg | 0
Ares/RGate-lowcontrast.svg | 0
Ares/RGate.svg | 0
Asrc/RGate.cpp | 333+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/RGate.hpp | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/bogaudio.cpp | 2++
Msrc/param_quantities.hpp | 19+++++++++++++++++++
12 files changed, 1289 insertions(+), 0 deletions(-)

diff --git a/plugin.json b/plugin.json @@ -308,6 +308,16 @@ ] }, { + "slug": "Bogaudio-RGate", + "name": "RGATE", + "description": "Clock-relative gate generator & clock divider/multiplier", + "manualUrl": "https://github.com/bogaudio/BogaudioModules/blob/master/README.md#rgate", + "tags": [ + "Clock modulator", + "Polyphonic" + ] + }, + { "slug": "Bogaudio-Edge", "name": "EDGE", "description": "Edge detector, gate-to-trigger, comparator", diff --git a/res-pp/RGate-dark-pp.svg b/res-pp/RGate-dark-pp.svg @@ -0,0 +1,273 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="75.0" height="380.0" viewBox="0 0 75.0 380.0"> + <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; +} + +polyline { + stroke: #333; +} +path { + stroke: #333; +} + +rect.module-background, .background-fill { + fill: #ddd; +} +polyline.module-border-inner { + stroke: #e4e4e4; +} +polyline.module-border-middle { + stroke: #ebebeb; +} +polyline.module-border-outer { + stroke: #f2f2f2; +} + +g.io-group { +} +rect.input-background, rect.input-background-filler { + fill: #fafafa; +} +rect.output-background, rect.output-background-filler { + fill: #bbb; +} +text.input-label, text.output-label { + /* font-size: 6pt; */ +} +polyline.input-label, polyline.output-label { +} +path.input-label, path.output-label { +} + +circle.port-rim { + stroke: #f0f0f0; +} +circle.port-barrel { + stroke: #222; + fill: #222; +} +circle.knob-center { + fill: #eee; +} +circle.knob-rim { + fill: #333; +} +circle.knob-tick { + fill: #fff; +} +polyline.knob-tick { + stroke: #fff; +} + + +text { + fill: #eee; +} + +polyline { + stroke: #ccc; +} +path { + stroke: #ccc; +} + +rect.module-background, .background-fill { + fill: #111; +} +polyline.module-border-inner { + stroke: #191919; +} +polyline.module-border-middle { + stroke: #111; +} +polyline.module-border-outer { + stroke: #000; +} + +rect.input-background, rect.input-background-filler { + fill: #aaa; +} +rect.output-background, rect.output-background-filler { + fill: #666; +} +text.input-label { + fill: #222; +} +polyline.input-label, path.input-label { + stroke: #222; +} +text.output-label { + fill: #ddd; +} +polyline.output-label, path.output-label { + stroke: #ddd; +} + +circle.port-rim { + stroke: #c0c0c0; +} +circle.port-barrel { + stroke: #222; + fill: #222; +} +circle.knob-center { + fill: #888; +} +circle.knob-rim { + fill: #444; +} +circle.knob-tick { + fill: #fff; +} +polyline.knob-tick { + stroke: #fff; +} +</style> + + <defs> + <symbol id="dial-length-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" transform="rotate(-240) translate(26 0) rotate(240) translate(-2 3)">0</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-210) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-180) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-150) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-120) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(-90) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-60) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-30) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(0) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(30) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(60) translate(22 0)"/> + </g> + </symbol> + + <symbol id="dial-clock-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" text-anchor="middle" transform="rotate(-240) translate(20 0) rotate(240.0) translate(0 3)">1</text> + <text font-size="7pt" text-anchor="middle" transform="rotate(-202.20355269907728) translate(19 0) rotate(202.20355269907728) translate(0 3)">2</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-174.5346329292023) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-140.0) translate(20 0) rotate(140.0) translate(0 3)">8</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-114.64336589439827) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-93.61498905772001) translate(20 0) rotate(93.61498905772001) translate(0 3)">16</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-58.73460656500686) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-29.558287676339546) translate(21 0) rotate(29.558287676339546) translate(0 3)">32</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(19.119387817386496) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(60) translate(20 0) rotate(-60) translate(0 3)">64</text> + </g> + </symbol> + </defs> + + + + <rect class="module-background background-fill" width="75.0" height="380.0"/> + + + <polyline class="module-border-inner" points="1,1 74.0,1 74.0,379.0 1,379.0 1,1" stroke-width="0.5" fill="none"/> + <polyline class="module-border-middle" points="0.5,0.5 74.5,0.5 74.5,379.5 0.5,379.5 0.5,0.5" stroke-width="0.8" fill="none"/> + <polyline class="module-border-outer" points="0,0 75.0,0 75.0,380.0 0,380.0 0,0" stroke-width="1" fill="none"/> + + <text class="title" font-size="9pt" letter-spacing="3px" text-anchor="end" transform="translate(72.0 17)">RGATE</text> + <g transform="translate(25 374)"> + <text class="brand" font-size="6.5pt" letter-spacing="2px">BGA</text> + <rect class="background-fill" width="2" height="2" fill="#ddd" transform="translate(11.5 -4)"/> + </g> + + + <g transform="translate(0 35)"> + <text font-size="8pt" letter-spacing="1.5px" text-anchor="middle" transform="translate(37.5 0)">LENGTH</text> + <g transform="translate(18.5 13)"><svg id="LENGTH_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-length-rgate" transform="translate(0 -5.5)" href="#dial-length-rgate"/> + </g> + + <g transform="translate(0 114)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK DIV</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_DIVIDE_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 185)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK MUL</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_MULTIPLY_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 248)"> + <rect class="input-background" width="64" height="112" rx="5" transform="translate(5.5 0)"/> + <g transform="translate(10.5 3)"><svg id="LENGTH_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 35)">LEN</text> + <g transform="translate(40.5 3)"><svg id="CLOCK_DIVIDE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text> + <g transform="translate(10.5 40)"><svg id="RUN_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text> + <g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 72)">MUL</text> + <g transform="translate(10.5 77)"><svg id="CLOCK_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="1px" text-anchor="middle" transform="translate(22.5 109)">CLOCK</text> + <g class="io-group" transform="translate(30 72)"> + <rect class="output-background" width="32" height="38" rx="5" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="32" height="10" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="10" height="10" transform="translate(7.5 30)"/> + <g transform="translate(10.5 5)"><svg id="GATE_OUTPUT"> + <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> + </svg></g> + <text class="output-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 37)">GATE</text> + </g> + </g> +</svg> diff --git a/res-pp/RGate-lowcontrast-pp.svg b/res-pp/RGate-lowcontrast-pp.svg @@ -0,0 +1,273 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="75.0" height="380.0" viewBox="0 0 75.0 380.0"> + <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; +} + +polyline { + stroke: #333; +} +path { + stroke: #333; +} + +rect.module-background, .background-fill { + fill: #ddd; +} +polyline.module-border-inner { + stroke: #e4e4e4; +} +polyline.module-border-middle { + stroke: #ebebeb; +} +polyline.module-border-outer { + stroke: #f2f2f2; +} + +g.io-group { +} +rect.input-background, rect.input-background-filler { + fill: #fafafa; +} +rect.output-background, rect.output-background-filler { + fill: #bbb; +} +text.input-label, text.output-label { + /* font-size: 6pt; */ +} +polyline.input-label, polyline.output-label { +} +path.input-label, path.output-label { +} + +circle.port-rim { + stroke: #f0f0f0; +} +circle.port-barrel { + stroke: #222; + fill: #222; +} +circle.knob-center { + fill: #eee; +} +circle.knob-rim { + fill: #333; +} +circle.knob-tick { + fill: #fff; +} +polyline.knob-tick { + stroke: #fff; +} + + +text { + fill: #b3b3b3; +} + +polyline { + stroke: #b3b3b3; +} +path { + stroke: #b3b3b3; +} + +rect.module-background, .background-fill { + fill: #333; +} +polyline.module-border-inner { + stroke: #191919; +} +polyline.module-border-middle { + stroke: #111; +} +polyline.module-border-outer { + stroke: #000; +} + +rect.input-background, rect.input-background-filler { + fill: #b3b3b3; +} +rect.output-background, rect.output-background-filler { + fill: #888; +} +text.input-label { + fill: #666; +} +polyline.input-label, path.input-label { + stroke: #666; +} +text.output-label { + fill: #ccc; +} +polyline.output-label, path.output-label { + stroke: #ccc; +} + +circle.port-rim { + stroke: #bbb; +} +circle.port-barrel { + stroke: #222; + fill: #222; +} +circle.knob-center { + fill: #bbb; +} +circle.knob-rim { + fill: #555; +} +circle.knob-tick { + fill: #fff; +} +polyline.knob-tick { + stroke: #fff; +} +</style> + + <defs> + <symbol id="dial-length-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" transform="rotate(-240) translate(26 0) rotate(240) translate(-2 3)">0</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-210) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-180) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-150) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-120) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(-90) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-60) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-30) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(0) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(30) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(60) translate(22 0)"/> + </g> + </symbol> + + <symbol id="dial-clock-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" text-anchor="middle" transform="rotate(-240) translate(20 0) rotate(240.0) translate(0 3)">1</text> + <text font-size="7pt" text-anchor="middle" transform="rotate(-202.20355269907728) translate(19 0) rotate(202.20355269907728) translate(0 3)">2</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-174.5346329292023) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-140.0) translate(20 0) rotate(140.0) translate(0 3)">8</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-114.64336589439827) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-93.61498905772001) translate(20 0) rotate(93.61498905772001) translate(0 3)">16</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-58.73460656500686) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-29.558287676339546) translate(21 0) rotate(29.558287676339546) translate(0 3)">32</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(19.119387817386496) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(60) translate(20 0) rotate(-60) translate(0 3)">64</text> + </g> + </symbol> + </defs> + + + + <rect class="module-background background-fill" width="75.0" height="380.0"/> + + + <polyline class="module-border-inner" points="1,1 74.0,1 74.0,379.0 1,379.0 1,1" stroke-width="0.5" fill="none"/> + <polyline class="module-border-middle" points="0.5,0.5 74.5,0.5 74.5,379.5 0.5,379.5 0.5,0.5" stroke-width="0.8" fill="none"/> + <polyline class="module-border-outer" points="0,0 75.0,0 75.0,380.0 0,380.0 0,0" stroke-width="1" fill="none"/> + + <text class="title" font-size="9pt" letter-spacing="3px" text-anchor="end" transform="translate(72.0 17)">RGATE</text> + <g transform="translate(25 374)"> + <text class="brand" font-size="6.5pt" letter-spacing="2px">BGA</text> + <rect class="background-fill" width="2" height="2" fill="#ddd" transform="translate(11.5 -4)"/> + </g> + + + <g transform="translate(0 35)"> + <text font-size="8pt" letter-spacing="1.5px" text-anchor="middle" transform="translate(37.5 0)">LENGTH</text> + <g transform="translate(18.5 13)"><svg id="LENGTH_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-length-rgate" transform="translate(0 -5.5)" href="#dial-length-rgate"/> + </g> + + <g transform="translate(0 114)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK DIV</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_DIVIDE_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 185)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK MUL</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_MULTIPLY_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 248)"> + <rect class="input-background" width="64" height="112" rx="5" transform="translate(5.5 0)"/> + <g transform="translate(10.5 3)"><svg id="LENGTH_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 35)">LEN</text> + <g transform="translate(40.5 3)"><svg id="CLOCK_DIVIDE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text> + <g transform="translate(10.5 40)"><svg id="RUN_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text> + <g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 72)">MUL</text> + <g transform="translate(10.5 77)"><svg id="CLOCK_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="1px" text-anchor="middle" transform="translate(22.5 109)">CLOCK</text> + <g class="io-group" transform="translate(30 72)"> + <rect class="output-background" width="32" height="38" rx="5" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="32" height="10" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="10" height="10" transform="translate(7.5 30)"/> + <g transform="translate(10.5 5)"><svg id="GATE_OUTPUT"> + <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> + </svg></g> + <text class="output-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 37)">GATE</text> + </g> + </g> +</svg> diff --git a/res-pp/RGate-pp.svg b/res-pp/RGate-pp.svg @@ -0,0 +1,209 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="75.0" height="380.0" viewBox="0 0 75.0 380.0"> + <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; +} + +polyline { + stroke: #333; +} +path { + stroke: #333; +} + +rect.module-background, .background-fill { + fill: #ddd; +} +polyline.module-border-inner { + stroke: #e4e4e4; +} +polyline.module-border-middle { + stroke: #ebebeb; +} +polyline.module-border-outer { + stroke: #f2f2f2; +} + +g.io-group { +} +rect.input-background, rect.input-background-filler { + fill: #fafafa; +} +rect.output-background, rect.output-background-filler { + fill: #bbb; +} +text.input-label, text.output-label { + /* font-size: 6pt; */ +} +polyline.input-label, polyline.output-label { +} +path.input-label, path.output-label { +} + +circle.port-rim { + stroke: #f0f0f0; +} +circle.port-barrel { + stroke: #222; + fill: #222; +} +circle.knob-center { + fill: #eee; +} +circle.knob-rim { + fill: #333; +} +circle.knob-tick { + fill: #fff; +} +polyline.knob-tick { + stroke: #fff; +} +</style> + + <defs> + <symbol id="dial-length-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" transform="rotate(-240) translate(26 0) rotate(240) translate(-2 3)">0</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-210) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-180) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-150) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-120) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(-90) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-60) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-30) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(0) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(30) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(60) translate(22 0)"/> + </g> + </symbol> + + <symbol id="dial-clock-rgate" viewBox="0 0 75.0px 75.0px"> + <g transform="translate(37.5 37.5)"> + <text font-size="7pt" text-anchor="middle" transform="rotate(-240) translate(20 0) rotate(240.0) translate(0 3)">1</text> + <text font-size="7pt" text-anchor="middle" transform="rotate(-202.20355269907728) translate(19 0) rotate(202.20355269907728) translate(0 3)">2</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-174.5346329292023) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-140.0) translate(20 0) rotate(140.0) translate(0 3)">8</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-114.64336589439827) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-93.61498905772001) translate(20 0) rotate(93.61498905772001) translate(0 3)">16</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-58.73460656500686) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(-29.558287676339546) translate(21 0) rotate(29.558287676339546) translate(0 3)">32</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(19.119387817386496) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(60) translate(20 0) rotate(-60) translate(0 3)">64</text> + </g> + </symbol> + </defs> + + + + <rect class="module-background background-fill" width="75.0" height="380.0"/> + + + <polyline class="module-border-inner" points="1,1 74.0,1 74.0,379.0 1,379.0 1,1" stroke-width="0.5" fill="none"/> + <polyline class="module-border-middle" points="0.5,0.5 74.5,0.5 74.5,379.5 0.5,379.5 0.5,0.5" stroke-width="0.8" fill="none"/> + <polyline class="module-border-outer" points="0,0 75.0,0 75.0,380.0 0,380.0 0,0" stroke-width="1" fill="none"/> + + <text class="title" font-size="9pt" letter-spacing="3px" text-anchor="end" transform="translate(72.0 17)">RGATE</text> + <g transform="translate(25 374)"> + <text class="brand" font-size="6.5pt" letter-spacing="2px">BGA</text> + <rect class="background-fill" width="2" height="2" fill="#ddd" transform="translate(11.5 -4)"/> + </g> + + + <g transform="translate(0 35)"> + <text font-size="8pt" letter-spacing="1.5px" text-anchor="middle" transform="translate(37.5 0)">LENGTH</text> + <g transform="translate(18.5 13)"><svg id="LENGTH_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-length-rgate" transform="translate(0 -5.5)" href="#dial-length-rgate"/> + </g> + + <g transform="translate(0 114)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK DIV</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_DIVIDE_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 185)"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate(37.5 0)">CLK MUL</text> + <g transform="translate(24.5 15)"><svg id="CLOCK_MULTIPLY_PARAM"> + <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.5" stroke-width="1" stroke="#00f" fill="none"/> + </g> + </svg></g> + <use xlink:href="#dial-clock-rgate" transform="translate(0 -9.5)" href="#dial-clock-rgate"/> + </g> + + <g transform="translate(0 248)"> + <rect class="input-background" width="64" height="112" rx="5" transform="translate(5.5 0)"/> + <g transform="translate(10.5 3)"><svg id="LENGTH_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 35)">LEN</text> + <g transform="translate(40.5 3)"><svg id="CLOCK_DIVIDE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text> + <g transform="translate(10.5 40)"><svg id="RUN_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text> + <g transform="translate(40.5 40)"><svg id="CLOCK_MULTIPLE_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 72)">MUL</text> + <g transform="translate(10.5 77)"><svg id="CLOCK_INPUT"> + <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> + </svg></g> + <text class="input-label" font-size="5pt" letter-spacing="1px" text-anchor="middle" transform="translate(22.5 109)">CLOCK</text> + <g class="io-group" transform="translate(30 72)"> + <rect class="output-background" width="32" height="38" rx="5" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="32" height="10" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="10" height="10" transform="translate(7.5 30)"/> + <g transform="translate(10.5 5)"><svg id="GATE_OUTPUT"> + <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> + </svg></g> + <text class="output-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 37)">GATE</text> + </g> + </g> +</svg> diff --git a/res-src/RGate-src.svg b/res-src/RGate-src.svg @@ -0,0 +1,77 @@ +<module hp="5"> + <style/> + + <defs> + <symbol id="dial-length-rgate" viewBox="0 0 ${width}px ${width}px"> + <g transform="translate($width/2.0 $width/2.0)"> + <text font-size="7pt" transform="rotate(-240) translate(26 0) rotate(240) translate(-2 3)">0</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-210) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-180) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-150) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-120) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(-90) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-60) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(-30) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(0) translate(22 0)"/> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(30) translate(22 0)"/> + <polyline points="0,0 5,0" stroke-width="1.5" transform="rotate(60) translate(22 0)"/> + </g> + </symbol> + + <symbol id="dial-clock-rgate" viewBox="0 0 ${width}px ${width}px"> + <g transform="translate($width/2.0 $width/2.0)"> + <text font-size="7pt" text-anchor="middle" var-r="-240" transform="rotate($r) translate(20 0) rotate(-1.0*$r) translate(0 3)">1</text> + <text font-size="7pt" text-anchor="middle" var-r="1.0**0.5/63.0**0.5*300.0-240.0" transform="rotate($r) translate(19 0) rotate(-1.0*$r) translate(0 3)">2</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(3.0**0.5/63.0**0.5*300.0-240.0) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" var-r="7.0**0.5/63.0**0.5*300.0-240.0" transform="rotate($r) translate(20 0) rotate(-1.0*$r) translate(0 3)">8</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(11.0**0.5/63.0**0.5*300.0-240.0) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" var-r="15.0**0.5/63.0**0.5*300.0-240.0" transform="rotate($r) translate(20 0) rotate(-1.0*$r) translate(0 3)">16</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(23.0**0.5/63.0**0.5*300.0-240.0) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" var-r="31.0**0.5/63.0**0.5*300.0-240.0" transform="rotate($r) translate(21 0) rotate(-1.0*$r) translate(0 3)">32</text> + <polyline points="0,0 3.5,0" stroke-width="0.7" transform="rotate(47.0**0.5/63.0**0.5*300.0-240.0) translate(16 0)"/> + <text font-size="7pt" text-anchor="middle" transform="rotate(60) translate(20 0) rotate(-60) translate(0 3)">64</text> + </g> + </symbol> + </defs> + + <def xlink:href="#module5" var-name="RGATE"/> + + <g transform="translate(0 35)" var-pad="13"> + <text font-size="8pt" letter-spacing="1.5px" text-anchor="middle" transform="translate($width/2.0 0)">LENGTH</text> + <def id="LENGTH_PARAM" xlink:href="#knob38" transform="translate($width/2.0-19 $pad)"/> + <use xlink:href="#dial-length-rgate" transform="translate(0 $pad+19-$width/2.0)"/> + </g> + + <g transform="translate(0 114)" var-pad="15"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate($width/2.0 0)">CLK DIV</text> + <def id="CLOCK_DIVIDE_PARAM" xlink:href="#knob26" transform="translate($width/2.0-13.0 $pad)"/> + <use xlink:href="#dial-clock-rgate" transform="translate(0 $pad+13-$width/2.0)"/> + </g> + + <g transform="translate(0 185)" var-pad="15"> + <text font-size="8pt" letter-spacing="1.2px" text-anchor="middle" transform="translate($width/2 0)">CLK MUL</text> + <def id="CLOCK_MULTIPLY_PARAM" xlink:href="#knob26" transform="translate($width/2.0-13.0 $pad)"/> + <use xlink:href="#dial-clock-rgate" transform="translate(0 $pad+13-$width/2.0)"/> + </g> + + <g transform="translate(0 248)"> + <rect class="input-background" width="64" height="112" rx="5" transform="translate(5.5 0)" /> + <def id="LENGTH_INPUT" xlink:href="#input" transform="translate(10.5 3)"/> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 35)">LEN</text> + <def id="CLOCK_DIVIDE_INPUT" xlink:href="#input" transform="translate(40.5 3)"/> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 35)">DIV</text> + <def id="RUN_INPUT" xlink:href="#input" transform="translate(10.5 40)"/> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 72)">RUN</text> + <def id="CLOCK_MULTIPLE_INPUT" xlink:href="#input" transform="translate(40.5 40)"/> + <text class="input-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(52.5 72)">MUL</text> + <def id="CLOCK_INPUT" xlink:href="#input" transform="translate(10.5 77)"/> + <text class="input-label" font-size="5pt" letter-spacing="1px" text-anchor="middle" transform="translate(22.5 109)">CLOCK</text> + <g class="io-group" transform="translate(30 72)"> + <rect class="output-background" width="32" height="38" rx="5" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="32" height="10" transform="translate(7.5 2)"/> + <rect class="output-background-filler" width="10" height="10" transform="translate(7.5 30)"/> + <def id="GATE_OUTPUT" xlink:href="#output" transform="translate(10.5 5)"/> + <text class="output-label" font-size="5pt" letter-spacing="2px" text-anchor="middle" transform="translate(22.5 37)">GATE</text> + </g> + </g> +</module> diff --git a/res/RGate-dark.svg b/res/RGate-dark.svg Binary files differ. diff --git a/res/RGate-lowcontrast.svg b/res/RGate-lowcontrast.svg Binary files differ. diff --git a/res/RGate.svg b/res/RGate.svg Binary files differ. diff --git a/src/RGate.cpp b/src/RGate.cpp @@ -0,0 +1,333 @@ + +#include "RGate.hpp" + +#define RUN_MODE "run_mode" +#define INITIAL_PULSE_SECONDS "initial_pulse_seconds" +#define POLY_INPUT "poly_input" + +void RGate::Engine::reset(bool triggers, bool hard) { + if (triggers) { + clockTrigger.reset(); + runTrigger.reset(); + initialGatePulseGen.process(10.0f); + } + if (hard) { + secondsSinceLastClock = -1.0f; + clockSeconds = -1.0f; + dividedSeconds = -1.0f; + multipliedSeconds = -1.0f; + gateSeconds = 0.0f; + } + dividerCount = 0; + dividedProgressSeconds = 0.0f; +} + +void RGate::reset() { + for (int c = 0; c < _channels; ++c) { + _engines[c]->reset(); + } +} + +void RGate::sampleRateChange() { + _sampleTime = APP->engine->getSampleTime(); +} + +json_t* RGate::toJson(json_t* root) { + json_object_set_new(root, RUN_MODE, json_integer(_runMode)); + json_object_set_new(root, INITIAL_PULSE_SECONDS, json_real(_initialPulseSeconds)); + json_object_set_new(root, POLY_INPUT, json_integer(_polyInputID)); + return root; +} + +void RGate::fromJson(json_t* root) { + json_t* rm = json_object_get(root, RUN_MODE); + if (rm) { + RunMode m = (RunMode)json_integer_value(rm); + switch (m) { + case GATED_RUN_RUNMODE: + case GATED_RUN_RESET_SOFT_RUNMODE: + case GATED_RUN_RESET_HARD_RUNMODE: + case TRIGGERED_RUN_RUNMODE: + case TRIGGERED_RUN_RESET_SOFT_RUNMODE: + case TRIGGERED_RUN_RESET_HARD_RUNMODE: + case RESET_SOFT_RUNMODE: + case RESET_HARD_RUNMODE: { + _runMode = m; + break; + } + default: { + _runMode = defaultRunMode; + } + } + } + + json_t* ips = json_object_get(root, INITIAL_PULSE_SECONDS); + if (ips) { + _initialPulseSeconds = std::max(0.0f, (float)json_real_value(ips)); + } + + json_t* p = json_object_get(root, POLY_INPUT); + if (p) { + _polyInputID = json_integer_value(p); + } +} + +bool RGate::active() { + return outputs[GATE_OUTPUT].isConnected(); +} + +int RGate::channels() { + return inputs[_polyInputID].getChannels(); +} + +void RGate::addChannel(int c) { + _engines[c] = new Engine(); + _engines[c]->reset(); +} + +void RGate::removeChannel(int c) { + delete _engines[c]; + _engines[c] = NULL; +} + +void RGate::modulateChannel(int c) { + Engine& e = *_engines[c]; + + e.gatePercentage = clamp(params[LENGTH_PARAM].getValue(), 0.0f, 1.0f); + if (inputs[LENGTH_INPUT].isConnected()) { + e.gatePercentage *= clamp(inputs[LENGTH_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); + } + + float division = clamp(params[CLOCK_DIVIDE_PARAM].getValue(), 0.0f, 1.0f); + if (inputs[CLOCK_DIVIDE_INPUT].isConnected()) { + division *= clamp(inputs[CLOCK_DIVIDE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); + } + division *= division; + division *= 63.0f; + division += 1.0f; + e.division = clamp((int)roundf(division), 1, 64); + + float multiplication = clamp(params[CLOCK_MULTIPLY_PARAM].getValue(), 0.0f, 1.0f); + if (inputs[CLOCK_MULTIPLE_INPUT].isConnected()) { + multiplication *= clamp(inputs[CLOCK_MULTIPLE_INPUT].getPolyVoltage(c) / 10.0f, 0.0f, 1.0f); + } + multiplication *= multiplication; + multiplication *= 63.0f; + multiplication += 1.0f; + e.multiplication = clamp((int)roundf(multiplication), 1, 64); +} + +void RGate::processChannel(const ProcessArgs& args, int c) { + Engine& e = *_engines[c]; + + bool runTriggered = e.runTrigger.process(inputs[RUN_INPUT].getPolyVoltage(c)); + switch (_runMode) { + case GATED_RUN_RUNMODE: { + _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING; + break; + } + case GATED_RUN_RESET_SOFT_RUNMODE: { + _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING; + if (runTriggered) { + e.reset(false, false); + } + break; + } + case GATED_RUN_RESET_HARD_RUNMODE: { + _running = e.runTrigger.isHigh() ? YES_RUNNING : NO_RUNNING; + if (runTriggered) { + e.reset(false, true); + } + break; + } + case TRIGGERED_RUN_RUNMODE: { + if (runTriggered) { + switch (_running) { + case UNKNOWN_RUNNING: + case NO_RUNNING: { + _running = YES_RUNNING; + break; + } + case YES_RUNNING: { + _running = NO_RUNNING; + break; + } + } + } + break; + } + case TRIGGERED_RUN_RESET_SOFT_RUNMODE: { + if (runTriggered) { + switch (_running) { + case UNKNOWN_RUNNING: + case NO_RUNNING: { + _running = YES_RUNNING; + break; + } + case YES_RUNNING: { + _running = NO_RUNNING; + break; + } + } + e.reset(false, false); + } + break; + } + case TRIGGERED_RUN_RESET_HARD_RUNMODE: { + if (runTriggered) { + switch (_running) { + case UNKNOWN_RUNNING: + case NO_RUNNING: { + _running = YES_RUNNING; + break; + } + case YES_RUNNING: { + _running = NO_RUNNING; + break; + } + } + e.reset(false, true); + } + break; + } + case RESET_SOFT_RUNMODE: { + if (runTriggered) { + e.reset(false, false); + } + break; + } + case RESET_HARD_RUNMODE: { + if (runTriggered) { + e.reset(false, true); + } + break; + } + } + + bool clock = false; + if (inputs[CLOCK_INPUT].isConnected()) { + clock = e.clockTrigger.process(inputs[CLOCK_INPUT].getPolyVoltage(c)); + if (clock) { + if (e.secondsSinceLastClock > 0.0f) { + e.clockSeconds = e.secondsSinceLastClock; + } + e.secondsSinceLastClock = 0.0f; + + if (_running == UNKNOWN_RUNNING) { + _running = YES_RUNNING; + } + } + e.secondsSinceLastClock += _sampleTime; + } + + float out = 0.0f; + if (e.clockSeconds > 0.0f) { + e.dividedSeconds = e.clockSeconds * (float)e.division; + e.multipliedSeconds = e.dividedSeconds / (float)e.multiplication; + e.gateSeconds = std::max(0.001f, e.multipliedSeconds * e.gatePercentage); + + if (clock) { + if (e.dividerCount < 1) { + e.dividedProgressSeconds = 0.0f; + } + else { + e.dividedProgressSeconds += _sampleTime; + } + ++e.dividerCount; + if (e.dividerCount >= e.division) { + e.dividerCount = 0; + } + } + else { + e.dividedProgressSeconds += _sampleTime; + } + + float multipliedProgressSeconds = e.dividedProgressSeconds / e.multipliedSeconds; + multipliedProgressSeconds -= (float)(int)multipliedProgressSeconds; + multipliedProgressSeconds *= e.multipliedSeconds; + out = (float)(multipliedProgressSeconds <= e.gateSeconds); + } + else { + if (clock) { + ++e.dividerCount; + if (_initialPulseSeconds > 0.0f) { + e.initialGatePulseGen.trigger(_initialPulseSeconds); + } + } + e.dividedProgressSeconds += _sampleTime; + out = (float)e.initialGatePulseGen.process(_sampleTime); + } + + outputs[GATE_OUTPUT].setChannels(_channels); + outputs[GATE_OUTPUT].setVoltage(out * (float)(_running == YES_RUNNING) * 10.0f, c); +} + +struct RGateWidget : BGModuleWidget { + static constexpr int hp = 5; + + RGateWidget(RGate* module) { + setModule(module); + box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT); + setPanel(box.size, "RGate"); + createScrews(); + + // generated by svg_widgets.rb + auto lengthParamPosition = Vec(18.5, 48.0); + auto clockDivideParamPosition = Vec(24.5, 129.0); + auto clockMultiplyParamPosition = Vec(24.5, 200.0); + + auto lengthInputPosition = Vec(10.5, 251.0); + auto clockDivideInputPosition = Vec(40.5, 251.0); + auto runInputPosition = Vec(10.5, 288.0); + auto clockMultipleInputPosition = Vec(40.5, 288.0); + auto clockInputPosition = Vec(10.5, 325.0); + + auto gateOutputPosition = Vec(40.5, 325.0); + // end generated by svg_widgets.rb + + addParam(createParam<Knob38>(lengthParamPosition, module, RGate::LENGTH_PARAM)); + addParam(createParam<Knob26>(clockDivideParamPosition, module, RGate::CLOCK_DIVIDE_PARAM)); + addParam(createParam<Knob26>(clockMultiplyParamPosition, module, RGate::CLOCK_MULTIPLY_PARAM)); + + addInput(createInput<Port24>(lengthInputPosition, module, RGate::LENGTH_INPUT)); + addInput(createInput<Port24>(clockDivideInputPosition, module, RGate::CLOCK_DIVIDE_INPUT)); + addInput(createInput<Port24>(runInputPosition, module, RGate::RUN_INPUT)); + addInput(createInput<Port24>(clockMultipleInputPosition, module, RGate::CLOCK_MULTIPLE_INPUT)); + addInput(createInput<Port24>(clockInputPosition, module, RGate::CLOCK_INPUT)); + + addOutput(createOutput<Port24>(gateOutputPosition, module, RGate::GATE_OUTPUT)); + } + + void contextMenu(Menu* menu) override { + auto m = dynamic_cast<RGate*>(module); + assert(m); + + OptionsMenuItem* p = new OptionsMenuItem("Polyphony channels from"); + p->addItem(OptionMenuItem("CLOCK input", [m]() { return m->_polyInputID == RGate::CLOCK_INPUT; }, [m]() { m->_polyInputID = RGate::CLOCK_INPUT; })); + p->addItem(OptionMenuItem("LEN input", [m]() { return m->_polyInputID == RGate::LENGTH_INPUT; }, [m]() { m->_polyInputID = RGate::LENGTH_INPUT; })); + OptionsMenuItem::addToMenu(p, menu); + + OptionsMenuItem* r = new OptionsMenuItem("RUN port"); + r->addItem(OptionMenuItem("Toggle run on trigger, hard reset on start", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RESET_HARD_RUNMODE; })); + r->addItem(OptionMenuItem("Toggle run on trigger, soft reset on start", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RESET_SOFT_RUNMODE; })); + r->addItem(OptionMenuItem("Toggle run on trigger", [m]() { return m->_runMode == RGate::TRIGGERED_RUN_RUNMODE; }, [m]() { m->_runMode = RGate::TRIGGERED_RUN_RUNMODE; })); + r->addItem(OptionMenuItem("Run when gate high, hard reset on rising edge", [m]() { return m->_runMode == RGate::GATED_RUN_RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RESET_HARD_RUNMODE; })); + r->addItem(OptionMenuItem("Run when gate high, soft reset on rising edge", [m]() { return m->_runMode == RGate::GATED_RUN_RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RESET_SOFT_RUNMODE; })); + r->addItem(OptionMenuItem("Run when gate high", [m]() { return m->_runMode == RGate::GATED_RUN_RUNMODE; }, [m]() { m->_runMode = RGate::GATED_RUN_RUNMODE; })); + r->addItem(OptionMenuItem("Hard reset on trigger", [m]() { return m->_runMode == RGate::RESET_HARD_RUNMODE; }, [m]() { m->_runMode = RGate::RESET_HARD_RUNMODE; })); + r->addItem(OptionMenuItem("Soft reset on trigger", [m]() { return m->_runMode == RGate::RESET_SOFT_RUNMODE; }, [m]() { m->_runMode = RGate::RESET_SOFT_RUNMODE; })); + OptionsMenuItem::addToMenu(r, menu); + + OptionsMenuItem* i = new OptionsMenuItem("Initial pulse duration"); + i->addItem(OptionMenuItem("No pulse", [m]() { return m->_initialPulseSeconds == 0.0f; }, [m]() { m->_initialPulseSeconds = 0.0f; })); + i->addItem(OptionMenuItem("1ms", [m]() { return m->_initialPulseSeconds == 0.001f; }, [m]() { m->_initialPulseSeconds = 0.001f; })); + i->addItem(OptionMenuItem("100ms", [m]() { return m->_initialPulseSeconds == 0.1f; }, [m]() { m->_initialPulseSeconds = 0.1f; })); + i->addItem(OptionMenuItem("200ms", [m]() { return m->_initialPulseSeconds == 0.2f; }, [m]() { m->_initialPulseSeconds = 0.2f; })); + i->addItem(OptionMenuItem("300ms", [m]() { return m->_initialPulseSeconds == 0.3f; }, [m]() { m->_initialPulseSeconds = 0.3f; })); + i->addItem(OptionMenuItem("400ms", [m]() { return m->_initialPulseSeconds == 0.4f; }, [m]() { m->_initialPulseSeconds = 0.4f; })); + i->addItem(OptionMenuItem("500ms", [m]() { return m->_initialPulseSeconds == 0.5f; }, [m]() { m->_initialPulseSeconds = 0.4f; })); + OptionsMenuItem::addToMenu(i, menu); + } +}; + +Model* modelRGate = createModel<RGate, RGateWidget>("Bogaudio-RGate", "RGATE", "Clock-relative gate generator & clock divider/multiplier", "Clock modulator", "Polyphonic"); diff --git a/src/RGate.hpp b/src/RGate.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "bogaudio.hpp" + +extern Model* modelRGate; + +namespace bogaudio { + +struct RGate : BGModule { + enum ParamsIds { + LENGTH_PARAM, + CLOCK_DIVIDE_PARAM, + CLOCK_MULTIPLY_PARAM, + NUM_PARAMS + }; + + enum InputsIds { + LENGTH_INPUT, + CLOCK_DIVIDE_INPUT, + RUN_INPUT, + CLOCK_MULTIPLE_INPUT, + CLOCK_INPUT, + NUM_INPUTS + }; + + enum OutputsIds { + GATE_OUTPUT, + NUM_OUTPUTS + }; + + enum RunMode { + GATED_RUN_RUNMODE, + GATED_RUN_RESET_SOFT_RUNMODE, + GATED_RUN_RESET_HARD_RUNMODE, + TRIGGERED_RUN_RUNMODE, + TRIGGERED_RUN_RESET_SOFT_RUNMODE, + TRIGGERED_RUN_RESET_HARD_RUNMODE, + RESET_SOFT_RUNMODE, + RESET_HARD_RUNMODE + }; + static constexpr RunMode defaultRunMode = TRIGGERED_RUN_RESET_HARD_RUNMODE; + + enum Running { + UNKNOWN_RUNNING, + YES_RUNNING, + NO_RUNNING + }; + + struct Engine { + Trigger clockTrigger; + Trigger runTrigger; + rack::dsp::PulseGenerator initialGatePulseGen; + float gatePercentage = 0.0f; + int division = 1; + int multiplication = 1; + float secondsSinceLastClock = -1.0f; + float clockSeconds = -1.0f; + float dividedSeconds = -1.0f; + float multipliedSeconds = -1.0f; + float gateSeconds = 0.1f; + int dividerCount = 0; + float dividedProgressSeconds = 0.0f; + + void reset(bool triggers = true, bool hard = true); + }; + + Engine* _engines[maxChannels] {}; + float _sampleTime = 0.001f; + RunMode _runMode = defaultRunMode; + Running _running = UNKNOWN_RUNNING; + float _initialPulseSeconds = 0.2f; + int _polyInputID = CLOCK_INPUT; + + RGate() { + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); + configParam(LENGTH_PARAM, 0.0f, 1.0f, 0.5f, "Gate length", "%", 0.0f, 100.0f); + configParam<RoundingParamQuantity<ScaledSquaringParamQuantity<63>>>(CLOCK_DIVIDE_PARAM, 0.0f, 1.0f, 0.0f, "Clock division", "", 0.0f, 1.0f, 1.0f); + configParam<RoundingParamQuantity<ScaledSquaringParamQuantity<63>>>(CLOCK_MULTIPLY_PARAM, 0.0f, 1.0f, 0.0f, "Clock multiplication", "", 0.0f, 1.0f, 1.0f); + } + + void reset() override; + void sampleRateChange() override; + json_t* toJson(json_t* root) override; + void fromJson(json_t* root) override; + bool active() override; + int channels() override; + void addChannel(int c) override; + void removeChannel(int c) override; + void modulateChannel(int c) override; + void processChannel(const ProcessArgs& args, int c) override; +}; + +} // namespace bogaudio diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp @@ -74,6 +74,7 @@ #include "Pressor.hpp" #include "Pulse.hpp" #include "Reftone.hpp" +#include "RGate.hpp" #include "SampleHold.hpp" #include "Shaper.hpp" #include "ShaperPlus.hpp" @@ -142,6 +143,7 @@ void init(rack::Plugin *p) { p->addModel(modelADSR); p->addModel(modelFollow); p->addModel(modelDGate); + p->addModel(modelRGate); p->addModel(modelEdge); p->addModel(modelNoise); diff --git a/src/param_quantities.hpp b/src/param_quantities.hpp @@ -1,6 +1,7 @@ #pragma once #include "rack.hpp" +#include <cmath> using namespace rack; @@ -45,6 +46,24 @@ typedef ScaledSquaringParamQuantity<10> TenXSquaringParamQuantity; typedef TenXSquaringParamQuantity EnvelopeSegmentParamQuantity; +template <class Base = ParamQuantity> +struct RoundingParamQuantity : Base { + float getDisplayValue() override { + float v = Base::getDisplayValue(); + if (!Base::module) { + return v; + } + return roundf(v); + } + + void setDisplayValue(float displayValue) override { + if (!Base::module) { + return; + } + Base::setDisplayValue(roundf(displayValue)); + } +}; + struct AmplifierParamQuantity : ParamQuantity { virtual bool isLinear(); virtual void setUnits(bool linear);