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:
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);