commit 36b7f203c8538b4be4a76e5144ae2a688fecbe26
parent 0d24f03ee4834de6c04365e44a5f61abeaea89a3
Author: Matt Demanett <matt@demanett.net>
Date: Sun, 5 Apr 2020 22:10:53 -0400
PGMR, PGMRX: four-step programmer and sequencer, with chainable expander. #100
Diffstat:
23 files changed, 1581 insertions(+), 164 deletions(-)
diff --git a/plugin.json b/plugin.json
@@ -518,6 +518,25 @@
]
},
{
+ "slug": "Bogaudio-Pgmr",
+ "name": "PGMR",
+ "description": "4-step programmer and sequencer",
+ "tags": [
+ "Sequencer",
+ "Polyphonic"
+ ]
+ },
+ {
+ "slug": "Bogaudio-PgmrX",
+ "name": "PGMRX",
+ "description": "4-step chainable expander for PGMR",
+ "tags": [
+ "Sequencer",
+ "Expander",
+ "Polyphonic"
+ ]
+ },
+ {
"slug": "Bogaudio-Analyzer",
"name": "ANALYZER",
"description": "4-channel spectrum analyzer",
diff --git a/res-src/Pgmr-src.svg b/res-src/Pgmr-src.svg
@@ -0,0 +1,276 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="225"
+ height="380"
+ viewBox="0 0 225 380"
+>
+ <style>
+ text {
+ fill: #333;
+ font-family: 'Roboto', sans-serif;
+ font-weight: bold;
+ }
+ text.title {
+ font-family: 'Comfortaa', sans-serif;
+ font-weight: normal;
+ }
+ text.brand {
+ font-family: 'Audiowide', sans-serif;
+ font-weight: bold;
+ }
+ </style>
+
+ <defs>
+ <symbol id="knob26" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <polyline points="-5,0 5,0" stroke-width="1" stroke="#00f" />
+ <polyline points="0,-5 0,5" stroke-width="1" stroke="#00f" />
+ <circle cx="0" cy="0" r="12.5" stroke-width="1" stroke="#00f" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="knobguide-pgmr" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-240) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-225) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-210) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-195) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-180) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-165) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-150) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-135) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-120) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-105) translate(15 0)" />
+
+ <text font-size="5pt" transform="rotate(-90) translate(15 0) rotate(90) translate(-1.9 0)">0</text>
+
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-75) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-60) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-45) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-30) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-15) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(0) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(15) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(30) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(45) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(60) translate(15 0)" />
+
+ <text font-size="9pt" transform="rotate(-225) translate(20 0) rotate(225) translate(-1 8.8)">-</text>
+ <text font-size="7pt" transform="rotate(45) translate(20 0) rotate(-45) translate(-3.9 8.5)">+</text>
+ </g>
+ </symbol>
+
+ <symbol id="button" viewBox="0 0 18px 18px">
+ <g transform="translate(9 9)">
+ <circle cx="0" cy="0" r="8.5" stroke-width="1" stroke="#00f" fill="#f00" />
+ </g>
+ </symbol>
+
+ <symbol id="light-small" viewBox="0 0 6.4px 6.4px">
+ <rect width="6.4" height="6.4" fill="#0f0" />
+ </symbol>
+
+ <symbol id="button-small" viewBox="0 0 9px 9px">
+ <g transform="translate(4.5 4.5)">
+ <circle r="4" stroke-width="1" stroke="#00f" fill="#f00" />
+ </g>
+ </symbol>
+
+ <symbol id="input" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="output" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" />
+ </g>
+ </symbol>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 224,1 224,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 224.5,0.5 224.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 225,0 225,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <!-- <rect width="80" height="20" fill="#0f0" transform="translate(0 0)" /> -->
+ <!-- <rect width="80" height="20" fill="#0f0" transform="translate(145 0)" /> -->
+ <text class="title" x="82" y="19" font-size="12pt" letter-spacing="4px">PGMR</text>
+ <g transform="translate(72.5 374)">
+ <text class="brand" font-size="8pt" letter-spacing="2px">BOGAUDIO</text>
+ <rect width="3.0" height="3" fill="#ddd" transform="translate(24 -5)" />
+ </g>
+
+ <!-- <polyline points="0,0 225,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 30)" /> -->
+ <!-- <polyline points="0,0 225,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 135.5)" /> -->
+ <!-- <polyline points="0,0 225,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 360)" /> -->
+
+ <g transform="translate(0 56)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="155" rx="5" fill="#bbb" transform="translate(0 -10)" />
+ <use id="A_OUTPUT" xlink:href="#output" transform="translate(5 3)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(8.5 0)">OUT</text>
+ <text font-size="5pt" letter-spacing="2px" transform="translate(15 35)">A</text>
+ <use id="B_OUTPUT" xlink:href="#output" transform="translate(5 38)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(15 70)">B</text>
+ <use id="C_OUTPUT" xlink:href="#output" transform="translate(5 73)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(15 105)">C</text>
+ <use id="D_OUTPUT" xlink:href="#output" transform="translate(5 107)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(15 140)">D</text>
+ </g>
+ </g>
+
+ <g transform="translate(0 224)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 92)" />
+ <rect width="34" height="102" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="CLOCK_INPUT" xlink:href="#input" transform="translate(5 2)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(2.5 34)">CLOCK</text>
+ <text font-size="5pt" letter-spacing="1px" transform="translate(5 46.5)">FWD</text>
+ <use id="DIRECTION_PARAM" xlink:href="#button-small" transform="translate(22.5 39.7)" />
+ <text font-size="5pt" letter-spacing="0.1px" transform="translate(2 60)">S.O.C.</text>
+ <use id="SELECT_ON_CLOCK_PARAM" xlink:href="#button-small" transform="translate(22.5 53.2)" />
+ <use id="SELECT_INPUT" xlink:href="#input" transform="translate(5 66)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 98)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 105)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 -3)" />
+ <rect width="34" height="30" rx="5" fill="#bbb" />
+ <use id="SELECT_ALL_OUTPUT" xlink:href="#output" transform="translate(5 1)" />
+ </g>
+ </g>
+
+ <g transform="translate(45 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.5 -1)">1</text>
+ <use id="CVA1_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB1_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC1_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD1_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT1_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT1_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT1_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT1_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(90 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.5 -1)">2</text>
+ <use id="CVA2_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB2_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC2_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD2_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT2_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT2_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT2_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT2_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(135 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.5 -1)">3</text>
+ <use id="CVA3_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB3_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC3_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD3_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT3_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT3_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT3_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT3_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(180 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.5 -1)">4</text>
+ <use id="CVA4_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB4_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC4_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD4_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT4_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT4_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT4_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT4_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>Z
+ </g>
+ <!-- <polyline points="0,0 225,0" stroke="#0f0" stroke-width="1" fill="none" transform="translate(0 315)" /> -->
+</svg>
diff --git a/res-src/PgmrX-src.svg b/res-src/PgmrX-src.svg
@@ -0,0 +1,230 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="180"
+ height="380"
+ viewBox="0 0 180 380"
+>
+ <style>
+ text {
+ fill: #333;
+ font-family: 'Roboto', sans-serif;
+ font-weight: bold;
+ }
+ text.title {
+ font-family: 'Comfortaa', sans-serif;
+ font-weight: normal;
+ }
+ text.brand {
+ font-family: 'Audiowide', sans-serif;
+ font-weight: bold;
+ }
+ </style>
+
+ <defs>
+ <symbol id="knob26" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <polyline points="-5,0 5,0" stroke-width="1" stroke="#00f" />
+ <polyline points="0,-5 0,5" stroke-width="1" stroke="#00f" />
+ <circle cx="0" cy="0" r="12.5" stroke-width="1" stroke="#00f" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="knobguide-pgmr" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-240) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-225) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-210) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-195) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-180) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-165) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-150) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-135) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-120) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-105) translate(15 0)" />
+
+ <text font-size="5pt" transform="rotate(-90) translate(15 0) rotate(90) translate(-1.9 0)">0</text>
+
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-75) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-60) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-45) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(-30) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(-15) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(0) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(15) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(30) translate(15 0)" />
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" transform="rotate(45) translate(15 0)" />
+ <polyline points="0,0 3.5,0" stroke-width="1.0" stroke="#333" transform="rotate(60) translate(15 0)" />
+
+ <text font-size="9pt" transform="rotate(-225) translate(20 0) rotate(225) translate(-1 8.8)">-</text>
+ <text font-size="7pt" transform="rotate(45) translate(20 0) rotate(-45) translate(-3.9 8.5)">+</text>
+ </g>
+ </symbol>
+
+ <symbol id="button" viewBox="0 0 18px 18px">
+ <g transform="translate(9 9)">
+ <circle cx="0" cy="0" r="8.5" stroke-width="1" stroke="#00f" fill="#f00" />
+ </g>
+ </symbol>
+
+ <symbol id="light-small" viewBox="0 0 6.4px 6.4px">
+ <rect width="6.4" height="6.4" fill="#0f0" />
+ </symbol>
+
+ <symbol id="input" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#0f0" fill="#0f0" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#0f0" fill="none" />
+ </g>
+ </symbol>
+
+ <symbol id="output" viewBox="0 0 24px 24px">
+ <g transform="translate(12 12)">
+ <circle cx="0" cy="0" r="5" stroke-width="1" stroke="#f00" fill="#f00" />
+ <circle cx="0" cy="0" r="10.5" stroke-width="3" stroke="#f00" fill="none" />
+ </g>
+ </symbol>
+ </defs>
+
+ <rect width="100%" height="100%" fill="#ddd" />
+ <polyline points="1,1 179,1 179,379 1,379 1,1" stroke="#e4e4e4" stroke-width="0.5" fill="none" />
+ <polyline points="0.5,0.5 179.5,0.5 179.5,379.5 0.5,379.5 0.5,0.5" stroke="#ebebeb" stroke-width="0.8" fill="none" />
+ <polyline points="0,0 180,0 180,380 0,380 0,0" stroke="#f2f2f2" stroke-width="1" fill="none" />
+
+ <!-- <rect width="50" height="20" fill="#0f0" transform="translate(0 0)" /> -->
+ <!-- <rect width="50" height="20" fill="#0f0" transform="translate(130 0)" /> -->
+ <text class="title" x="52" y="19" font-size="12pt" letter-spacing="4px">PGMRX</text>
+ <g transform="translate(50 374)">
+ <text class="brand" font-size="8pt" letter-spacing="2px">BOGAUDIO</text>
+ <rect width="3.0" height="3" fill="#ddd" transform="translate(24 -5)" />
+ </g>
+
+ <g transform="translate(0 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(18 -1)">+1</text>
+ <use id="CVA1_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB1_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC1_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD1_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT1_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT1_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT1_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT1_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(45 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(18 -1)">+2</text>
+ <use id="CVA2_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB2_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC2_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD2_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT2_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT2_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT2_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT2_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(90 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(18 -1)">+3</text>
+ <use id="CVA3_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB3_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC3_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD3_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT3_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT3_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT3_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT3_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+
+ <g transform="translate(135 30)">
+ <!-- <rect width="34" height="330" fill="#ccc" transform="translate(5.5 0)" /> -->
+ <text font-size="5pt" letter-spacing="2px" transform="translate(17.5 -1)">+4</text>
+ <use id="CVA4_PARAM" xlink:href="#knob26" transform="translate(0 1)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 1)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 48)">A</text>
+ <use id="CVB4_PARAM" xlink:href="#knob26" transform="translate(0 55)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 55)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 102)">B</text>
+ <use id="CVC4_PARAM" xlink:href="#knob26" transform="translate(0 109)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 109)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 156)">C</text>
+ <use id="CVD4_PARAM" xlink:href="#knob26" transform="translate(0 163)" />
+ <use xlink:href="#knobguide-pgmr" transform="translate(0 163)" />
+ <text font-size="5pt" letter-spacing="2px" transform="translate(20.3 210)">D</text>
+ <g transform="translate(0 223)">
+ <g transform="translate(5.5 0)">
+ <rect width="34" height="10" fill="#fafafa" transform="translate(0 63)" />
+ <rect width="34" height="76" rx="5" fill="#fafafa" transform="translate(0 -3)" />
+ <use id="SELECT4_LIGHT" xlink:href="#light-small" transform="translate(13.8 2)" />
+ <use id="SELECT4_PARAM" xlink:href="#button" transform="translate(8 14)" />
+ <use id="SELECT4_INPUT" xlink:href="#input" transform="translate(5 37)" />
+ <text font-size="5pt" letter-spacing="1px" transform="translate(2.5 69)">SELECT</text>
+ </g>
+ <g transform="translate(5.5 73)">
+ <rect width="34" height="10" fill="#bbb" transform="translate(0 0)" />
+ <rect width="34" height="33" rx="5" fill="#bbb" />
+ <use id="SELECT4_OUTPUT" xlink:href="#output" transform="translate(5 4)" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/res/Pgmr.svg b/res/Pgmr.svg
Binary files differ.
diff --git a/res/PgmrX.svg b/res/PgmrX.svg
Binary files differ.
diff --git a/src/AddrSeq.cpp b/src/AddrSeq.cpp
@@ -1,61 +1,18 @@
#include "AddrSeq.hpp"
-#define RANGE_OFFSET "range_offset"
-#define RANGE_SCALE "range_scale"
-
-float AddrSeq::OutputParamQuantity::getDisplayValue() {
- float v = getValue();
- if (!module) {
- return v;
- }
-
- AddrSeq* m = dynamic_cast<AddrSeq*>(module);
- v += m->_rangeOffset;
- v *= m->_rangeScale;
- return v;
-}
-
-void AddrSeq::OutputParamQuantity::setDisplayValue(float v) {
- if (!module) {
- return;
- }
-
- AddrSeq* m = dynamic_cast<AddrSeq*>(module);
- v /= m->_rangeScale;
- v -= m->_rangeOffset;
- setValue(v);
-}
-
-json_t* AddrSeq::dataToJson() {
- json_t* root = AddressableSequenceModule::dataToJson();
- json_object_set_new(root, RANGE_OFFSET, json_real(_rangeOffset));
- json_object_set_new(root, RANGE_SCALE, json_real(_rangeScale));
- return root;
-}
-
-void AddrSeq::dataFromJson(json_t* root) {
- AddressableSequenceModule::dataFromJson(root);
-
- json_t* ro = json_object_get(root, RANGE_OFFSET);
- if (ro) {
- _rangeOffset = json_real_value(ro);
- }
-
- json_t* rs = json_object_get(root, RANGE_SCALE);
- if (rs) {
- _rangeScale = json_real_value(rs);
- }
+void AddrSeq::processAlways(const ProcessArgs& args) {
+ std::fill(_lightSums, _lightSums + 8, 0.0f);
}
void AddrSeq::processChannel(const ProcessArgs& args, int c) {
int step = nextStep(
c,
- inputs[RESET_INPUT],
+ &inputs[RESET_INPUT],
inputs[CLOCK_INPUT],
- params[STEPS_PARAM],
+ ¶ms[STEPS_PARAM],
params[DIRECTION_PARAM],
- params[SELECT_PARAM],
+ ¶ms[SELECT_PARAM],
inputs[SELECT_INPUT]
);
@@ -64,10 +21,15 @@ void AddrSeq::processChannel(const ProcessArgs& args, int c) {
out *= _rangeScale;
outputs[OUT_OUTPUT].setChannels(_channels);
outputs[OUT_OUTPUT].setVoltage(out, c);
- if (c == 0) {
- for (int i = 0; i < 8; ++i) {
- lights[OUT1_LIGHT + i].value = step == i;
- }
+
+ for (int i = 0; i < 8; ++i) {
+ _lightSums[i] += step == i;
+ }
+}
+
+void AddrSeq::postProcessAlways(const ProcessArgs& args) {
+ for (int i = 0; i < 8; ++i) {
+ lights[OUT1_LIGHT + i].value = _lightSums[i] / (float)_channels;
}
}
diff --git a/src/AddrSeq.hpp b/src/AddrSeq.hpp
@@ -2,15 +2,12 @@
#include "bogaudio.hpp"
#include "addressable_sequence.hpp"
-#include "dsp/signal.hpp"
-
-using namespace bogaudio::dsp;
extern Model* modelAddrSeq;
namespace bogaudio {
-struct AddrSeq : AddressableSequenceModule {
+struct AddrSeq : OutputRangeAddressableSequenceModule {
enum ParamsIds {
STEPS_PARAM,
DIRECTION_PARAM,
@@ -50,15 +47,9 @@ struct AddrSeq : AddressableSequenceModule {
NUM_LIGHTS
};
- float _rangeOffset = 0.0f;
- float _rangeScale = 10.0f;
-
- struct OutputParamQuantity : ParamQuantity {
- float getDisplayValue() override;
- void setDisplayValue(float v) override;
- };
+ float _lightSums[8] {};
- AddrSeq() : AddressableSequenceModule(CLOCK_INPUT, SELECT_INPUT) {
+ AddrSeq() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(STEPS_PARAM, 1.0f, 8.0f, 8.0f, "Steps");
configParam(DIRECTION_PARAM, 0.0f, 1.0f, 1.0f, "Direction");
@@ -71,11 +62,12 @@ struct AddrSeq : AddressableSequenceModule {
configParam<OutputParamQuantity>(OUT6_PARAM, -1.0f, 1.0f, 0.0f, "Step 6", " V");
configParam<OutputParamQuantity>(OUT7_PARAM, -1.0f, 1.0f, 0.0f, "Step 7", " V");
configParam<OutputParamQuantity>(OUT8_PARAM, -1.0f, 1.0f, 0.0f, "Step 8", " V");
+ setInputIDs(CLOCK_INPUT, SELECT_INPUT);
}
- json_t* dataToJson() override;
- void dataFromJson(json_t* root) override;
+ void processAlways(const ProcessArgs& args) override;
void processChannel(const ProcessArgs& args, int c) override;
+ void postProcessAlways(const ProcessArgs& args) override;
};
} // namespace bogaudio
diff --git a/src/EightOne.cpp b/src/EightOne.cpp
@@ -1,14 +1,18 @@
#include "EightOne.hpp"
+void EightOne::processAlways(const ProcessArgs& args) {
+ std::fill(_lightSums, _lightSums + 8, 0.0f);
+}
+
void EightOne::processChannel(const ProcessArgs& args, int c) {
int step = nextStep(
c,
- inputs[RESET_INPUT],
+ &inputs[RESET_INPUT],
inputs[CLOCK_INPUT],
- params[STEPS_PARAM],
+ ¶ms[STEPS_PARAM],
params[DIRECTION_PARAM],
- params[SELECT_PARAM],
+ ¶ms[SELECT_PARAM],
inputs[SELECT_INPUT]
);
@@ -16,18 +20,20 @@ void EightOne::processChannel(const ProcessArgs& args, int c) {
if (_channels > 1) {
outputs[OUT_OUTPUT].setChannels(_channels);
outputs[OUT_OUTPUT].setVoltage(in.getPolyVoltage(c), c);
- if (c == 0) {
- for (int i = 0; i < 8; ++i) {
- lights[IN1_LIGHT + i].value = step == i;
- }
- }
}
else {
outputs[OUT_OUTPUT].setChannels(in.getChannels());
outputs[OUT_OUTPUT].writeVoltages(in.getVoltages());
- for (int i = 0; i < 8; ++i) {
- lights[IN1_LIGHT + i].value = step == i;
- }
+ }
+
+ for (int i = 0; i < 8; ++i) {
+ _lightSums[i] += step == i;
+ }
+}
+
+void EightOne::postProcessAlways(const ProcessArgs& args) {
+ for (int i = 0; i < 8; ++i) {
+ lights[IN1_LIGHT + i].value = _lightSums[i] / (float)_channels;
}
}
diff --git a/src/EightOne.hpp b/src/EightOne.hpp
@@ -50,14 +50,19 @@ struct EightOne : AddressableSequenceModule {
NUM_LIGHTS
};
- EightOne() : AddressableSequenceModule(CLOCK_INPUT, SELECT_INPUT) {
+ float _lightSums[8] {};
+
+ EightOne() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(STEPS_PARAM, 1.0f, 8.0f, 8.0f, "Steps");
configParam(DIRECTION_PARAM, 0.0f, 1.0f, 1.0f, "Direction");
configParam(SELECT_PARAM, 0.0f, 7.0f, 0.0f, "Select step");
+ setInputIDs(CLOCK_INPUT, SELECT_INPUT);
}
+ void processAlways(const ProcessArgs& args) override;
void processChannel(const ProcessArgs& args, int c) override;
+ void postProcessAlways(const ProcessArgs& args) override;
};
} // namespace bogaudio
diff --git a/src/Mix4.cpp b/src/Mix4.cpp
@@ -29,7 +29,7 @@ void Mix4::sampleRateChange() {
void Mix4::processAll(const ProcessArgs& args) {
Mix4ExpanderMessage* toExp = &_dummyExpanderMessage;
Mix4ExpanderMessage* fromExp = &_dummyExpanderMessage;
- if (connected()) {
+ if (expanderConnected()) {
toExp = toExpander();
fromExp = fromExpander();
}
@@ -82,7 +82,7 @@ void Mix4::processAll(const ProcessArgs& args) {
float mono = 0.0f;
float left = 0.0f;
float right = 0.0f;
- if (connected()) {
+ if (expanderConnected()) {
mono += fromExp->returnA[0] + fromExp->returnB[0];
left += fromExp->returnA[0] + fromExp->returnB[0];
right += fromExp->returnA[1] + fromExp->returnB[1];
@@ -244,7 +244,7 @@ void Mix4x::sampleRateChange() {
}
void Mix4x::processAll(const ProcessArgs& args) {
- if (!connected()) {
+ if (!baseConnected()) {
outputs[SEND_A_OUTPUT].setVoltage(0.0f);
outputs[SEND_B_OUTPUT].setVoltage(0.0f);
return;
diff --git a/src/Mix4.hpp b/src/Mix4.hpp
@@ -15,7 +15,7 @@ struct Mix4x;
typedef MixerExpanderMessage<4> Mix4ExpanderMessage;
-struct Mix4 : ExpandableModule<Mix4ExpanderMessage, Mix4x> {
+struct Mix4 : ExpandableModule<Mix4ExpanderMessage, Mix4x, BGModule> {
enum ParamsIds {
LEVEL1_PARAM,
PAN1_PARAM,
@@ -105,7 +105,7 @@ struct Mix4 : ExpandableModule<Mix4ExpanderMessage, Mix4x> {
void processAll(const ProcessArgs& args) override;
};
-struct Mix4x : ExpanderModule<Mix4ExpanderMessage, Mix4> {
+struct Mix4x : ExpanderModule<Mix4ExpanderMessage, Mix4, BGModule> {
enum ParamsIds {
LOW1_PARAM,
MID1_PARAM,
diff --git a/src/Mix8.cpp b/src/Mix8.cpp
@@ -29,7 +29,7 @@ void Mix8::sampleRateChange() {
void Mix8::processAll(const ProcessArgs& args) {
Mix8ExpanderMessage* toExp = &_dummyExpanderMessage;
Mix8ExpanderMessage* fromExp = &_dummyExpanderMessage;
- if (connected()) {
+ if (expanderConnected()) {
toExp = toExpander();
fromExp = fromExpander();
}
@@ -86,7 +86,7 @@ void Mix8::processAll(const ProcessArgs& args) {
float mono = 0.0f;
float left = 0.0f;
float right = 0.0f;
- if (connected()) {
+ if (expanderConnected()) {
mono += fromExp->returnA[0] + fromExp->returnB[0];
left += fromExp->returnA[0] + fromExp->returnB[0];
right += fromExp->returnA[1] + fromExp->returnB[1];
@@ -294,7 +294,7 @@ void Mix8x::sampleRateChange() {
}
void Mix8x::processAll(const ProcessArgs& args) {
- if (!connected()) {
+ if (!baseConnected()) {
outputs[SEND_A_OUTPUT].setVoltage(0.0f);
outputs[SEND_B_OUTPUT].setVoltage(0.0f);
return;
diff --git a/src/Mix8.hpp b/src/Mix8.hpp
@@ -15,7 +15,7 @@ struct Mix8x;
typedef MixerExpanderMessage<8> Mix8ExpanderMessage;
-struct Mix8 : ExpandableModule<Mix8ExpanderMessage, Mix8x> {
+struct Mix8 : ExpandableModule<Mix8ExpanderMessage, Mix8x, BGModule> {
enum ParamsIds {
LEVEL1_PARAM,
MUTE1_PARAM,
@@ -145,7 +145,7 @@ struct Mix8 : ExpandableModule<Mix8ExpanderMessage, Mix8x> {
void processAll(const ProcessArgs& args) override;
};
-struct Mix8x : ExpanderModule<Mix8ExpanderMessage, Mix8> {
+struct Mix8x : ExpanderModule<Mix8ExpanderMessage, Mix8, BGModule> {
enum ParamsIds {
LOW1_PARAM,
MID1_PARAM,
diff --git a/src/OneEight.cpp b/src/OneEight.cpp
@@ -1,14 +1,18 @@
#include "OneEight.hpp"
+void OneEight::processAlways(const ProcessArgs& args) {
+ std::fill(_lightSums, _lightSums + 8, 0.0f);
+}
+
void OneEight::processChannel(const ProcessArgs& args, int c) {
int step = nextStep(
c,
- inputs[RESET_INPUT],
+ &inputs[RESET_INPUT],
inputs[CLOCK_INPUT],
- params[STEPS_PARAM],
+ ¶ms[STEPS_PARAM],
params[DIRECTION_PARAM],
- params[SELECT_PARAM],
+ ¶ms[SELECT_PARAM],
inputs[SELECT_INPUT]
);
@@ -17,18 +21,14 @@ void OneEight::processChannel(const ProcessArgs& args, int c) {
for (int i = 0; i < 8; ++i) {
outputs[OUT1_OUTPUT + i].setChannels(_channels);
outputs[OUT1_OUTPUT + i].setVoltage((step == i) * in, c);
- }
- if (c == 0) {
- for (int i = 0; i < 8; ++i) {
- lights[OUT1_LIGHT + i].value = step == i;
- }
+ _lightSums[i] += step == i;
}
}
else if (!inputs[IN_INPUT].isConnected()) {
for (int i = 0; i < 8; ++i) {
outputs[OUT1_OUTPUT + i].setChannels(1);
outputs[OUT1_OUTPUT + i].setVoltage((step == i) * 10.0f);
- lights[OUT1_LIGHT + i].value = step == i;
+ _lightSums[i] += step == i;
}
}
else {
@@ -37,11 +37,17 @@ void OneEight::processChannel(const ProcessArgs& args, int c) {
for (int i = 0; i < 8; ++i) {
outputs[OUT1_OUTPUT + i].setChannels(inputs[IN_INPUT].getChannels());
outputs[OUT1_OUTPUT + i].writeVoltages((step == i) ? in : zeroes);
- lights[OUT1_LIGHT + i].value = step == i;
+ _lightSums[i] += step == i;
}
}
}
+void OneEight::postProcessAlways(const ProcessArgs& args) {
+ for (int i = 0; i < 8; ++i) {
+ lights[OUT1_LIGHT + i].value = _lightSums[i] / (float)_channels;
+ }
+}
+
struct OneEightWidget : AddressableSequenceModuleWidget {
static constexpr int hp = 6;
diff --git a/src/OneEight.hpp b/src/OneEight.hpp
@@ -50,14 +50,19 @@ struct OneEight : AddressableSequenceModule {
NUM_LIGHTS
};
- OneEight() : AddressableSequenceModule(CLOCK_INPUT, SELECT_INPUT) {
+ float _lightSums[8] {};
+
+ OneEight() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
configParam(STEPS_PARAM, 1.0f, 8.0f, 8.0f, "Steps");
configParam(DIRECTION_PARAM, 0.0f, 1.0f, 1.0f, "Direction");
configParam(SELECT_PARAM, 0.0f, 7.0f, 0.0f, "Select step");
+ setInputIDs(CLOCK_INPUT, SELECT_INPUT);
}
+ void processAlways(const ProcessArgs& args) override;
void processChannel(const ProcessArgs& args, int c) override;
+ void postProcessAlways(const ProcessArgs& args) override;
};
} // namespace bogaudio
diff --git a/src/Pgmr.cpp b/src/Pgmr.cpp
@@ -0,0 +1,511 @@
+
+#include "Pgmr.hpp"
+
+#define SELECT_TRIGGERS "SELECT_TRIGGERS"
+
+void PgmrStep::reset() {
+ for (int c = 0; c < BGModule::maxChannels; ++c) {
+ triggers[c].reset();
+ pulseGens[c].process(1000.0f);
+ }
+}
+
+
+PgmrRegistry::Base::Base(Pgmr& b) : module(b) {
+ std::copy(b._localSteps, b._localSteps + 4, std::back_inserter(steps));
+}
+
+int PgmrRegistry::registerBase(Pgmr& b) {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ int id = _nextID;
+ ++_nextID;
+ auto p = _bases.emplace(id, Base(b));
+ b.setSteps(p.first->second.steps);
+ return id;
+}
+
+void PgmrRegistry::deregisterBase(int id) {
+ std::lock_guard<std::mutex> lock(_lock);
+ _bases.erase(id);
+}
+
+void PgmrRegistry::registerExpander(int baseID, int position, PgmrX& x) {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ assert(position > 0);
+ auto base = _bases.find(baseID);
+ if (base != _bases.end()) {
+ int i = 4 * position;
+ if (i < (int)base->second.steps.size()) {
+ assert(!base->second.steps[i]);
+ std::copy(x._localSteps, x._localSteps + 4, base->second.steps.begin() + i);
+ }
+ else {
+ base->second.steps.resize(i + 4, NULL);
+ std::copy(x._localSteps, x._localSteps + 4, base->second.steps.begin() + i);
+ }
+ for (auto i = base->second.steps.begin(), n = base->second.steps.end(); i != n; ++i) {
+ if (!*i) {
+ return;
+ }
+ }
+ base->second.module.setSteps(base->second.steps);
+ }
+}
+
+void PgmrRegistry::deregisterExpander(int baseID, int position) {
+ std::lock_guard<std::mutex> lock(_lock);
+
+ auto base = _bases.find(baseID);
+ if (base != _bases.end()) {
+ int n = 4 * position;
+ if (n < (int)base->second.steps.size()) {
+ int i = 0;
+ for (; i < n; ++i) {
+ if (!base->second.steps[i]) {
+ break;
+ }
+ }
+ base->second.steps.resize(i);
+ base->second.module.setSteps(base->second.steps);
+ }
+ }
+}
+
+PgmrRegistry& PgmrRegistry::registry() {
+ static PgmrRegistry instance;
+ return instance;
+}
+
+
+void Pgmr::reset() {
+ std::lock_guard<std::mutex> lock(_stepsLock);
+
+ for (int c = 0; c < maxChannels; ++c) {
+ _lastSteps[c] = -1;
+ _allPulseGens[c].process(1000.0f);
+ }
+ for (int i = 0, n = _steps.size(); i < n; ++i) {
+ _steps[i]->reset();
+ }
+}
+
+void Pgmr::sampleRateChange() {
+ _sampleTime = APP->engine->getSampleTime();
+}
+
+json_t* Pgmr::dataToJson() {
+ json_t* root = OutputRangeAddressableSequenceModule::dataToJson();
+ json_object_set_new(root, SELECT_TRIGGERS, json_boolean(_selectTriggers));
+ return root;
+}
+
+void Pgmr::dataFromJson(json_t* root) {
+ OutputRangeAddressableSequenceModule::dataFromJson(root);
+ json_t* st = json_object_get(root, SELECT_TRIGGERS);
+ if (st) {
+ _selectTriggers = json_is_true(st);
+ }
+}
+
+void Pgmr::modulate() {
+ _selectOnClock = params[SELECT_ON_CLOCK_PARAM].getValue() > 0.5f;
+}
+
+void Pgmr::processAlways(const ProcessArgs& args) {
+ if (expanderConnected()) {
+ PgmrExpanderMessage* te = toExpander();
+ te->baseID = _id;
+ te->position = 1;
+ te->rangeOffset = _rangeOffset;
+ te->rangeScale = _rangeScale;
+ }
+}
+
+void Pgmr::processChannel(const ProcessArgs& args, int c) {
+ std::lock_guard<std::mutex> lock(_stepsLock);
+ int steps = _steps.size();
+ if (c == 0) {
+ for (int i = 0, n = _steps.size(); i < n; ++i) {
+ _steps[i]->lightSum = 0.0f;
+ }
+ }
+
+ int step = nextStep(
+ c,
+ NULL,
+ inputs[CLOCK_INPUT],
+ NULL,
+ params[DIRECTION_PARAM],
+ NULL,
+ inputs[SELECT_INPUT],
+ steps
+ );
+ for (int i = 0; i < steps; ++i) {
+ if (_steps[i]->triggers[c].process(_steps[i]->selectParam.getValue() + _steps[i]->selectInput.getPolyVoltage(c))) {
+ step = setStep(c, i, steps);
+ }
+ }
+
+ {
+ float out = _steps[step]->aParam.getValue();
+ out += _rangeOffset;
+ out *= _rangeScale;
+ outputs[A_OUTPUT].setChannels(_channels);
+ outputs[A_OUTPUT].setVoltage(out, c);
+ }
+ {
+ float out = _steps[step]->bParam.getValue();
+ out += _rangeOffset;
+ out *= _rangeScale;
+ outputs[B_OUTPUT].setChannels(_channels);
+ outputs[B_OUTPUT].setVoltage(out, c);
+ }
+ {
+ float out = _steps[step]->cParam.getValue();
+ out += _rangeOffset;
+ out *= _rangeScale;
+ outputs[C_OUTPUT].setChannels(_channels);
+ outputs[C_OUTPUT].setVoltage(out, c);
+ }
+ {
+ float out = _steps[step]->dParam.getValue();
+ out += _rangeOffset;
+ out *= _rangeScale;
+ outputs[D_OUTPUT].setChannels(_channels);
+ outputs[D_OUTPUT].setVoltage(out, c);
+ }
+
+ if (step != _lastSteps[c]) {
+ _lastSteps[c] = step;
+ _allPulseGens[c].trigger(0.001f);
+ _steps[step]->pulseGens[c].trigger(0.001f);
+ }
+ outputs[SELECT_ALL_OUTPUT].setChannels(_channels);
+ outputs[SELECT_ALL_OUTPUT].setVoltage(_allPulseGens[c].process(_sampleTime) * 5.0f, c);
+
+ for (int i = 0; i < steps; ++i) {
+ _steps[i]->selectedOutput.setChannels(_channels);
+ _steps[i]->selectedOutput.setVoltage((_steps[i]->pulseGens[c].process(_sampleTime) || (!_selectTriggers && step == i)) * 5.0f, c);
+
+ _steps[i]->lightSum += step == i;
+ }
+
+ if (c == _channels - 1) {
+ for (int i = 0, n = _steps.size(); i < n; ++i) {
+ _steps[i]->selectedLight.value = _steps[i]->lightSum / (float)_channels;
+ }
+ }
+}
+
+void Pgmr::setSteps(std::vector<PgmrStep*>& steps) {
+ std::lock_guard<std::mutex> lock(_stepsLock);
+ _steps = steps;
+}
+
+
+struct PgmrWidget : AddressableSequenceBaseModuleWidget {
+ static constexpr int hp = 15;
+
+ PgmrWidget(Pgmr* module) {
+ setModule(module);
+ box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
+
+ {
+ SvgPanel *panel = new SvgPanel();
+ panel->box.size = box.size;
+ panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Pgmr.svg")));
+ addChild(panel);
+ }
+
+ addChild(createWidget<ScrewSilver>(Vec(15, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(15, 365)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 30, 365)));
+
+ // generated by svg_widgets.rb
+ auto directionParamPosition = Vec(28.0, 263.7);
+ auto selectOnClockParamPosition = Vec(28.0, 277.2);
+ auto cva1ParamPosition = Vec(54.5, 40.5);
+ auto cvb1ParamPosition = Vec(54.5, 94.5);
+ auto cvc1ParamPosition = Vec(54.5, 148.5);
+ auto cvd1ParamPosition = Vec(54.5, 202.5);
+ auto select1ParamPosition = Vec(58.5, 267.0);
+ auto cva2ParamPosition = Vec(99.5, 40.5);
+ auto cvb2ParamPosition = Vec(99.5, 94.5);
+ auto cvc2ParamPosition = Vec(99.5, 148.5);
+ auto cvd2ParamPosition = Vec(99.5, 202.5);
+ auto select2ParamPosition = Vec(103.5, 267.0);
+ auto cva3ParamPosition = Vec(144.5, 40.5);
+ auto cvb3ParamPosition = Vec(144.5, 94.5);
+ auto cvc3ParamPosition = Vec(144.5, 148.5);
+ auto cvd3ParamPosition = Vec(144.5, 202.5);
+ auto select3ParamPosition = Vec(148.5, 267.0);
+ auto cva4ParamPosition = Vec(189.5, 40.5);
+ auto cvb4ParamPosition = Vec(189.5, 94.5);
+ auto cvc4ParamPosition = Vec(189.5, 148.5);
+ auto cvd4ParamPosition = Vec(189.5, 202.5);
+ auto select4ParamPosition = Vec(193.5, 267.0);
+
+ auto clockInputPosition = Vec(10.5, 226.0);
+ auto selectInputPosition = Vec(10.5, 290.0);
+ auto select1InputPosition = Vec(55.5, 290.0);
+ auto select2InputPosition = Vec(100.5, 290.0);
+ auto select3InputPosition = Vec(145.5, 290.0);
+ auto select4InputPosition = Vec(190.5, 290.0);
+
+ auto aOutputPosition = Vec(10.5, 59.0);
+ auto bOutputPosition = Vec(10.5, 94.0);
+ auto cOutputPosition = Vec(10.5, 129.0);
+ auto dOutputPosition = Vec(10.5, 163.0);
+ auto selectAllOutputPosition = Vec(10.5, 330.0);
+ auto select1OutputPosition = Vec(55.5, 330.0);
+ auto select2OutputPosition = Vec(100.5, 330.0);
+ auto select3OutputPosition = Vec(145.5, 330.0);
+ auto select4OutputPosition = Vec(190.5, 330.0);
+
+ auto select1LightPosition = Vec(64.3, 255.0);
+ auto select2LightPosition = Vec(109.3, 255.0);
+ auto select3LightPosition = Vec(154.3, 255.0);
+ auto select4LightPosition = Vec(199.3, 255.0);
+ // end generated by svg_widgets.rb
+
+ addParam(createParam<IndicatorButtonGreen9>(directionParamPosition, module, Pgmr::DIRECTION_PARAM));
+ addParam(createParam<IndicatorButtonGreen9>(selectOnClockParamPosition, module, Pgmr::SELECT_ON_CLOCK_PARAM));
+ addParam(createParam<Knob26>(cva1ParamPosition, module, Pgmr::CVA1_PARAM));
+ addParam(createParam<Knob26>(cvb1ParamPosition, module, Pgmr::CVB1_PARAM));
+ addParam(createParam<Knob26>(cvc1ParamPosition, module, Pgmr::CVC1_PARAM));
+ addParam(createParam<Knob26>(cvd1ParamPosition, module, Pgmr::CVD1_PARAM));
+ addParam(createParam<Button18>(select1ParamPosition, module, Pgmr::SELECT1_PARAM));
+ addParam(createParam<Knob26>(cva2ParamPosition, module, Pgmr::CVA2_PARAM));
+ addParam(createParam<Knob26>(cvb2ParamPosition, module, Pgmr::CVB2_PARAM));
+ addParam(createParam<Knob26>(cvc2ParamPosition, module, Pgmr::CVC2_PARAM));
+ addParam(createParam<Knob26>(cvd2ParamPosition, module, Pgmr::CVD2_PARAM));
+ addParam(createParam<Button18>(select2ParamPosition, module, Pgmr::SELECT2_PARAM));
+ addParam(createParam<Knob26>(cva3ParamPosition, module, Pgmr::CVA3_PARAM));
+ addParam(createParam<Knob26>(cvb3ParamPosition, module, Pgmr::CVB3_PARAM));
+ addParam(createParam<Knob26>(cvc3ParamPosition, module, Pgmr::CVC3_PARAM));
+ addParam(createParam<Knob26>(cvd3ParamPosition, module, Pgmr::CVD3_PARAM));
+ addParam(createParam<Button18>(select3ParamPosition, module, Pgmr::SELECT3_PARAM));
+ addParam(createParam<Knob26>(cva4ParamPosition, module, Pgmr::CVA4_PARAM));
+ addParam(createParam<Knob26>(cvb4ParamPosition, module, Pgmr::CVB4_PARAM));
+ addParam(createParam<Knob26>(cvc4ParamPosition, module, Pgmr::CVC4_PARAM));
+ addParam(createParam<Knob26>(cvd4ParamPosition, module, Pgmr::CVD4_PARAM));
+ addParam(createParam<Button18>(select4ParamPosition, module, Pgmr::SELECT4_PARAM));
+
+ addInput(createInput<Port24>(clockInputPosition, module, Pgmr::CLOCK_INPUT));
+ addInput(createInput<Port24>(selectInputPosition, module, Pgmr::SELECT_INPUT));
+ addInput(createInput<Port24>(select1InputPosition, module, Pgmr::SELECT1_INPUT));
+ addInput(createInput<Port24>(select2InputPosition, module, Pgmr::SELECT2_INPUT));
+ addInput(createInput<Port24>(select3InputPosition, module, Pgmr::SELECT3_INPUT));
+ addInput(createInput<Port24>(select4InputPosition, module, Pgmr::SELECT4_INPUT));
+
+ addOutput(createOutput<Port24>(aOutputPosition, module, Pgmr::A_OUTPUT));
+ addOutput(createOutput<Port24>(bOutputPosition, module, Pgmr::B_OUTPUT));
+ addOutput(createOutput<Port24>(cOutputPosition, module, Pgmr::C_OUTPUT));
+ addOutput(createOutput<Port24>(dOutputPosition, module, Pgmr::D_OUTPUT));
+ addOutput(createOutput<Port24>(selectAllOutputPosition, module, Pgmr::SELECT_ALL_OUTPUT));
+ addOutput(createOutput<Port24>(select1OutputPosition, module, Pgmr::SELECT1_OUTPUT));
+ addOutput(createOutput<Port24>(select2OutputPosition, module, Pgmr::SELECT2_OUTPUT));
+ addOutput(createOutput<Port24>(select3OutputPosition, module, Pgmr::SELECT3_OUTPUT));
+ addOutput(createOutput<Port24>(select4OutputPosition, module, Pgmr::SELECT4_OUTPUT));
+
+ addChild(createLight<SmallLight<GreenLight>>(select1LightPosition, module, Pgmr::SELECT1_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select2LightPosition, module, Pgmr::SELECT2_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select3LightPosition, module, Pgmr::SELECT3_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select4LightPosition, module, Pgmr::SELECT4_LIGHT));
+ }
+
+ struct RangeOptionMenuItem : OptionMenuItem {
+ RangeOptionMenuItem(Pgmr* module, const char* label, float offset, float scale)
+ : OptionMenuItem(
+ label,
+ [=]() { return module->_rangeOffset == offset && module->_rangeScale == scale; },
+ [=]() {
+ module->_rangeOffset = offset;
+ module->_rangeScale = scale;
+ }
+ )
+ {}
+ };
+
+ void appendContextMenu(Menu* menu) override {
+ AddressableSequenceBaseModuleWidget::appendContextMenu(menu);
+
+ auto m = dynamic_cast<Pgmr*>(module);
+ assert(m);
+ OptionsMenuItem* mi = new OptionsMenuItem("Range");
+ mi->addItem(RangeOptionMenuItem(m, "+/-10V", 0.0f, 10.0f));
+ mi->addItem(RangeOptionMenuItem(m, "+/-5V", 0.0f, 5.0f));
+ mi->addItem(RangeOptionMenuItem(m, "+/-3V", 0.0f, 3.0f));
+ mi->addItem(RangeOptionMenuItem(m, "+/-1V", 0.0f, 1.0f));
+ OptionsMenuItem::addToMenu(mi, menu);
+
+ OptionsMenuItem* so = new OptionsMenuItem("Step-selected output");
+ so->addItem(OptionMenuItem("Gate", [m]() { return !m->_selectTriggers; }, [m]() { m->_selectTriggers = false; }));
+ so->addItem(OptionMenuItem("Trigger", [m]() { return m->_selectTriggers; }, [m]() { m->_selectTriggers = true; }));
+ OptionsMenuItem::addToMenu(so, menu);
+ }
+};
+
+Model* modelPgmr = createModel<Pgmr, PgmrWidget>("Bogaudio-Pgmr", "PGMR", "4-step programmer and sequencer", "Sequencer", "Polyphonic");
+
+
+float PgmrX::OutputParamQuantity::getDisplayValue() {
+ float v = getValue();
+ if (!module) {
+ return v;
+ }
+
+ auto m = dynamic_cast<PgmrX*>(module);
+ v += m->_rangeOffset;
+ v *= m->_rangeScale;
+ return v;
+}
+
+void PgmrX::OutputParamQuantity::setDisplayValue(float v) {
+ if (!module) {
+ return;
+ }
+
+ auto m = dynamic_cast<PgmrX*>(module);
+ v /= m->_rangeScale;
+ v -= m->_rangeOffset;
+ setValue(v);
+}
+
+void PgmrX::processAlways(const ProcessArgs& args) {
+ int position = 0;
+ int baseID = 0;
+ if (baseConnected()) {
+ PgmrExpanderMessage* bm = fromBase();
+ baseID = bm->baseID;
+ position = bm->position;
+ _rangeOffset = bm->rangeOffset;
+ _rangeScale = bm->rangeScale;
+ }
+
+ if (_registered && (position <= 0 || position != _position)) {
+ PgmrRegistry::registry().deregisterExpander(_baseID, _position);
+ _registered = false;
+ _baseID = 0;
+ _position = 0;
+ }
+ else if (!_registered && position > 0) {
+ _registered = true;
+ _baseID = baseID;
+ _position = position;
+ PgmrRegistry::registry().registerExpander(_baseID, _position, *this);
+ }
+
+ if (_position < 1) {
+ for (int i = 0; i < 4; ++i) {
+ _localSteps[i]->selectedLight.value = 0.0f;
+ }
+ }
+ if (expanderConnected()) {
+ PgmrExpanderMessage* te = toExpander();
+ te->baseID = _baseID;
+ te->position = _position > 0 ? _position + 1 : 0;
+ te->rangeOffset = _rangeOffset;
+ te->rangeScale = _rangeScale;
+ }
+}
+
+struct PgmrXWidget : ModuleWidget {
+ static constexpr int hp = 12;
+
+ PgmrXWidget(PgmrX* module) {
+ setModule(module);
+ box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
+
+ {
+ SvgPanel *panel = new SvgPanel();
+ panel->box.size = box.size;
+ panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/PgmrX.svg")));
+ addChild(panel);
+ }
+
+ addChild(createWidget<ScrewSilver>(Vec(0, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 0)));
+ addChild(createWidget<ScrewSilver>(Vec(0, 365)));
+ addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365)));
+
+ // generated by svg_widgets.rb
+ auto cva1ParamPosition = Vec(9.5, 40.5);
+ auto cvb1ParamPosition = Vec(9.5, 94.5);
+ auto cvc1ParamPosition = Vec(9.5, 148.5);
+ auto cvd1ParamPosition = Vec(9.5, 202.5);
+ auto select1ParamPosition = Vec(13.5, 267.0);
+ auto cva2ParamPosition = Vec(54.5, 40.5);
+ auto cvb2ParamPosition = Vec(54.5, 94.5);
+ auto cvc2ParamPosition = Vec(54.5, 148.5);
+ auto cvd2ParamPosition = Vec(54.5, 202.5);
+ auto select2ParamPosition = Vec(58.5, 267.0);
+ auto cva3ParamPosition = Vec(99.5, 40.5);
+ auto cvb3ParamPosition = Vec(99.5, 94.5);
+ auto cvc3ParamPosition = Vec(99.5, 148.5);
+ auto cvd3ParamPosition = Vec(99.5, 202.5);
+ auto select3ParamPosition = Vec(103.5, 267.0);
+ auto cva4ParamPosition = Vec(144.5, 40.5);
+ auto cvb4ParamPosition = Vec(144.5, 94.5);
+ auto cvc4ParamPosition = Vec(144.5, 148.5);
+ auto cvd4ParamPosition = Vec(144.5, 202.5);
+ auto select4ParamPosition = Vec(148.5, 267.0);
+
+ auto select1InputPosition = Vec(10.5, 290.0);
+ auto select2InputPosition = Vec(55.5, 290.0);
+ auto select3InputPosition = Vec(100.5, 290.0);
+ auto select4InputPosition = Vec(145.5, 290.0);
+
+ auto select1OutputPosition = Vec(10.5, 330.0);
+ auto select2OutputPosition = Vec(55.5, 330.0);
+ auto select3OutputPosition = Vec(100.5, 330.0);
+ auto select4OutputPosition = Vec(145.5, 330.0);
+
+ auto select1LightPosition = Vec(19.3, 255.0);
+ auto select2LightPosition = Vec(64.3, 255.0);
+ auto select3LightPosition = Vec(109.3, 255.0);
+ auto select4LightPosition = Vec(154.3, 255.0);
+ // end generated by svg_widgets.rb
+
+ addParam(createParam<Knob26>(cva1ParamPosition, module, PgmrX::CVA1_PARAM));
+ addParam(createParam<Knob26>(cvb1ParamPosition, module, PgmrX::CVB1_PARAM));
+ addParam(createParam<Knob26>(cvc1ParamPosition, module, PgmrX::CVC1_PARAM));
+ addParam(createParam<Knob26>(cvd1ParamPosition, module, PgmrX::CVD1_PARAM));
+ addParam(createParam<Button18>(select1ParamPosition, module, PgmrX::SELECT1_PARAM));
+ addParam(createParam<Knob26>(cva2ParamPosition, module, PgmrX::CVA2_PARAM));
+ addParam(createParam<Knob26>(cvb2ParamPosition, module, PgmrX::CVB2_PARAM));
+ addParam(createParam<Knob26>(cvc2ParamPosition, module, PgmrX::CVC2_PARAM));
+ addParam(createParam<Knob26>(cvd2ParamPosition, module, PgmrX::CVD2_PARAM));
+ addParam(createParam<Button18>(select2ParamPosition, module, PgmrX::SELECT2_PARAM));
+ addParam(createParam<Knob26>(cva3ParamPosition, module, PgmrX::CVA3_PARAM));
+ addParam(createParam<Knob26>(cvb3ParamPosition, module, PgmrX::CVB3_PARAM));
+ addParam(createParam<Knob26>(cvc3ParamPosition, module, PgmrX::CVC3_PARAM));
+ addParam(createParam<Knob26>(cvd3ParamPosition, module, PgmrX::CVD3_PARAM));
+ addParam(createParam<Button18>(select3ParamPosition, module, PgmrX::SELECT3_PARAM));
+ addParam(createParam<Knob26>(cva4ParamPosition, module, PgmrX::CVA4_PARAM));
+ addParam(createParam<Knob26>(cvb4ParamPosition, module, PgmrX::CVB4_PARAM));
+ addParam(createParam<Knob26>(cvc4ParamPosition, module, PgmrX::CVC4_PARAM));
+ addParam(createParam<Knob26>(cvd4ParamPosition, module, PgmrX::CVD4_PARAM));
+ addParam(createParam<Button18>(select4ParamPosition, module, PgmrX::SELECT4_PARAM));
+
+ addInput(createInput<Port24>(select1InputPosition, module, PgmrX::SELECT1_INPUT));
+ addInput(createInput<Port24>(select2InputPosition, module, PgmrX::SELECT2_INPUT));
+ addInput(createInput<Port24>(select3InputPosition, module, PgmrX::SELECT3_INPUT));
+ addInput(createInput<Port24>(select4InputPosition, module, PgmrX::SELECT4_INPUT));
+
+ addOutput(createOutput<Port24>(select1OutputPosition, module, PgmrX::SELECT1_OUTPUT));
+ addOutput(createOutput<Port24>(select2OutputPosition, module, PgmrX::SELECT2_OUTPUT));
+ addOutput(createOutput<Port24>(select3OutputPosition, module, PgmrX::SELECT3_OUTPUT));
+ addOutput(createOutput<Port24>(select4OutputPosition, module, PgmrX::SELECT4_OUTPUT));
+
+ addChild(createLight<SmallLight<GreenLight>>(select1LightPosition, module, PgmrX::SELECT1_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select2LightPosition, module, PgmrX::SELECT2_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select3LightPosition, module, PgmrX::SELECT3_LIGHT));
+ addChild(createLight<SmallLight<GreenLight>>(select4LightPosition, module, PgmrX::SELECT4_LIGHT));
+ }
+};
+
+Model* modelPgmrX = createModel<PgmrX, PgmrXWidget>("Bogaudio-PgmrX", "PGMRX", "4-step chainable expander for PGMR", "Sequencer", "Expander", "Polyphonic");
diff --git a/src/Pgmr.hpp b/src/Pgmr.hpp
@@ -0,0 +1,303 @@
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include "bogaudio.hpp"
+#include "addressable_sequence.hpp"
+#include "expanders.hpp"
+
+extern Model* modelPgmr;
+extern Model* modelPgmrX;
+
+namespace bogaudio {
+
+struct PgmrExpanderMessage : ExpanderMessage {
+ int baseID = -1;
+ int position = -1;
+ float rangeOffset = 0.0f;
+ float rangeScale = 10.0f;
+};
+
+struct PgmrStep {
+ Param& aParam;
+ Param& bParam;
+ Param& cParam;
+ Param& dParam;
+ Light& selectedLight;
+ Param& selectParam;
+ Input& selectInput;
+ Output& selectedOutput;
+
+ Trigger triggers[BGModule::maxChannels];
+ rack::dsp::PulseGenerator pulseGens[BGModule::maxChannels];
+ float lightSum = 0.0f;
+
+ PgmrStep(
+ Param& aParam,
+ Param& bParam,
+ Param& cParam,
+ Param& dParam,
+ Light& selectedLight,
+ Param& selectParam,
+ Input& selectInput,
+ Output& selectedOutput
+ )
+ : aParam(aParam)
+ , bParam(bParam)
+ , cParam(cParam)
+ , dParam(dParam)
+ , selectedLight(selectedLight)
+ , selectParam(selectParam)
+ , selectInput(selectInput)
+ , selectedOutput(selectedOutput)
+ {
+ }
+
+ void reset();
+};
+
+struct PgmrBase {
+ PgmrStep* _localSteps[4] {};
+
+ virtual ~PgmrBase() {
+ for (int i = 0; i < 4; ++i) {
+ if (_localSteps[i]) {
+ delete _localSteps[i];
+ }
+ }
+ }
+};
+
+struct Pgmr;
+struct PgmrX;
+
+struct PgmrRegistry {
+private:
+ struct Base {
+ Pgmr& module;
+ std::vector<PgmrStep*> steps;
+
+ Base(Pgmr& b);
+ };
+
+ std::mutex _lock;
+ int _nextID = 1;
+ std::unordered_map<int, Base> _bases;
+
+public:
+ int registerBase(Pgmr& b);
+ void deregisterBase(int id);
+ void registerExpander(int baseID, int position, PgmrX& x);
+ void deregisterExpander(int baseID, int position);
+
+ static PgmrRegistry& registry();
+};
+
+struct Pgmr : ExpandableModule<PgmrExpanderMessage, PgmrX, OutputRangeAddressableSequenceModule>, PgmrBase {
+ enum ParamsIds {
+ DIRECTION_PARAM,
+ SELECT_ON_CLOCK_PARAM,
+ CVA1_PARAM,
+ CVB1_PARAM,
+ CVC1_PARAM,
+ CVD1_PARAM,
+ SELECT1_PARAM,
+ CVA2_PARAM,
+ CVB2_PARAM,
+ CVC2_PARAM,
+ CVD2_PARAM,
+ SELECT2_PARAM,
+ CVA3_PARAM,
+ CVB3_PARAM,
+ CVC3_PARAM,
+ CVD3_PARAM,
+ SELECT3_PARAM,
+ CVA4_PARAM,
+ CVB4_PARAM,
+ CVC4_PARAM,
+ CVD4_PARAM,
+ SELECT4_PARAM,
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ CLOCK_INPUT,
+ SELECT_INPUT,
+ SELECT1_INPUT,
+ SELECT2_INPUT,
+ SELECT3_INPUT,
+ SELECT4_INPUT,
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ A_OUTPUT,
+ B_OUTPUT,
+ C_OUTPUT,
+ D_OUTPUT,
+ SELECT_ALL_OUTPUT,
+ SELECT1_OUTPUT,
+ SELECT2_OUTPUT,
+ SELECT3_OUTPUT,
+ SELECT4_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ enum LightsIds {
+ SELECT1_LIGHT,
+ SELECT2_LIGHT,
+ SELECT3_LIGHT,
+ SELECT4_LIGHT,
+ NUM_LIGHTS
+ };
+
+ float _sampleTime = 0.001f;
+ bool _selectTriggers = false;
+ std::mutex _stepsLock;
+ std::vector<PgmrStep*> _steps;
+ int _lastSteps[maxChannels] {};
+ rack::dsp::PulseGenerator _allPulseGens[maxChannels];
+ int _id = -1;
+
+ Pgmr() {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+ configParam(DIRECTION_PARAM, 0.0f, 1.0f, 1.0f, "Forward");
+ configParam(SELECT_ON_CLOCK_PARAM, 0.0f, 1.0f, 0.0f, "Select on clock");
+ configParam<OutputParamQuantity>(CVA1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1A", " V");
+ configParam<OutputParamQuantity>(CVB1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1B", " V");
+ configParam<OutputParamQuantity>(CVC1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1C", " V");
+ configParam<OutputParamQuantity>(CVD1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1D", " V");
+ configParam(SELECT1_PARAM, 0.0f, 1.0f, 0.0f, "Select 1");
+ configParam<OutputParamQuantity>(CVA2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2A", " V");
+ configParam<OutputParamQuantity>(CVB2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2B", " V");
+ configParam<OutputParamQuantity>(CVC2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2C", " V");
+ configParam<OutputParamQuantity>(CVD2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2D", " V");
+ configParam(SELECT2_PARAM, 0.0f, 1.0f, 0.0f, "Select 2");
+ configParam<OutputParamQuantity>(CVA3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3A", " V");
+ configParam<OutputParamQuantity>(CVB3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3B", " V");
+ configParam<OutputParamQuantity>(CVC3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3C", " V");
+ configParam<OutputParamQuantity>(CVD3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3D", " V");
+ configParam(SELECT3_PARAM, 0.0f, 1.0f, 0.0f, "Select 3");
+ configParam<OutputParamQuantity>(CVA4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4A", " V");
+ configParam<OutputParamQuantity>(CVB4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4B", " V");
+ configParam<OutputParamQuantity>(CVC4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4C", " V");
+ configParam<OutputParamQuantity>(CVD4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4D", " V");
+ configParam(SELECT4_PARAM, 0.0f, 1.0f, 0.0f, "Select 4");
+ setInputIDs(CLOCK_INPUT, SELECT_INPUT);
+
+ _localSteps[0] = new PgmrStep(params[CVA1_PARAM], params[CVB1_PARAM], params[CVC1_PARAM], params[CVD1_PARAM], lights[SELECT1_LIGHT], params[SELECT1_PARAM], inputs[SELECT1_INPUT], outputs[SELECT1_OUTPUT]);
+ _localSteps[1] = new PgmrStep(params[CVA2_PARAM], params[CVB2_PARAM], params[CVC2_PARAM], params[CVD2_PARAM], lights[SELECT2_LIGHT], params[SELECT2_PARAM], inputs[SELECT2_INPUT], outputs[SELECT2_OUTPUT]);
+ _localSteps[2] = new PgmrStep(params[CVA3_PARAM], params[CVB3_PARAM], params[CVC3_PARAM], params[CVD3_PARAM], lights[SELECT3_LIGHT], params[SELECT3_PARAM], inputs[SELECT3_INPUT], outputs[SELECT3_OUTPUT]);
+ _localSteps[3] = new PgmrStep(params[CVA4_PARAM], params[CVB4_PARAM], params[CVC4_PARAM], params[CVD4_PARAM], lights[SELECT4_LIGHT], params[SELECT4_PARAM], inputs[SELECT4_INPUT], outputs[SELECT4_OUTPUT]);
+ _id = PgmrRegistry::registry().registerBase(*this);
+ }
+ virtual ~Pgmr() {
+ PgmrRegistry::registry().deregisterBase(_id);
+ }
+
+ void reset() override;
+ void sampleRateChange() override;
+ json_t* dataToJson() override;
+ void dataFromJson(json_t* root) override;
+ void modulate() override;
+ void processAlways(const ProcessArgs& args) override;
+ void processChannel(const ProcessArgs& args, int c) override;
+ void setSteps(std::vector<PgmrStep*>& steps);
+};
+
+struct PgmrX : ExpanderModule<PgmrExpanderMessage, PgmrBase, ExpandableModule<PgmrExpanderMessage, PgmrX, BGModule>>, PgmrBase {
+ enum ParamsIds {
+ CVA1_PARAM,
+ CVB1_PARAM,
+ CVC1_PARAM,
+ CVD1_PARAM,
+ SELECT1_PARAM,
+ CVA2_PARAM,
+ CVB2_PARAM,
+ CVC2_PARAM,
+ CVD2_PARAM,
+ SELECT2_PARAM,
+ CVA3_PARAM,
+ CVB3_PARAM,
+ CVC3_PARAM,
+ CVD3_PARAM,
+ SELECT3_PARAM,
+ CVA4_PARAM,
+ CVB4_PARAM,
+ CVC4_PARAM,
+ CVD4_PARAM,
+ SELECT4_PARAM,
+ NUM_PARAMS
+ };
+
+ enum InputsIds {
+ SELECT1_INPUT,
+ SELECT2_INPUT,
+ SELECT3_INPUT,
+ SELECT4_INPUT,
+ NUM_INPUTS
+ };
+
+ enum OutputsIds {
+ SELECT1_OUTPUT,
+ SELECT2_OUTPUT,
+ SELECT3_OUTPUT,
+ SELECT4_OUTPUT,
+ NUM_OUTPUTS
+ };
+
+ enum LightsIds {
+ SELECT1_LIGHT,
+ SELECT2_LIGHT,
+ SELECT3_LIGHT,
+ SELECT4_LIGHT,
+ NUM_LIGHTS
+ };
+
+ struct OutputParamQuantity : ParamQuantity {
+ float getDisplayValue() override;
+ void setDisplayValue(float v) override;
+ };
+
+ bool _registered = false;
+ int _baseID = -1;
+ int _position = -1;
+ float _rangeOffset = 0.0f;
+ float _rangeScale = 10.0f;
+
+ PgmrX() {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+ configParam<OutputParamQuantity>(CVA1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1A", " V");
+ configParam<OutputParamQuantity>(CVB1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1B", " V");
+ configParam<OutputParamQuantity>(CVC1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1C", " V");
+ configParam<OutputParamQuantity>(CVD1_PARAM, -1.0f, 1.0f, 0.0f, "Step 1D", " V");
+ configParam(SELECT1_PARAM, 0.0f, 1.0f, 0.0f, "Select 1");
+ configParam<OutputParamQuantity>(CVA2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2A", " V");
+ configParam<OutputParamQuantity>(CVB2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2B", " V");
+ configParam<OutputParamQuantity>(CVC2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2C", " V");
+ configParam<OutputParamQuantity>(CVD2_PARAM, -1.0f, 1.0f, 0.0f, "Step 2D", " V");
+ configParam(SELECT2_PARAM, 0.0f, 1.0f, 0.0f, "Select 2");
+ configParam<OutputParamQuantity>(CVA3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3A", " V");
+ configParam<OutputParamQuantity>(CVB3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3B", " V");
+ configParam<OutputParamQuantity>(CVC3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3C", " V");
+ configParam<OutputParamQuantity>(CVD3_PARAM, -1.0f, 1.0f, 0.0f, "Step 3D", " V");
+ configParam(SELECT3_PARAM, 0.0f, 1.0f, 0.0f, "Select 3");
+ configParam<OutputParamQuantity>(CVA4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4A", " V");
+ configParam<OutputParamQuantity>(CVB4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4B", " V");
+ configParam<OutputParamQuantity>(CVC4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4C", " V");
+ configParam<OutputParamQuantity>(CVD4_PARAM, -1.0f, 1.0f, 0.0f, "Step 4D", " V");
+ configParam<OutputParamQuantity>(SELECT4_PARAM, 0.0f, 1.0f, 0.0f, "Select 4");
+ _localSteps[0] = new PgmrStep(params[CVA1_PARAM], params[CVB1_PARAM], params[CVC1_PARAM], params[CVD1_PARAM], lights[SELECT1_LIGHT], params[SELECT1_PARAM], inputs[SELECT1_INPUT], outputs[SELECT1_OUTPUT]);
+ _localSteps[1] = new PgmrStep(params[CVA2_PARAM], params[CVB2_PARAM], params[CVC2_PARAM], params[CVD2_PARAM], lights[SELECT2_LIGHT], params[SELECT2_PARAM], inputs[SELECT2_INPUT], outputs[SELECT2_OUTPUT]);
+ _localSteps[2] = new PgmrStep(params[CVA3_PARAM], params[CVB3_PARAM], params[CVC3_PARAM], params[CVD3_PARAM], lights[SELECT3_LIGHT], params[SELECT3_PARAM], inputs[SELECT3_INPUT], outputs[SELECT3_OUTPUT]);
+ _localSteps[3] = new PgmrStep(params[CVA4_PARAM], params[CVB4_PARAM], params[CVC4_PARAM], params[CVD4_PARAM], lights[SELECT4_LIGHT], params[SELECT4_PARAM], inputs[SELECT4_INPUT], outputs[SELECT4_OUTPUT]);
+ }
+ virtual ~PgmrX() {
+ PgmrRegistry::registry().deregisterExpander(_baseID, _position);
+ }
+
+ void processAlways(const ProcessArgs& args) override;
+};
+
+} // namespace bogaudio
diff --git a/src/TestExpander.cpp b/src/TestExpander.cpp
@@ -7,11 +7,11 @@ int TestExpanderBase::channels() {
void TestExpanderBase::processAll(const ProcessArgs& args) {
outputs[OUT_OUTPUT].setChannels(_channels);
- lights[COM_LIGHT].value = connected();
+ lights[COM_LIGHT].value = expanderConnected();
}
void TestExpanderBase::processChannel(const ProcessArgs& args, int c) {
- if (connected()) {
+ if (expanderConnected()) {
toExpander()->sample[c] = inputs[IN_INPUT].getPolyVoltage(c);
outputs[OUT_OUTPUT].setVoltage(fromExpander()->sample[c], c);
}
@@ -58,11 +58,11 @@ Model* modelTestExpanderBase = createModel<TestExpanderBase, TestExpanderBaseWid
void TestExpanderExtension::processAll(const ProcessArgs& args) {
outputs[OUT_OUTPUT].setChannels(_channels);
- lights[COM_LIGHT].value = connected();
+ lights[COM_LIGHT].value = baseConnected();
}
void TestExpanderExtension::processChannel(const ProcessArgs& args, int c) {
- if (connected()) {
+ if (baseConnected()) {
float sample = fromBase()->sample[c];
toBase()->sample[c] = -sample;
outputs[OUT_OUTPUT].setVoltage(sample, c);
diff --git a/src/TestExpander.hpp b/src/TestExpander.hpp
@@ -14,7 +14,7 @@ struct TestExpanderMessage : ExpanderMessage {
struct TestExpanderExtension;
-struct TestExpanderBase : ExpandableModule<TestExpanderMessage, TestExpanderExtension> {
+struct TestExpanderBase : ExpandableModule<TestExpanderMessage, TestExpanderExtension, BGModule> {
enum ParamsIds {
NUM_PARAMS
};
@@ -43,7 +43,7 @@ struct TestExpanderBase : ExpandableModule<TestExpanderMessage, TestExpanderExte
void processChannel(const ProcessArgs& args, int c) override;
};
-struct TestExpanderExtension : ExpanderModule<TestExpanderMessage, TestExpanderBase> {
+struct TestExpanderExtension : ExpanderModule<TestExpanderMessage, TestExpanderBase, BGModule> {
enum ParamsIds {
NUM_PARAMS
};
diff --git a/src/addressable_sequence.cpp b/src/addressable_sequence.cpp
@@ -48,32 +48,45 @@ void AddressableSequenceModule::dataFromJson(json_t* root) {
}
int AddressableSequenceModule::channels() {
+ assert(_polyInputID >= 0);
+ assert(_clockInputID >= 0);
+ assert(_selectInputID >= 0);
return _polyInputID == _selectInputID ? inputs[_selectInputID].getChannels() : inputs[_clockInputID].getChannels();
}
int AddressableSequenceModule::nextStep(
int c,
- Input& resetInput,
+ Input* resetInput,
Input& clockInput,
- Param& stepsParam,
+ Param* stepsParam,
Param& directionParam,
- Param& selectParam,
- Input& selectInput
+ Param* selectParam,
+ Input& selectInput,
+ int n
) {
- bool reset = _reset[c].process(resetInput.getVoltage());
- if (reset) {
- _timer[c].reset();
+ bool reset = false;
+ if (resetInput) {
+ reset = _reset[c].process(resetInput->getPolyVoltage(c));
+ if (reset) {
+ _timer[c].reset();
+ }
}
bool timer = _timer[c].next();
bool clock = _clock[c].process(clockInput.getPolyVoltage(c)) && !timer;
- int steps = clamp(stepsParam.getValue(), 1.0f, 8.0f);
+ int steps = n;
+ if (stepsParam) {
+ steps = clamp(stepsParam->getValue(), 1.0f, (float)n);
+ }
int reverse = 1 - 2 * (directionParam.getValue() == 0.0f);
_step[c] = (_step[c] + reverse * clock) % steps;
_step[c] += (_step[c] < 0) * steps;
_step[c] -= _step[c] * reset;
- float select = selectParam.getValue();
+ float select = 0;
+ if (selectParam) {
+ select = clamp(selectParam->getValue(), 0.0f, (float)(n - 1));
+ }
if (_triggeredSelect) {
if (_selectTrigger[c].process(selectInput.getPolyVoltage(c))) {
_select[c] = (1 + (int)_select[c]) % ((int)select + 1);
@@ -81,10 +94,62 @@ int AddressableSequenceModule::nextStep(
_select[c] -= _select[c] * reset;
}
else {
- select += clamp(selectInput.getPolyVoltage(c), 0.0f, 10.0f) * 0.1f * 8.0f;
+ select += clamp(selectInput.getPolyVoltage(c), 0.0f, 9.99f) * 0.1f * (float)n;
if (!_selectOnClock || clock) {
_select[c] = select;
}
}
- return (_step[c] + (int)_select[c]) % 8;
+ return (_step[c] + (int)_select[c]) % n;
+}
+
+int AddressableSequenceModule::setStep(int c, int i, int n) {
+ return _step[c] = i % n;
+}
+
+
+float OutputRangeAddressableSequenceModule::OutputParamQuantity::getDisplayValue() {
+ float v = getValue();
+ if (!module) {
+ return v;
+ }
+
+ auto m = dynamic_cast<OutputRangeAddressableSequenceModule*>(module);
+ v += m->_rangeOffset;
+ v *= m->_rangeScale;
+ return v;
+}
+
+void OutputRangeAddressableSequenceModule::OutputParamQuantity::setDisplayValue(float v) {
+ if (!module) {
+ return;
+ }
+
+ auto m = dynamic_cast<OutputRangeAddressableSequenceModule*>(module);
+ v /= m->_rangeScale;
+ v -= m->_rangeOffset;
+ setValue(v);
+}
+
+#define RANGE_OFFSET "range_offset"
+#define RANGE_SCALE "range_scale"
+
+json_t* OutputRangeAddressableSequenceModule::dataToJson() {
+ json_t* root = AddressableSequenceModule::dataToJson();
+ json_object_set_new(root, RANGE_OFFSET, json_real(_rangeOffset));
+ json_object_set_new(root, RANGE_SCALE, json_real(_rangeScale));
+ return root;
+}
+
+void OutputRangeAddressableSequenceModule::dataFromJson(json_t* root) {
+ AddressableSequenceModule::dataFromJson(root);
+
+ json_t* ro = json_object_get(root, RANGE_OFFSET);
+ if (ro) {
+ _rangeOffset = json_real_value(ro);
+ }
+
+ json_t* rs = json_object_get(root, RANGE_SCALE);
+ if (rs) {
+ _rangeScale = json_real_value(rs);
+ }
}
diff --git a/src/addressable_sequence.hpp b/src/addressable_sequence.hpp
@@ -8,9 +8,9 @@ using namespace rack;
namespace bogaudio {
struct AddressableSequenceModule : BGModule {
- int _polyInputID;
- int _clockInputID;
- int _selectInputID;
+ int _polyInputID = -1;
+ int _clockInputID = -1;
+ int _selectInputID = -1;
Trigger _clock[maxChannels];
Trigger _reset[maxChannels];
Trigger _selectTrigger[maxChannels];
@@ -20,13 +20,11 @@ struct AddressableSequenceModule : BGModule {
bool _selectOnClock = false;
bool _triggeredSelect = false;
- AddressableSequenceModule(int clockInputID, int selectInputID)
- : _polyInputID(clockInputID)
- , _clockInputID(clockInputID)
- , _selectInputID(selectInputID)
- {
+ void setInputIDs(int clockInputID, int selectInputID) {
+ _polyInputID = clockInputID;
+ _clockInputID = clockInputID;
+ _selectInputID = selectInputID;
}
-
void reset() override;
void sampleRateChange() override;
json_t* dataToJson() override;
@@ -34,16 +32,18 @@ struct AddressableSequenceModule : BGModule {
int channels() override;
int nextStep(
int c,
- Input& resetInput,
+ Input* resetInput,
Input& clockInput,
- Param& stepsParam,
+ Param* stepsParam,
Param& directionParam,
- Param& selectParam,
- Input& selectInput
+ Param* selectParam,
+ Input& selectInput,
+ int n = 8
);
+ int setStep(int c, int i, int n = 8);
};
-struct AddressableSequenceModuleWidget : ModuleWidget {
+struct AddressableSequenceBaseModuleWidget : ModuleWidget {
void appendContextMenu(Menu* menu) override {
AddressableSequenceModule* m = dynamic_cast<AddressableSequenceModule*>(module);
assert(m);
@@ -53,9 +53,31 @@ struct AddressableSequenceModuleWidget : ModuleWidget {
p->addItem(OptionMenuItem("CLOCK input", [m]() { return m->_polyInputID == m->_clockInputID; }, [m]() { m->_polyInputID = m->_clockInputID; }));
p->addItem(OptionMenuItem("SELECT input", [m]() { return m->_polyInputID == m->_selectInputID; }, [m]() { m->_polyInputID = m->_selectInputID; }));
OptionsMenuItem::addToMenu(p, menu);
+ }
+};
+
+struct AddressableSequenceModuleWidget : AddressableSequenceBaseModuleWidget {
+ void appendContextMenu(Menu* menu) override {
+ AddressableSequenceBaseModuleWidget::appendContextMenu(menu);
+
+ AddressableSequenceModule* m = dynamic_cast<AddressableSequenceModule*>(module);
+ assert(m);
menu->addChild(new BoolOptionMenuItem("Select on clock mode", [m]() { return &m->_selectOnClock; }));
menu->addChild(new BoolOptionMenuItem("Triggered select mode", [m]() { return &m->_triggeredSelect; }));
}
};
+struct OutputRangeAddressableSequenceModule : AddressableSequenceModule {
+ float _rangeOffset = 0.0f;
+ float _rangeScale = 10.0f;
+
+ struct OutputParamQuantity : ParamQuantity {
+ float getDisplayValue() override;
+ void setDisplayValue(float v) override;
+ };
+
+ json_t* dataToJson() override;
+ void dataFromJson(json_t* root) override;
+};
+
} // namespace bogaudio
diff --git a/src/bogaudio.cpp b/src/bogaudio.cpp
@@ -51,6 +51,7 @@
#include "Offset.hpp"
#include "OneEight.hpp"
#include "Pan.hpp"
+#include "Pgmr.hpp"
#include "PolyCon.hpp"
#include "PolyMult.hpp"
#include "Pressor.hpp"
@@ -156,6 +157,8 @@ void init(rack::Plugin *p) {
p->addModel(modelOneEight);
p->addModel(modelEightOne);
p->addModel(modelAddrSeq);
+ p->addModel(modelPgmr);
+ p->addModel(modelPgmrX);
p->addModel(modelAnalyzer);
p->addModel(modelAnalyzerXL);
diff --git a/src/expanders.hpp b/src/expanders.hpp
@@ -15,85 +15,97 @@ struct ExpanderMessage {
virtual ~ExpanderMessage() {}
};
-template<class MSG, class EM>
-struct ExpandableModule : BGModule {
+template<class MSG, class EM, class BASE>
+struct ExpandableModule : BASE {
MSG _messages[2] {};
+ bool _wasConnected = false;
ExpandableModule() {
static_assert(std::is_base_of<ExpanderMessage, MSG>::value, "type parameter MSG must derive from ExpanderMessage");
+ static_assert(std::is_base_of<BGModule, BASE>::value, "type parameter BASE must derive from BGModule");
- rightExpander.producerMessage = &_messages[0];
- rightExpander.consumerMessage = &_messages[1];
+ BGModule::rightExpander.producerMessage = &_messages[0];
+ BGModule::rightExpander.consumerMessage = &_messages[1];
}
- inline bool connected() {
- return rightExpander.module && dynamic_cast<EM*>(rightExpander.module);
+ bool expanderConnected() {
+ bool connected = BGModule::rightExpander.module && dynamic_cast<EM*>(BGModule::rightExpander.module);
+ if (!connected && _wasConnected) {
+ _messages[1] = _messages[0] = MSG();
+ }
+ return _wasConnected = connected;
}
- inline MSG* toExpander() {
- assert(connected());
- MSG* m = (MSG*)rightExpander.module->leftExpander.producerMessage;
+ MSG* toExpander() {
+ assert(expanderConnected());
+ MSG* m = (MSG*)BGModule::rightExpander.module->BGModule::leftExpander.producerMessage;
assert(m);
- m->channels = _channels;
+ m->channels = BGModule::_channels;
return m;
}
- inline MSG* fromExpander() {
- assert(connected());
- MSG* m = (MSG*)rightExpander.consumerMessage;
+ MSG* fromExpander() {
+ assert(expanderConnected());
+ MSG* m = (MSG*)BGModule::rightExpander.consumerMessage;
assert(m);
return m;
}
- void process(const ProcessArgs& args) override {
- BGModule::process(args);
- if (rightExpander.module) {
- rightExpander.module->leftExpander.messageFlipRequested = true;
+ void process(const BGModule::ProcessArgs& args) override {
+ BASE::process(args);
+ if (BGModule::rightExpander.module) {
+ BGModule::rightExpander.module->leftExpander.messageFlipRequested = true;
}
}
};
// An expander must be to the right of the expanded module to work.
-template<class MSG, class BM>
-struct ExpanderModule : BGModule {
+template<class MSG, class BM, class BASE>
+struct ExpanderModule : BASE {
MSG _messages[2] {};
+ bool _wasConnected = false;
ExpanderModule() {
static_assert(std::is_base_of<ExpanderMessage, MSG>::value, "type parameter MSG must derive from ExpanderMessage");
+ static_assert(std::is_base_of<BGModule, BASE>::value, "type parameter BASE must derive from BGModule");
- leftExpander.producerMessage = &_messages[0];
- leftExpander.consumerMessage = &_messages[1];
+ BGModule::leftExpander.producerMessage = &_messages[0];
+ BGModule::leftExpander.consumerMessage = &_messages[1];
}
- inline bool connected() {
- return leftExpander.module && dynamic_cast<BM*>(leftExpander.module);
+ bool baseConnected() {
+ bool connected = BGModule::leftExpander.module && dynamic_cast<BM*>(BGModule::leftExpander.module);
+ if (!connected && _wasConnected) {
+ _messages[1] = _messages[0] = MSG();
+ }
+ return _wasConnected = connected;
}
- inline MSG* fromBase() {
- assert(connected());
- MSG* m = (MSG*)leftExpander.consumerMessage;
+ MSG* fromBase() {
+ assert(baseConnected());
+ MSG* m = (MSG*)BGModule::leftExpander.consumerMessage;
assert(m);
return m;
}
- inline MSG* toBase() {
- assert(connected());
- MSG* m = (MSG*)leftExpander.module->rightExpander.producerMessage;
+ MSG* toBase() {
+ assert(baseConnected());
+ MSG* m = (MSG*)BGModule::leftExpander.module->rightExpander.producerMessage;
assert(m);
return m;
}
int channels() override final {
- if (connected()) {
+ if (baseConnected()) {
return fromBase()->channels;
}
return 1;
}
- void process(const ProcessArgs& args) override {
- BGModule::process(args);
- if (leftExpander.module) {
- leftExpander.module->rightExpander.messageFlipRequested = true;
+ void process(const BGModule::ProcessArgs& args) override {
+ BASE::process(args);
+ if (BGModule::leftExpander.module) {
+ BGModule::leftExpander.module->rightExpander.messageFlipRequested = true;
}
}
};