From 7381020517998b87af731eb79cdcd95c4de9a2d0 Mon Sep 17 00:00:00 2001 From: sickozell Date: Mon, 11 Mar 2024 11:48:15 +0100 Subject: [PATCH] update to 2.6.8 --- README.md | 31 +- changelog.md | 6 + extra/crossCompiler.md | 6 +- plugin.json | 42 +- res/Calcs.svg | 871 +++---------- res/Modulator7Compact.svg | 745 +++++++++++ res/PolyMuter16.svg | 1131 ++++++++++++++++ res/PolyMuter16Plus.svg | 1151 +++++++++++++++++ res/PolyMuter8.svg | 759 +++++++++++ res/PolyMuter8Plus.svg | 779 +++++++++++ res/component/SickoBigKnob.svg | 28 +- res/component/SickoKnob.svg | 24 +- res/component/SickoLargeKnob.svg | 26 +- res/component/SickoMuteButton_0.svg | 254 ++++ res/component/SickoMuteButton_1_green.svg | 264 ++++ res/component/SickoMuteButton_2_red.svg | 294 +++++ res/component/SickoMuteButton_3_red_green.svg | 270 ++++ res/component/SickoSmallKnob.svg | 28 +- src/Calcs.cpp | 232 ++-- src/Modulator7Compact.cpp | 398 ++++++ src/PolyMuter16.cpp | 449 +++++++ src/PolyMuter16Plus.cpp | 779 +++++++++++ src/PolyMuter8.cpp | 391 ++++++ src/PolyMuter8Plus.cpp | 734 +++++++++++ src/plugin.cpp | 5 + src/plugin.hpp | 5 + 26 files changed, 8881 insertions(+), 821 deletions(-) create mode 100644 res/Modulator7Compact.svg create mode 100644 res/PolyMuter16.svg create mode 100644 res/PolyMuter16Plus.svg create mode 100644 res/PolyMuter8.svg create mode 100644 res/PolyMuter8Plus.svg create mode 100644 res/component/SickoMuteButton_0.svg create mode 100644 res/component/SickoMuteButton_1_green.svg create mode 100644 res/component/SickoMuteButton_2_red.svg create mode 100644 res/component/SickoMuteButton_3_red_green.svg create mode 100644 src/Modulator7Compact.cpp create mode 100644 src/PolyMuter16.cpp create mode 100644 src/PolyMuter16Plus.cpp create mode 100644 src/PolyMuter8.cpp create mode 100644 src/PolyMuter8Plus.cpp diff --git a/README.md b/README.md index 4de387c..55556a7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# SickoCV v2.6.7 +# SickoCV v2.6.8 VCV Rack plugin modules -![SickoCV modules 2 6 7](https://github.com/sickozell/SickoCV/assets/80784296/ca99e925-119b-4f8a-ac1a-bf1dcdd11b7e) +![SickoCV modules 2 6 8](https://github.com/sickozell/SickoCV/assets/80784296/e29f6164-0446-4899-b6d9-81e22f6923ac) ## table of contents - [Common modules behavior](#common-modules-behavior) @@ -18,8 +18,9 @@ VCV Rack plugin modules - [drummer / drummer4 / drummer4+](#drummer--drummer4--drummer4) - [drumPlayer / drumPlayer+ / drumPlayerXtra](#drumplayer--drumplayer--drumplayerxtra) - [holder / holder Compact / holder8](#holder--holder-compact--holder8) -- [modulator / modulator7](#modulator--modulator7) +- [modulator / modulator7 / modulator7 Compact](#modulator--modulator7--modulator7-compact) - [parking](#parking) +- [polyMuter8 / polyMuter8+ / polyMuter16 / polyMuter16+](#polymuter8--polymuter8--polymuter16--polymuter16) - [shifter](#shifter) - [sickoAmp](#sickoamp) - [sickoLooper1 / sickoLooperX / sickoLooper3 / sickoLooper5](#sickolooper1--sickolooperx--sickolooper3--sickolooper5) @@ -505,13 +506,14 @@ This function sets the module to Track & Hold mode, sample on HIGH gate, scale o [back to top](#table-of-contents) -## modulator / modulator7 +## modulator / modulator7 / modulator7 Compact ### single or 7 triangle/ramp LFOs depending on a main rate managed by a manual knob or synchronized with a clock. -![modulator](https://github.com/sickozell/SickoCV/assets/80784296/a275769b-fddf-43e1-b9a6-874284b50043) +![modulator](https://github.com/sickozell/SickoCV/assets/80784296/706a9a80-0b1e-462a-b3a4-21d8e80066e3) #### - INSTRUCTIONS Following instructions refer to modulator7, but can be applied also to modulator module. +The 'modulator7 Compact' module is just a 7 triangle only unipolar LFOs whithout sync and reset features. Rate Knob range is 0.01/100 Hz and can be modulated by Rate Input and adjusted with its attenuverter. The Sync button or a trigger on the Sync Switch Input, toggles between manual or synced rate. @@ -545,6 +547,25 @@ It can also be used to connect other modules sockets when they need to be wired [back to top](#table-of-contents) +## polyMuter8 / polyMuter8+ / polyMuter16 / polyMuter16+ +### Mutes or soloes the single channels of a poly-cable + +![polymuter](https://github.com/sickozell/SickoCV/assets/80784296/dffb9dda-a74c-428e-8bf4-e7880c82b305) + +#### - INSTRUCTIONS +polyMuter mutes the single channels of a polyphonic cable connected to IN and outputs the same number of channels to OUT. +To avoid clicks the FADE knob sets the fade length in milliseconds of the mute/unmute operation. +Fade range is from 0 to 10 seconds and the default setting is 10ms. +Please note that when the knob is set to full anti-clockwise the tooltip popup will show 1ms, but it actually means no fade. + +polyMuter16 is the standard version of the module, polyMuter8 will output a maximum of 8 channels. The channel display will show the number of channels of the input cable. + +The plus version of each module can also 'solo' the channels by right-clicking on mute buttons, so the led button will become green. Multiple solo channels can be selected. +If an already muted channel is soloed the button becomes green and red, with a further right-click it will go back to mute, or if left-clicked the channel will be directly unmuted. +By using the plus version of polyMuter the right-click menu of buttons is no longer available (that's why there are two versions), but the buttons are still midi mappable usually for the mute function only. Please note that the mute function is activated by exactly a +10v gate, and the solo function by a +3.4v/9.9v gate. + +[back to top](#table-of-contents) + ## shifter ### 64 selectable stages shift register #### - DESCRIPTION diff --git a/changelog.md b/changelog.md index 35a8339..69e3d36 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +### 2.6.8 (2024-03-11) +- added 'polyMuter8' and 'polyMuter8+' modules. +- added 'polyMuter16' and 'polyMuter16+' modules. +- added 'modulator7 Compact' module. +- calcs: added output range selection to context menu. + ### 2.6.7 (2024-03-02) - switcher: added 'Route & Hold' feature. - changed ports and knobs design. diff --git a/extra/crossCompiler.md b/extra/crossCompiler.md index d00fa8c..b744f41 100644 --- a/extra/crossCompiler.md +++ b/extra/crossCompiler.md @@ -1,11 +1,11 @@ -git tag v2.6.7-beta1 -m "create v2.6.7-beta1" +git tag v2.6.8-beta6 -m "create v2.6.8-beta6" git push origin --tags delete local tag -git tag -d v2.6.7-beta +git tag -d v2.6.8-beta delete remote -git push --delete origin v2.6.7-beta +git push --delete origin v2.6.8-beta ### How to build a VCVRack plugin with Github Action diff --git a/plugin.json b/plugin.json index 38ccdcb..5ecb3f0 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "slug": "SickoCV", "name": "SickoCV", - "version": "2.6.7", + "version": "2.6.8", "license": "GPL-3.0-or-later", "brand": "Sickozell", "author": "Sickozell", @@ -229,6 +229,14 @@ "Polyphonic" ] }, + { + "slug": "Modulator7Compact", + "name": "modulator7 Compact", + "description": "7 unipolar triangle LFOs", + "tags": [ + "Low-frequency oscillator" + ] + }, { "slug": "Parking", "name": "parking", @@ -237,6 +245,38 @@ "Utility" ] }, + { + "slug": "PolyMuter8", + "name": "polyMuter8", + "description": "Mutes up to 8 single channels of a poly-cable", + "tags": [ + "Utility" + ] + }, + { + "slug": "PolyMuter8Plus", + "name": "polyMuter8+", + "description": "Mutes or Soloes up to 8 single channels of a poly-cable", + "tags": [ + "Utility" + ] + }, + { + "slug": "PolyMuter16", + "name": "polyMuter16", + "description": "Mutes single channels of a poly-cable", + "tags": [ + "Utility" + ] + }, + { + "slug": "PolyMuter16Plus", + "name": "polyMuter16+", + "description": "Mutes or Soloes single channels of a poly-cable", + "tags": [ + "Utility" + ] + }, { "slug": "Shifter", "name": "shifter", diff --git a/res/Calcs.svg b/res/Calcs.svg index 046fa27..4c4904f 100644 --- a/res/Calcs.svg +++ b/res/Calcs.svg @@ -24,15 +24,15 @@ inkscape:deskcolor="#d1d1d1" inkscape:document-units="mm" showgrid="false" - inkscape:zoom="5.6568544" - inkscape:cx="26.428115" - inkscape:cy="470.57955" + inkscape:zoom="2.0000001" + inkscape:cx="-204.24999" + inkscape:cy="347.49998" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" - inkscape:current-layer="layer1" + inkscape:current-layer="g1084" showguides="true" /> @@ -158,7 +158,7 @@ inkscape:label="Elementi" style="display:inline;fill:#f9f9f9"> - + inkscape:label="Table Letters" + style="display:inline"> + inkscape:label="Divisori" + style="display:inline"> x/y + x="12.645837" + y="74.316666">x/y x+y + x="12.822226" + y="18.916666">x+y x-y + x="12.822226" + y="46.613945">x-y c + x="24" + y="7.3638873">c b + x="14.182" + y="7.4166651">b a + x="4.2839999" + y="7.3843751">a avg(abc) + inkscape:label="avg(abc)">abc avg(xy) - B - U + style="font-size:2.82222px;text-align:center;text-anchor:middle;stroke:none;stroke-width:0.265" + x="15.261521" + y="102.26944">avg ab ac bc - - - - - + id="g1084" + inkscape:label="Etichette Testo path" + style="display:inline"> + id="path1086" /> + id="path1088" /> - - - - - - - - - - - - + id="path1090" /> + id="text1046" + style="font-size:2.82222px;font-family:'C64 User';-inkscape-font-specification:'C64 User';display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="x/y"> + d="m 12.645837,74.316666 v -0.352778 h 0.352777 v -0.352777 h 0.352778 v -0.352778 h -0.352778 v -0.352777 h -0.352777 v -0.352778 h 0.705555 v 0.352778 h 0.705555 v -0.352778 h 0.705555 v 0.352778 h -0.352778 v 0.352777 h -0.352777 v 0.352778 h 0.352777 v 0.352777 h 0.352778 v 0.352778 h -0.705555 v -0.352778 h -0.705555 v 0.352778 z" + style="stroke-width:0.265" + id="path1093" /> + d="m 15.115279,74.316666 v -0.352778 h 0.352778 v -0.352777 h 0.352777 v -0.352778 h 0.352778 v -0.352777 h 0.352777 v -0.352778 h 0.352778 v -0.352777 h 0.705555 v 0.352777 h -0.352777 v 0.352778 h -0.352778 v 0.352777 h -0.352778 v 0.352778 h -0.352777 v 0.352777 h -0.352778 v 0.352778 z" + style="stroke-width:0.265" + id="path1095" /> + d="m 20.054165,72.552778 v 1.41111 h -0.352778 v 0.352778 H 19.34861 v 0.352777 H 17.9375 v -0.352777 h 1.058332 V 73.963888 H 18.290277 V 73.611111 H 17.9375 v -1.058333 h 0.705555 v 1.058333 h 0.705555 v -1.058333 z" + style="stroke-width:0.265" + id="path1097" /> + d="M -35.951252,3.1166725 V 2.7638938 h 0.352779 V 2.411115 h 0.352779 V 2.0583362 h -0.352779 V 1.7055575 h -0.352779 V 1.3527787 h 0.705558 v 0.3527788 h 0.705557 V 1.3527787 h 0.705558 v 0.3527788 h -0.352779 v 0.3527787 h -0.352779 V 2.411115 h 0.352779 v 0.3527788 h 0.352779 v 0.3527787 h -0.705558 V 2.7638938 h -0.705557 v 0.3527787 z" + id="path1107" /> + d="M -33.129022,2.7638938 V 2.411115 h 0.352779 V 2.0583362 h -0.705558 V 1.7055575 h 0.705558 V 1.3527787 h -0.352779 V 0.99999994 h 0.705558 V 1.3527787 h 0.705557 V 0.99999994 h 0.705558 V 1.3527787 h -0.352779 v 0.3527788 h 0.705558 v 0.3527787 h -0.705558 V 2.411115 h 0.352779 v 0.3527788 h -0.705558 V 2.411115 h -0.705557 v 0.3527788 z" + id="path1109" /> + d="m -28.190119,1.3527787 v 1.4111151 h -0.352779 v 0.3527787 h -0.352779 v 0.3527788 h -1.411115 V 3.1166725 h 1.058337 V 2.7638938 h -0.705558 V 2.411115 h -0.352779 V 1.3527787 h 0.705558 V 2.411115 h 0.705557 V 1.3527787 Z" + id="path1111" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="path1114" /> + id="path1116" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="path1118" /> - + id="text1058" + style="font-size:2.82222px;font-family:'C64 User';-inkscape-font-specification:'C64 User';display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="x-y"> - - - + d="m 12.822226,46.613945 v -0.352778 h 0.352777 V 45.90839 h 0.352778 v -0.352778 h -0.352778 v -0.352777 h -0.352777 v -0.352778 h 0.705555 v 0.352778 h 0.705555 v -0.352778 h 0.705555 v 0.352778 h -0.352778 v 0.352777 h -0.352777 v 0.352778 h 0.352777 v 0.352777 h 0.352778 v 0.352778 h -0.705555 v -0.352778 h -0.705555 v 0.352778 z" + style="stroke-width:0.265" + id="path1121" /> - - + d="m 15.291668,45.555612 v -0.352777 h 2.116665 v 0.352777 z" + style="stroke-width:0.265" + id="path1123" /> + d="m 19.877776,44.850057 v 1.41111 h -0.352778 v 0.352778 h -0.352777 v 0.352778 h -1.41111 v -0.352778 h 1.058332 V 46.261167 H 18.113888 V 45.90839 h -0.352777 v -1.058333 h 0.705555 v 1.058333 h 0.705555 v -1.058333 z" + style="stroke-width:0.265" + id="path1125" /> + id="text1062" + style="font-size:2.82222px;font-family:'C64 User';-inkscape-font-specification:'C64 User';fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="c"> + d="M 24.352778,7.3638873 V 7.0111098 H 24 V 5.9527773 h 0.352778 V 5.5999998 h 1.41111 v 0.3527775 h -1.058333 v 1.0583325 h 1.058333 v 0.3527775 z" + style="stroke-width:0.265" + id="path1128" /> + id="text1066" + style="font-size:2.82222px;font-family:'C64 User';-inkscape-font-specification:'C64 User';display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="b"> + d="M 14.887555,7.0638876 H 15.59311 V 6.3583325 H 14.887555 Z M 14.182,7.4166651 V 5.3 h 0.705555 v 0.705555 h 1.058333 v 0.3527775 h 0.352777 v 0.7055551 h -0.352777 v 0.3527775 z" + style="stroke-width:0.265" + id="path1131" /> + id="text1070" + style="font-size:3.175px;font-family:'C64 User';-inkscape-font-specification:'C64 User';fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="a"> + d="m 4.6808749,7.3843751 v -0.396875 h -0.396875 v -0.396875 h 0.396875 v -0.396875 h 1.190625 v -0.396875 h -1.190625 v -0.396875 h 1.5875 v 0.396875 h 0.396875 v 1.5875 z m 0.396875,-0.396875 h 0.79375 v -0.396875 h -0.79375 z" + style="stroke-width:0.265" + id="path1134" /> - - - - - + aria-label="abc" + id="text1072" + style="font-size:2.82223px;font-family:'C64 User';-inkscape-font-specification:'C64 User';display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="avg(abc)"> + d="m 13.007778,120.10834 v -0.35278 H 12.655 v -0.35278 h 0.352778 V 119.05 h 1.058337 v -0.35278 h -1.058337 v -0.35278 h 1.411116 v 0.35278 h 0.352778 v 1.41112 z m 0.352779,-0.35278 h 0.705558 v -0.35278 h -0.705558 z" + id="path1137" /> + d="m 15.830009,119.75556 h 0.705557 V 119.05 h -0.705557 z m -0.705558,0.35278 v -2.11668 h 0.705558 v 0.70556 h 1.058336 V 119.05 h 0.352779 v 0.70556 h -0.352779 v 0.35278 z" + id="path1139" /> + d="m 17.946681,120.10834 v -0.35278 h -0.352778 v -1.05834 h 0.352778 v -0.35278 h 1.411115 v 0.35278 H 18.29946 v 1.05834 h 1.058336 v 0.35278 z" + id="path1141" /> + aria-label="avg" + id="text1076" + style="font-size:2.82222px;font-family:'C64 User';-inkscape-font-specification:'C64 User';text-align:center;text-anchor:middle;display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="avg(xy)"> + d="m 11.910135,102.26944 v -0.35278 h -0.352778 v -0.35278 h 0.352778 v -0.35277 h 1.058333 v -0.35278 h -1.058333 v -0.35278 h 1.41111 v 0.35278 h 0.352778 v 1.41111 z m 0.352777,-0.35278 h 0.705556 v -0.35278 h -0.705556 z" + style="stroke-width:0.265" + id="path1144" /> + d="m 14.732355,102.26944 v -0.35278 h -0.352777 v -0.35278 H 14.0268 v -1.05833 h 0.705555 v 1.05833 h 0.705555 v -1.05833 h 0.705555 v 1.05833 h -0.352777 v 0.35278 H 15.43791 v 0.35278 z" + style="stroke-width:0.265" + id="path1146" /> + d="m 16.496243,102.62222 v -0.35278 h 1.41111 v -0.35278 H 16.84902 v -0.35278 h -0.352777 v -0.70555 h 0.352777 v -0.35278 h 1.763888 v 1.76389 H 18.26013 v 0.35278 z m 0.705555,-1.05834 h 0.705555 v -0.70555 h -0.705555 z" + style="stroke-width:0.265" + id="path1148" /> + + + d="m 4.012241,105.21667 v -0.35278 H 3.6594622 v -0.35278 H 4.012241 v -0.35278 h 1.0583363 v -0.35278 H 4.012241 v -0.35278 h 1.411115 v 0.35278 h 0.3527788 v 1.41112 z m 0.3527787,-0.35278 h 0.7055576 v -0.35278 H 4.3650197 Z" + id="path1151" /> + d="m 6.8344712,104.86389 h 0.7055575 v -0.70556 H 6.8344712 Z m -0.7055576,0.35278 v -2.11668 h 0.7055576 v 0.70556 h 1.0583363 v 0.35278 h 0.3527787 v 0.70556 H 7.8928075 v 0.35278 z" + id="path1153" /> + + + d="m 22.533069,105.21667 v -0.35278 H 22.18029 v -0.35278 h 0.352779 v -0.35278 h 1.058336 v -0.35278 h -1.058336 v -0.35278 h 1.411115 v 0.35278 h 0.352779 v 1.41112 z m 0.352779,-0.35278 h 0.705557 v -0.35278 h -0.705557 z" + id="path1156" /> + d="m 25.00252,105.21667 v -0.35278 h -0.352778 v -1.05834 h 0.352778 v -0.35278 h 1.411115 v 0.35278 h -1.058336 v 1.05834 h 1.058336 v 0.35278 z" + id="path1158" /> - + aria-label="bc" + id="text1082" + style="font-size:2.82223px;font-family:'C64 User';-inkscape-font-specification:'C64 User';display:inline;fill:#ffffff;stroke-width:0.264999;stroke-dasharray:0.264999, 0.264999" + inkscape:label="avg(bc)"> + d="m 13.828885,104.86389 h 0.705557 v -0.70556 h -0.705557 z m -0.705558,0.35278 v -2.11668 h 0.705558 v 0.70556 h 1.058336 v 0.35278 H 15.24 v 0.70556 h -0.352779 v 0.35278 z" + id="path1161" /> + d="m 15.945557,105.21667 v -0.35278 h -0.352778 v -1.05834 h 0.352778 v -0.35278 h 1.411115 v 0.35278 h -1.058336 v 1.05834 h 1.058336 v 0.35278 z" + id="path1163" /> - + + id="text1186" + style="font-size:3.52778px;font-family:'C64 User';-inkscape-font-specification:'C64 User';text-align:center;text-anchor:middle;display:inline;fill:#ffffff;stroke-width:0.264583" + inkscape:label="calcs"> + d="M 14.209374,3.9164174 V 3.4754449 H 13.768402 V 2.1525273 h 0.440972 V 1.7115548 h 1.76389 v 0.4409725 h -1.322917 v 1.3229176 h 1.322917 v 0.4409725 z" + id="path1188" /> + d="M 16.855209,3.9164174 V 3.4754449 H 16.414237 V 3.0344723 h 0.440972 V 2.5934998 h 1.322918 V 2.1525273 H 16.855209 V 1.7115548 h 1.76389 v 0.4409725 h 0.440973 v 1.7638901 z m 0.440973,-0.4409725 h 0.881945 V 3.0344723 h -0.881945 z" + id="path1190" /> + d="m 20.823961,1.2705823 v 2.2048626 h 0.440973 v 0.4409725 h -1.76389 V 3.4754449 h 0.440972 V 1.7115548 H 19.501044 V 1.2705823 Z" + id="path1192" /> + d="M 22.146879,3.9164174 V 3.4754449 H 21.705906 V 2.1525273 h 0.440973 V 1.7115548 h 1.76389 v 0.4409725 h -1.322918 v 1.3229176 h 1.322918 v 0.4409725 z" + id="path1194" /> + d="m 26.997576,1.7115548 v 0.4409725 h -1.76389 v 0.4409725 h 1.322918 v 0.4409725 h 0.440972 V 3.4754449 H 26.556604 V 3.9164174 H 24.351741 V 3.4754449 h 1.76389 V 3.0344723 H 24.792714 V 2.5934998 H 24.351741 V 2.1525273 h 0.440973 V 1.7115548 Z" + id="path1196" /> diff --git a/res/Modulator7Compact.svg b/res/Modulator7Compact.svg new file mode 100644 index 0000000..b5c4d84 --- /dev/null +++ b/res/Modulator7Compact.svg @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/PolyMuter16.svg b/res/PolyMuter16.svg new file mode 100644 index 0000000..0d68a3f --- /dev/null +++ b/res/PolyMuter16.svg @@ -0,0 +1,1131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/PolyMuter16Plus.svg b/res/PolyMuter16Plus.svg new file mode 100644 index 0000000..29b6154 --- /dev/null +++ b/res/PolyMuter16Plus.svg @@ -0,0 +1,1151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/PolyMuter8.svg b/res/PolyMuter8.svg new file mode 100644 index 0000000..3e5d755 --- /dev/null +++ b/res/PolyMuter8.svg @@ -0,0 +1,759 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/PolyMuter8Plus.svg b/res/PolyMuter8Plus.svg new file mode 100644 index 0000000..4901751 --- /dev/null +++ b/res/PolyMuter8Plus.svg @@ -0,0 +1,779 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/component/SickoBigKnob.svg b/res/component/SickoBigKnob.svg index 1640502..f262ec1 100644 --- a/res/component/SickoBigKnob.svg +++ b/res/component/SickoBigKnob.svg @@ -27,8 +27,8 @@ fit-margin-top="0" id="base" inkscape:current-layer="svg52164" - inkscape:cx="22.254923" - inkscape:cy="10.16466" + inkscape:cx="9.7321428" + inkscape:cy="18.839286" inkscape:document-units="mm" inkscape:pageopacity="0.0" inkscape:pageshadow="2" @@ -37,7 +37,7 @@ inkscape:window-width="1920" inkscape:window-x="-8" inkscape:window-y="-8" - inkscape:zoom="15.839192" + inkscape:zoom="11.2" pagecolor="#ffffff" showgrid="false" inkscape:showpageshadow="2" @@ -49,23 +49,23 @@ opacity="0.99" fill="#ededed" enable-background="new " - d="m 21.61559,3.4288401 h 1.768816 V 21.43669 c 0,0.448842 -0.204181,0.623568 -0.456059,0.623568 h -0.856696 c -0.25188,0 -0.456061,-0.178896 -0.456061,-0.623568 z" + d="m 21.615815,3.3159431 h 1.768366 V 21.429932 c 0,0.451487 -0.20413,0.627243 -0.455943,0.627243 H 22.07176 c -0.251815,0 -0.455945,-0.17995 -0.455945,-0.627243 z" id="path2" - style="stroke-width:1.52867" + style="stroke-width:1.53297" sodipodi:nodetypes="ccssssc" /> diff --git a/res/component/SickoKnob.svg b/res/component/SickoKnob.svg index 0e0d831..a25fec0 100644 --- a/res/component/SickoKnob.svg +++ b/res/component/SickoKnob.svg @@ -27,7 +27,7 @@ fit-margin-top="0" id="base" inkscape:current-layer="svg111794" - inkscape:cx="11.808036" + inkscape:cx="11.651786" inkscape:cy="16.294643" inkscape:document-units="mm" inkscape:pageopacity="0.0" @@ -49,23 +49,23 @@ opacity="0.99" fill="#ededed" enable-background="new " - d="m 13.289385,2.0575355 h 1.768819 V 13.498295 c 0,0.285158 -0.204182,0.396165 -0.456061,0.396165 h -0.856698 c -0.251878,0 -0.45606,-0.113658 -0.45606,-0.396165 z" + d="m 13.289285,2.0881552 h 1.769019 V 13.499089 c 0,0.284414 -0.204205,0.395132 -0.456112,0.395132 h -0.856796 c -0.251906,0 -0.456111,-0.113362 -0.456111,-0.395132 z" id="path2" - style="stroke-width:1.21846" + style="stroke-width:1.21694" sodipodi:nodetypes="ccssssc" /> diff --git a/res/component/SickoLargeKnob.svg b/res/component/SickoLargeKnob.svg index 62e52e9..39d5810 100644 --- a/res/component/SickoLargeKnob.svg +++ b/res/component/SickoLargeKnob.svg @@ -27,8 +27,8 @@ fit-margin-top="0" id="base" inkscape:current-layer="svg111794" - inkscape:cx="-0.60855467" - inkscape:cy="14.175744" + inkscape:cx="-0.78754133" + inkscape:cy="14.247339" inkscape:document-units="mm" inkscape:pageopacity="0.0" inkscape:pageshadow="2" @@ -57,23 +57,23 @@ opacity="0.99" fill="#ededed" enable-background="new " - d="m 17.115575,2.6528098 h 1.768819 l 0,14.4915062 c 0,0.361197 -0.204182,0.501804 -0.45606,0.501804 h -0.856699 c -0.251878,0 -0.45606,-0.143965 -0.45606,-0.501804 z" + d="m 17.115613,2.6360829 h 1.768743 V 17.141759 c 0,0.361551 -0.204173,0.502295 -0.45604,0.502295 h -0.856663 c -0.251867,0 -0.45604,-0.144106 -0.45604,-0.502295 z" id="path2" - style="stroke-width:1.37133" + style="stroke-width:1.37197" sodipodi:nodetypes="ccssssc" /> diff --git a/res/component/SickoMuteButton_0.svg b/res/component/SickoMuteButton_0.svg new file mode 100644 index 0000000..265ff99 --- /dev/null +++ b/res/component/SickoMuteButton_0.svg @@ -0,0 +1,254 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/component/SickoMuteButton_1_green.svg b/res/component/SickoMuteButton_1_green.svg new file mode 100644 index 0000000..6656100 --- /dev/null +++ b/res/component/SickoMuteButton_1_green.svg @@ -0,0 +1,264 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/component/SickoMuteButton_2_red.svg b/res/component/SickoMuteButton_2_red.svg new file mode 100644 index 0000000..2809ff6 --- /dev/null +++ b/res/component/SickoMuteButton_2_red.svg @@ -0,0 +1,294 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/component/SickoMuteButton_3_red_green.svg b/res/component/SickoMuteButton_3_red_green.svg new file mode 100644 index 0000000..a56baa9 --- /dev/null +++ b/res/component/SickoMuteButton_3_red_green.svg @@ -0,0 +1,270 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/component/SickoSmallKnob.svg b/res/component/SickoSmallKnob.svg index 0cf640f..8f6a99a 100644 --- a/res/component/SickoSmallKnob.svg +++ b/res/component/SickoSmallKnob.svg @@ -27,8 +27,8 @@ fit-margin-top="0" id="base" inkscape:current-layer="svg111794" - inkscape:cx="11.285298" - inkscape:cy="1.0101525" + inkscape:cx="4.4196428" + inkscape:cy="11.116071" inkscape:document-units="mm" inkscape:pageopacity="0.0" inkscape:pageshadow="2" @@ -37,7 +37,7 @@ inkscape:window-width="1920" inkscape:window-x="0" inkscape:window-y="0" - inkscape:zoom="31.678384" + inkscape:zoom="22.4" pagecolor="#ffffff" showgrid="false" inkscape:showpageshadow="2" @@ -49,23 +49,23 @@ opacity="0.99" fill="#ededed" enable-background="new " - d="m 10.45348,1.6177368 h 1.768817 v 9.1782192 c 0,0.228765 -0.204181,0.317819 -0.456059,0.317819 h -0.856697 c -0.25188,0 -0.456061,-0.09118 -0.456061,-0.317819 z" + d="m 10.453273,1.6711263 h 1.769231 v 9.1290017 c 0,0.227538 -0.204229,0.316114 -0.456166,0.316114 h -0.856897 c -0.251939,0 -0.456168,-0.09069 -0.456168,-0.316114 z" id="path2" - style="stroke-width:1.09135" + style="stroke-width:1.08855" sodipodi:nodetypes="ccssssc" /> diff --git a/src/Calcs.cpp b/src/Calcs.cpp index e198a2a..e95a91e 100644 --- a/src/Calcs.cpp +++ b/src/Calcs.cpp @@ -1,3 +1,8 @@ +#define UNLIMITED 0 +#define LIM_0_10 1 +#define LIM_10_10 2 +#define LIM_5_5 3 + #include "plugin.hpp" struct Calcs : Module { @@ -42,12 +47,19 @@ struct Calcs : Module { LIGHTS_LEN }; + float aValue = 0; + float bValue = 0; + float cValue = 0; + + int outRange = 1; + Calcs() { config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); configInput(A_INPUT, "a"); configInput(B_INPUT, "b"); configInput(C_INPUT, "c"); + // this is maintained to not brak patched for previous version configSwitch(RANGE_SWITCH, 0.f, 1.f, 1.f, "Input range", {"Bipolar", "Unipolar"}); configOutput(BPLUSA_OUTPUT, "b+a"); @@ -79,94 +91,96 @@ struct Calcs : Module { } + float checkRange(float checkedValue) { - if (params[RANGE_SWITCH].getValue() == 0) { - if (checkedValue > 5) - checkedValue = 5; - else if (checkedValue < -5) - checkedValue = -5; - } else { - if (checkedValue > 10) - checkedValue = 10; - else if (checkedValue < 0) - checkedValue = 0; + + switch (outRange) { + case UNLIMITED: + break; + + case LIM_0_10: + if (checkedValue > 10.f) + checkedValue = 10.f; + else if (checkedValue < 0.f) + checkedValue = 0.f; + break; + + case LIM_10_10: + if (checkedValue > 10.f) + checkedValue = 10.f; + else if (checkedValue < -10.f) + checkedValue = -10.f; + break; + + case LIM_5_5: + if (checkedValue > 5.f) + checkedValue = 5.f; + else if (checkedValue < -5.f) + checkedValue = -5.f; + break; } return checkedValue; } void process(const ProcessArgs& args) override { - if (inputs[A_INPUT].isConnected() && inputs[B_INPUT].isConnected()) { - outputs[BPLUSA_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() + inputs[B_INPUT].getVoltage())); - outputs[ATIMESB_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() * inputs[B_INPUT].getVoltage())); - outputs[BMINUSA_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() - inputs[A_INPUT].getVoltage())); - outputs[AMINUSB_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() - inputs[B_INPUT].getVoltage())); - - if (inputs[B_INPUT].getVoltage() != 0) - outputs[ADIVB_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() / inputs[B_INPUT].getVoltage())); - - if (inputs[C_INPUT].getVoltage() != 0) - outputs[BDIVA_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() / inputs[A_INPUT].getVoltage())); - - outputs[AAVGB_OUTPUT].setVoltage(checkRange((inputs[A_INPUT].getVoltage() + inputs[B_INPUT].getVoltage())/2)); - } else { - outputs[BPLUSA_OUTPUT].setVoltage(0); - outputs[ATIMESB_OUTPUT].setVoltage(0); - outputs[BMINUSA_OUTPUT].setVoltage(0); - outputs[AMINUSB_OUTPUT].setVoltage(0); - outputs[ADIVB_OUTPUT].setVoltage(0); - outputs[BDIVA_OUTPUT].setVoltage(0); - outputs[AAVGB_OUTPUT].setVoltage(0); - } - if (inputs[B_INPUT].isConnected() && inputs[C_INPUT].isConnected()) { - outputs[CPLUSB_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() + inputs[C_INPUT].getVoltage())); - outputs[BTIMESC_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() * inputs[C_INPUT].getVoltage())); - outputs[BMINUSC_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() - inputs[C_INPUT].getVoltage())); - outputs[CMINUSB_OUTPUT].setVoltage(checkRange(inputs[C_INPUT].getVoltage() - inputs[B_INPUT].getVoltage())); - - if (inputs[C_INPUT].getVoltage() != 0) - outputs[BDIVC_OUTPUT].setVoltage(checkRange(inputs[B_INPUT].getVoltage() / inputs[C_INPUT].getVoltage())); - - if (inputs[B_INPUT].getVoltage() != 0) - outputs[CDIVB_OUTPUT].setVoltage(checkRange(inputs[C_INPUT].getVoltage() / inputs[B_INPUT].getVoltage())); - - outputs[BAVGC_OUTPUT].setVoltage(checkRange((inputs[B_INPUT].getVoltage() + inputs[C_INPUT].getVoltage())/2)); - } else { - outputs[CPLUSB_OUTPUT].setVoltage(0); - outputs[BTIMESC_OUTPUT].setVoltage(0); - outputs[BMINUSC_OUTPUT].setVoltage(0); - outputs[CMINUSB_OUTPUT].setVoltage(0); - outputs[BDIVC_OUTPUT].setVoltage(0); - outputs[CDIVB_OUTPUT].setVoltage(0); - outputs[BAVGC_OUTPUT].setVoltage(0); - } - if (inputs[A_INPUT].isConnected() && inputs[C_INPUT].isConnected()) { - outputs[CPLUSA_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() + inputs[C_INPUT].getVoltage())); - outputs[ATIMESC_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() * inputs[C_INPUT].getVoltage())); - outputs[AMINUSC_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() - inputs[C_INPUT].getVoltage())); - outputs[CMINUSA_OUTPUT].setVoltage(checkRange(inputs[C_INPUT].getVoltage() - inputs[A_INPUT].getVoltage())); - - if (inputs[A_INPUT].getVoltage() != 0) - outputs[CDIVA_OUTPUT].setVoltage(checkRange(inputs[C_INPUT].getVoltage() / inputs[A_INPUT].getVoltage())); - - if (inputs[C_INPUT].getVoltage() != 0) - outputs[ADIVC_OUTPUT].setVoltage(checkRange(inputs[A_INPUT].getVoltage() / inputs[C_INPUT].getVoltage())); - - outputs[AAVGC_OUTPUT].setVoltage(checkRange((inputs[A_INPUT].getVoltage() + inputs[C_INPUT].getVoltage())/2)); - } else { - outputs[CPLUSA_OUTPUT].setVoltage(0); - outputs[ATIMESC_OUTPUT].setVoltage(0); - outputs[AMINUSC_OUTPUT].setVoltage(0); - outputs[CMINUSA_OUTPUT].setVoltage(0); - outputs[CDIVA_OUTPUT].setVoltage(0); - outputs[ADIVC_OUTPUT].setVoltage(0); - outputs[AAVGC_OUTPUT].setVoltage(0); - } + aValue = inputs[A_INPUT].getVoltage(); + bValue = inputs[B_INPUT].getVoltage(); + cValue = inputs[C_INPUT].getVoltage(); + - if (inputs[A_INPUT].isConnected() && inputs[B_INPUT].isConnected() && inputs[C_INPUT].isConnected()) - outputs[AVGABC_OUTPUT].setVoltage(checkRange((inputs[A_INPUT].getVoltage() + inputs[B_INPUT].getVoltage() + inputs[C_INPUT].getVoltage())/3)); + outputs[BPLUSA_OUTPUT].setVoltage(checkRange(aValue + bValue)); + outputs[ATIMESB_OUTPUT].setVoltage(checkRange(aValue * bValue)); + outputs[BMINUSA_OUTPUT].setVoltage(checkRange(bValue - aValue)); + outputs[AMINUSB_OUTPUT].setVoltage(checkRange(aValue - bValue)); + + if (bValue != 0.f) + outputs[ADIVB_OUTPUT].setVoltage(checkRange(aValue / bValue)); else - outputs[AVGABC_OUTPUT].setVoltage(0); + outputs[ADIVB_OUTPUT].setVoltage(0.f); + + if (cValue != 0.f) + outputs[BDIVA_OUTPUT].setVoltage(checkRange(bValue / aValue)); + else + outputs[BDIVA_OUTPUT].setVoltage(0.f); + + outputs[AAVGB_OUTPUT].setVoltage(checkRange((aValue + bValue)/2)); + + // --------------------------- + + outputs[CPLUSB_OUTPUT].setVoltage(checkRange(bValue + cValue)); + outputs[BTIMESC_OUTPUT].setVoltage(checkRange(bValue * cValue)); + outputs[BMINUSC_OUTPUT].setVoltage(checkRange(bValue - cValue)); + outputs[CMINUSB_OUTPUT].setVoltage(checkRange(cValue - bValue)); + + if (cValue != 0.f) + outputs[BDIVC_OUTPUT].setVoltage(checkRange(bValue / cValue)); + else + outputs[BDIVC_OUTPUT].setVoltage(0.f); + + if (bValue != 0.f) + outputs[CDIVB_OUTPUT].setVoltage(checkRange(cValue / bValue)); + + outputs[BAVGC_OUTPUT].setVoltage(checkRange((bValue + cValue)/2)); + + // -------------------------- + + outputs[CPLUSA_OUTPUT].setVoltage(checkRange(aValue + cValue)); + outputs[ATIMESC_OUTPUT].setVoltage(checkRange(aValue * cValue)); + outputs[AMINUSC_OUTPUT].setVoltage(checkRange(aValue - cValue)); + outputs[CMINUSA_OUTPUT].setVoltage(checkRange(cValue - aValue)); + + if (aValue != 0.f) + outputs[CDIVA_OUTPUT].setVoltage(checkRange(cValue / aValue)); + + if (cValue != 0.f) + outputs[ADIVC_OUTPUT].setVoltage(checkRange(aValue / cValue)); + + outputs[AAVGC_OUTPUT].setVoltage(checkRange((aValue + cValue)/2)); + + // -------------------------- + + outputs[AVGABC_OUTPUT].setVoltage(checkRange((aValue + bValue + cValue)/3)); } }; @@ -178,23 +192,21 @@ struct CalcsWidget : ModuleWidget { addChild(createWidget(Vec(0, 0))); addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); - float xa = 4.4; - float xb = 12.6; - float xc = 20.7; + float xa = 5.7f; + float xb = 15.2f; + float xc = 24.9f; - float yStart = 12; - float yIncrement = 8; + float yStart = 12.3f; + float yIncrement = 8.f; - addParam(createParamCentered(mm2px(Vec(27.5, 11)), module, Calcs::RANGE_SWITCH)); - addInput(createInputCentered(mm2px(Vec(xa, yStart)), module, Calcs::A_INPUT)); addInput(createInputCentered(mm2px(Vec(xb, yStart)), module, Calcs::B_INPUT)); addInput(createInputCentered(mm2px(Vec(xc, yStart)), module, Calcs::C_INPUT)); - xa = 8.2; - xb = 16.3; - xc = 24.4; - yStart = 23.9; + xa = 8.2f; + xb = 16.3f; + xc = 24.4f; + yStart = 23.9f; addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BPLUSA_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xc, yStart)), module, Calcs::CPLUSA_OUTPUT)); yStart += yIncrement; @@ -204,7 +216,7 @@ struct CalcsWidget : ModuleWidget { addOutput(createOutputCentered(mm2px(Vec(xa, yStart)), module, Calcs::ATIMESC_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BTIMESC_OUTPUT)); - yStart = 51.3; + yStart = 51.3f; addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BMINUSA_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xc, yStart)), module, Calcs::CMINUSA_OUTPUT)); yStart += yIncrement; @@ -214,7 +226,7 @@ struct CalcsWidget : ModuleWidget { addOutput(createOutputCentered(mm2px(Vec(xa, yStart)), module, Calcs::AMINUSC_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BMINUSC_OUTPUT)); - yStart = 79.1; + yStart = 79.1f; addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BDIVA_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xc, yStart)), module, Calcs::CDIVA_OUTPUT)); yStart += yIncrement; @@ -224,17 +236,41 @@ struct CalcsWidget : ModuleWidget { addOutput(createOutputCentered(mm2px(Vec(xa, yStart)), module, Calcs::ADIVC_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BDIVC_OUTPUT)); - xa = 6; - xb = 15.24; - xc = 24.5; - yStart = 109.8; + xa = 6.f; + xb = 15.24f; + xc = 24.5f; + yStart = 109.8f; addOutput(createOutputCentered(mm2px(Vec(xa, yStart)), module, Calcs::AAVGB_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xb, yStart)), module, Calcs::BAVGC_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(xc, yStart)), module, Calcs::AAVGC_OUTPUT)); - xa = 6; - yStart = 119; + xa = 6.f; + yStart = 119.f; addOutput(createOutputCentered(mm2px(Vec(xa, yStart)), module, Calcs::AVGABC_OUTPUT)); } + + void appendContextMenu(Menu *menu) override { + Calcs *module = dynamic_cast(this->module); + assert(module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("Output Range")); + struct RangeItem : MenuItem { + Calcs* module; + int outRange; + void onAction(const event::Action& e) override { + module->outRange = outRange; + } + }; + + std::string outRangeNames[4] = {"Unlimited", "0-10v", "± 10v", "± 5v"}; + for (int i = 0; i < 4; i++) { + RangeItem* rangeItem = createMenuItem(outRangeNames[i]); + rangeItem->rightText = CHECKMARK(module->outRange == i); + rangeItem->module = module; + rangeItem->outRange = i; + menu->addChild(rangeItem); + } + } }; Model* modelCalcs = createModel("Calcs"); \ No newline at end of file diff --git a/src/Modulator7Compact.cpp b/src/Modulator7Compact.cpp new file mode 100644 index 0000000..d01167c --- /dev/null +++ b/src/Modulator7Compact.cpp @@ -0,0 +1,398 @@ +#include "plugin.hpp" + +//using namespace std; // this is for debug + +struct Modulator7Compact : Module { + enum ParamId { + RATE_PARAM, + RATE_ATTENUV_PARAM, + ENUMS(XRATE_PARAM, 7), + PARAMS_LEN + }; + enum InputId { + RATE_INPUT, + INPUTS_LEN + }; + enum OutputId { + ENUMS(OUT_OUTPUT, 7), + OUTPUTS_LEN + }; + enum LightId { + LIGHTS_LEN + }; + + double sampleRateCoeff = APP->engine->getSampleRate() / 2; + double rateProvv = 1.f; + double rate = 1.f; + double rateKnob = 0.5f; + double prevRateKnob = 1.f; + double xRate[7] = {1, 1, 1, 1, 1, 1, 1}; + double xRateKnob[7] = {1, 1, 1, 1, 1, 1, 1}; + double prevXrateKnob[7] = {0, 0, 0, 0, 0, 0, 0}; + double waveCoeff = 0; + double waveValue[7] = {0, 0, 0, 0, 0, 0, 0}; + + int waveSlope[7] = {1, 1, 1, 1, 1, 1, 1}; + + int polyChans = 1; + float out = 0; + + static constexpr float minRate = 0.01f; // in milliseconds + static constexpr float maxRate = 100.f; // in milliseconds + + static constexpr float minXrate = 0.047619f; // in milliseconds + static constexpr float maxXrate = 21.f; // in milliseconds + + //************************************************************** + // DEBUG + + /* + std::string debugDisplay = "X"; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + Modulator7Compact() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + configParam(RATE_PARAM, 0.f, 1.f, 0.5f, "Rate", "Hz", maxRate / minRate, minRate); + configInput(RATE_INPUT, "Rate"); + configParam(RATE_ATTENUV_PARAM, -1.f, 1.f, 0.f, "Rate CV", "%", 0, 100); + for (int i = 0; i < 7; i++) { + configParam(XRATE_PARAM+i, 0.f, 1.f, 0.5f, "xRate", "x", maxXrate / minXrate, minXrate); + configOutput(OUT_OUTPUT+i, ""); + } + } + + void onReset(const ResetEvent &e) override { + rateKnob = 0.5; + prevRateKnob = 1; + polyChans = 1; + for (int i = 0; i < 7; i++) { + waveSlope[i] = 1; + waveValue[i] = 0; + xRateKnob[i] = 1; + prevXrateKnob[i] = 0; + } + + waveCoeff = 0; + + Module::onReset(e); + } + + void onSampleRateChange() override { + sampleRateCoeff = APP->engine->getSampleRate() / 2; + } + + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "polyChans", json_integer(polyChans)); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* polyChansJ = json_object_get(rootJ, "polyChans"); + if (polyChansJ) + polyChans = json_integer_value(polyChansJ); + } + + + void setPreset(int preset) { + switch (preset) { + + case 0: // integer * + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.613835); // 2x + params[XRATE_PARAM+2].setValue(0.680424); // 3x + params[XRATE_PARAM+3].setValue(0.727670); // 4x + params[XRATE_PARAM+4].setValue(0.764317); // 5x + params[XRATE_PARAM+5].setValue(0.794259); // 6x + params[XRATE_PARAM+6].setValue(0.819576); // 7x + break; + + case 1: // integer / + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.386165); // 2x + params[XRATE_PARAM+2].setValue(0.319576); // 3x + params[XRATE_PARAM+3].setValue(0.272330); // 4x + params[XRATE_PARAM+4].setValue(0.235683); // 5x + params[XRATE_PARAM+5].setValue(0.205741); // 6x + params[XRATE_PARAM+6].setValue(0.180424); // 7x + break; + + case 2: // even * + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.613835); // 2x + params[XRATE_PARAM+2].setValue(0.727670); // 4x + params[XRATE_PARAM+3].setValue(0.794259); // 6x + params[XRATE_PARAM+4].setValue(0.841505); // 8x + params[XRATE_PARAM+5].setValue(0.878152); // 10x + params[XRATE_PARAM+6].setValue(0.908095); // 12x + break; + + case 3: // even / + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.386165); // 2x + params[XRATE_PARAM+2].setValue(0.272330); // 4x + params[XRATE_PARAM+3].setValue(0.205741); // 6x + params[XRATE_PARAM+4].setValue(0.158495); // 8x + params[XRATE_PARAM+5].setValue(0.121848); // 10x + params[XRATE_PARAM+6].setValue(0.091905); // 12x + break; + + case 4: // odd + + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.680424); // 3x + params[XRATE_PARAM+2].setValue(0.764317); // 5x + params[XRATE_PARAM+3].setValue(0.819576); // 7x + params[XRATE_PARAM+4].setValue(0.860849); // 9x + params[XRATE_PARAM+5].setValue(0.893805); // 11x + params[XRATE_PARAM+6].setValue(0.921240); // 13x + break; + + case 5: // odd - + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.319576); // 3x + params[XRATE_PARAM+2].setValue(0.235683); // 5x + params[XRATE_PARAM+3].setValue(0.180424); // 7x + params[XRATE_PARAM+4].setValue(0.139151); // 9x + params[XRATE_PARAM+5].setValue(0.106195); // 11x + params[XRATE_PARAM+6].setValue(0.078760); // 13x + break; + + case 6: // primes * + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.613835); // 2x + params[XRATE_PARAM+2].setValue(0.680424); // 3x + params[XRATE_PARAM+3].setValue(0.764317); // 5x + params[XRATE_PARAM+4].setValue(0.819576); // 7x + params[XRATE_PARAM+5].setValue(0.893805); // 11x + params[XRATE_PARAM+6].setValue(0.921240); // 13x + break; + + case 7: // prime / + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.386165); // 2x + params[XRATE_PARAM+2].setValue(0.319576); // 3x + params[XRATE_PARAM+3].setValue(0.235683); // 5x + params[XRATE_PARAM+4].setValue(0.180424); // 7x + params[XRATE_PARAM+5].setValue(0.106195); // 11x + params[XRATE_PARAM+6].setValue(0.078760); // 13x + break; + + case 8: // fibonacci * + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.613835); // 2x + params[XRATE_PARAM+2].setValue(0.680424); // 3x + params[XRATE_PARAM+3].setValue(0.764317); // 5x + params[XRATE_PARAM+4].setValue(0.841505); // 8x + params[XRATE_PARAM+5].setValue(0.921240); // 13x + params[XRATE_PARAM+6].setValue(1); // 21x + break; + + case 9: // fibonacci / + params[XRATE_PARAM+0].setValue(0.5); // 1x + params[XRATE_PARAM+1].setValue(0.386165); // 2x + params[XRATE_PARAM+2].setValue(0.319576); // 3x + params[XRATE_PARAM+3].setValue(0.235683); // 5x + params[XRATE_PARAM+4].setValue(0.158495); // 8x + params[XRATE_PARAM+5].setValue(0.078760); // 13x + params[XRATE_PARAM+6].setValue(0); // 21x + break; + } + + } + + static float convertXrateToSeconds(float cv) { + return minXrate * std::pow(maxXrate / minXrate, cv); + } + + static float convertRateToSeconds(float cv) { + return minRate * std::pow(maxRate / minRate, cv); + } + + void process(const ProcessArgs& args) override { + + rateKnob = params[RATE_PARAM].getValue(); + + if (rateKnob != prevRateKnob) { + rateProvv = convertRateToSeconds(rateKnob); + prevRateKnob = rateKnob; + } + + rate = rateProvv + (inputs[RATE_INPUT].getVoltage() * params[RATE_ATTENUV_PARAM].getValue() * 10); + + if (rate > 100) + rate = 100; + else if (rate < 0.01) + rate = 0.01; + + waveCoeff = rate / sampleRateCoeff; + + for (int i = 0; i < 6; i++) { + + xRateKnob[i] = params[XRATE_PARAM+i].getValue(); + + if (xRateKnob[i] != prevXrateKnob[i]) { + xRate[i] = convertXrateToSeconds(xRateKnob[i]); + prevXrateKnob[i] = xRateKnob[i]; + } + + waveValue[i] += xRate[i] * waveCoeff * waveSlope[i]; + + if (waveValue[i] > 1) { + waveSlope[i] = -1; + waveValue[i] = 2 - waveValue[i]; + } else if (waveValue[i] < 0) { + waveSlope[i] = 1; + waveValue[i] = -waveValue[i]; + } + + out = waveValue[i] * 10.f; + if (polyChans >= i){ + outputs[OUT_OUTPUT+6].setVoltage(out, i); + } + outputs[OUT_OUTPUT+i].setVoltage(out); + + } + + xRateKnob[6] = params[XRATE_PARAM+6].getValue(); + + if (xRateKnob[6] != prevXrateKnob[6]) { + xRate[6] = convertXrateToSeconds(xRateKnob[6]); + prevXrateKnob[6] = xRateKnob[6]; + } + + waveValue[6] += xRate[6] * waveCoeff * waveSlope[6]; + + if (waveValue[6] > 1) { + waveSlope[6] = -1; + waveValue[6] = 2 - waveValue[6]; + } else if (waveValue[6] < 0) { + waveSlope[6] = 1; + waveValue[6] = -waveValue[6]; + } + + out = waveValue[6] * 10.f; + + if (polyChans == 1) { + outputs[OUT_OUTPUT+6].setVoltage(out); + outputs[OUT_OUTPUT+6].setChannels(1); + } else { + outputs[OUT_OUTPUT+6].setVoltage(out, 6); + outputs[OUT_OUTPUT+6].setChannels(polyChans); + } + + + + + //debugDisplay = to_string(params[XRATE_PARAM].getValue()); + } +}; + +/* +struct Modulator7CompactDebugDisplay : TransparentWidget { + Modulator7Compact *module; + int frame = 0; + Modulator7CompactDebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + nvgTextBox(args.vg, 9, 6,120, module->debugDisplay.c_str(), NULL); + nvgTextBox(args.vg, 9, 16,120, module->debugDisplay2.c_str(), NULL); + nvgTextBox(args.vg, 129, 6,120, module->debugDisplay3.c_str(), NULL); + //nvgTextBox(args.vg, 129, 16,120, module->debugDisplay4.c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + +struct Modulator7CompactWidget : ModuleWidget { + Modulator7CompactWidget(Modulator7Compact* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/Modulator7Compact.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + /* + { + Modulator7CompactDebugDisplay *display = new Modulator7CompactDebugDisplay(); + display->box.pos = Vec(23, 3); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xRtKnob = 12 - 0.6 - 1; + const float yRtKnob = 21 + 1; + + const float xRateAtnv = 18.8 - 0.6; + const float yRateAtnv = 32 + 1; + + const float xRateIn = 11 - 0.6 - 1; + const float yRateIn = 39; + + const float yStart = 57; + constexpr float yStartShift = 10; + + const float xRt = 7.3 - 0.6; + const float xOut = 18.7; + + addParam(createParamCentered(mm2px(Vec(xRtKnob, yRtKnob)), module, Modulator7Compact::RATE_PARAM)); + addParam(createParamCentered(mm2px(Vec(xRateAtnv, yRateAtnv)), module, Modulator7Compact::RATE_ATTENUV_PARAM)); + addInput(createInputCentered(mm2px(Vec(xRateIn, yRateIn)), module, Modulator7Compact::RATE_INPUT)); + + for (int i = 0; i < 7; i++) { + addParam(createParamCentered(mm2px(Vec(xRt, yStart + (yStartShift * i))), module, Modulator7Compact::XRATE_PARAM+i)); + addOutput(createOutputCentered(mm2px(Vec(xOut, yStart + (yStartShift * i))), module, Modulator7Compact::OUT_OUTPUT+i)); + } + } + + void appendContextMenu(Menu* menu) override { + Modulator7Compact* module = dynamic_cast(this->module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createSubmenuItem("Polyphony on 7th out", std::to_string(module->polyChans), [=](Menu * menu) { + menu->addChild(createMenuItem("Monophonic", "", [=]() {module->polyChans = 1;})); + menu->addChild(createMenuItem("2", "", [=]() {module->polyChans = 2;})); + menu->addChild(createMenuItem("3", "", [=]() {module->polyChans = 3;})); + menu->addChild(createMenuItem("4", "", [=]() {module->polyChans = 4;})); + menu->addChild(createMenuItem("5", "", [=]() {module->polyChans = 5;})); + menu->addChild(createMenuItem("6", "", [=]() {module->polyChans = 6;})); + menu->addChild(createMenuItem("7", "", [=]() {module->polyChans = 7;})); + })); + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("xRate Presets")); + + menu->addChild(createMenuItem("Integer *", "", [=]() {module->setPreset(0);})); + menu->addChild(createMenuItem("Integer /", "", [=]() {module->setPreset(1);})); + menu->addChild(createMenuItem("Even *", "", [=]() {module->setPreset(2);})); + menu->addChild(createMenuItem("Even /", "", [=]() {module->setPreset(3);})); + menu->addChild(createMenuItem("Odd *", "", [=]() {module->setPreset(4);})); + menu->addChild(createMenuItem("Odd /", "", [=]() {module->setPreset(5);})); + menu->addChild(createMenuItem("Prime *", "", [=]() {module->setPreset(6);})); + menu->addChild(createMenuItem("Prime /", "", [=]() {module->setPreset(7);})); + menu->addChild(createMenuItem("Fibonacci *", "", [=]() {module->setPreset(8);})); + menu->addChild(createMenuItem("Fibonacci /", "", [=]() {module->setPreset(9);})); + + } +}; + +Model* modelModulator7Compact = createModel("Modulator7Compact"); \ No newline at end of file diff --git a/src/PolyMuter16.cpp b/src/PolyMuter16.cpp new file mode 100644 index 0000000..03c422b --- /dev/null +++ b/src/PolyMuter16.cpp @@ -0,0 +1,449 @@ +#define COLOR_LCD_RED 0xdd, 0x33, 0x33, 0xff + +#include "plugin.hpp" + +using namespace std; + +struct PolyMuter16 : Module { + enum ParamId { + FADE_PARAM, + ENUMS(MUTE_PARAM, 16), + PARAMS_LEN + }; + enum InputId { + IN_INPUT, + INPUTS_LEN + }; + enum OutputId { + OUT_OUTPUT, + OUTPUTS_LEN + }; + enum LightId { + ENUMS(MUTE_LIGHT, 16), + LIGHTS_LEN + }; + + + //************************************************************** + // DEBUG + + /* + std::string debugDisplay = "X"; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + bool initStart = false; + + int inChans = 0; + //int outChans = 0; + //int chan; + float mute[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + float prevMute[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + float ampValue[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float ampDelta[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + bool fading[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; + + float fadeKnob = 0.f; + float prevFadeKnob = 1.f; + + float fadeValue = 0; + + const float noEnvTime = 0.00101; + + PolyMuter16() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + + configInput(IN_INPUT, "Poly"); + configParam(FADE_PARAM, 0.f, 1.f, 0.25f, "Fade", "ms", 10000.f, 1.f); + configOutput(OUT_OUTPUT, "Poly"); + + configSwitch(MUTE_PARAM, 0.f, 1.f, 0.f, "Mute #1", {"Off", "On"}); + configSwitch(MUTE_PARAM+1, 0.f, 1.f, 0.f, "Mute #2", {"Off", "On"}); + configSwitch(MUTE_PARAM+2, 0.f, 1.f, 0.f, "Mute #3", {"Off", "On"}); + configSwitch(MUTE_PARAM+3, 0.f, 1.f, 0.f, "Mute #4", {"Off", "On"}); + configSwitch(MUTE_PARAM+4, 0.f, 1.f, 0.f, "Mute #5", {"Off", "On"}); + configSwitch(MUTE_PARAM+5, 0.f, 1.f, 0.f, "Mute #+", {"Off", "On"}); + configSwitch(MUTE_PARAM+6, 0.f, 1.f, 0.f, "Mute #7", {"Off", "On"}); + configSwitch(MUTE_PARAM+7, 0.f, 1.f, 0.f, "Mute #8", {"Off", "On"}); + configSwitch(MUTE_PARAM+8, 0.f, 1.f, 0.f, "Mute #9", {"Off", "On"}); + configSwitch(MUTE_PARAM+9, 0.f, 1.f, 0.f, "Mute #10", {"Off", "On"}); + configSwitch(MUTE_PARAM+10, 0.f, 1.f, 0.f, "Mute #11", {"Off", "On"}); + configSwitch(MUTE_PARAM+11, 0.f, 1.f, 0.f, "Mute #12", {"Off", "On"}); + configSwitch(MUTE_PARAM+12, 0.f, 1.f, 0.f, "Mute #13", {"Off", "On"}); + configSwitch(MUTE_PARAM+13, 0.f, 1.f, 0.f, "Mute #14", {"Off", "On"}); + configSwitch(MUTE_PARAM+14, 0.f, 1.f, 0.f, "Mute #15", {"Off", "On"}); + configSwitch(MUTE_PARAM+15, 0.f, 1.f, 0.f, "Mute #16", {"Off", "On"}); + + } + + void onReset(const ResetEvent &e) override { + initStart = false; + + fadeKnob = 0.f; + prevFadeKnob = 1.f; + fadeValue = 0; + + for (int i = 0; i < 16; i++) { + mute[i] = 0; + prevMute[i] = 0; + ampValue[i] = 1; + ampDelta[i] = 1; + fading[i] = false; + } + + Module::onReset(e); + } + + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "initStart", json_boolean(initStart)); + json_object_set_new(rootJ, "mute1", json_real(params[MUTE_PARAM].getValue())); + json_object_set_new(rootJ, "mute2", json_real(params[MUTE_PARAM+1].getValue())); + json_object_set_new(rootJ, "mute3", json_real(params[MUTE_PARAM+2].getValue())); + json_object_set_new(rootJ, "mute4", json_real(params[MUTE_PARAM+3].getValue())); + json_object_set_new(rootJ, "mute5", json_real(params[MUTE_PARAM+4].getValue())); + json_object_set_new(rootJ, "mute6", json_real(params[MUTE_PARAM+5].getValue())); + json_object_set_new(rootJ, "mute7", json_real(params[MUTE_PARAM+6].getValue())); + json_object_set_new(rootJ, "mute8", json_real(params[MUTE_PARAM+7].getValue())); + json_object_set_new(rootJ, "mute9", json_real(params[MUTE_PARAM+8].getValue())); + json_object_set_new(rootJ, "mute10", json_real(params[MUTE_PARAM+9].getValue())); + json_object_set_new(rootJ, "mute11", json_real(params[MUTE_PARAM+10].getValue())); + json_object_set_new(rootJ, "mute12", json_real(params[MUTE_PARAM+11].getValue())); + json_object_set_new(rootJ, "mute13", json_real(params[MUTE_PARAM+12].getValue())); + json_object_set_new(rootJ, "mute14", json_real(params[MUTE_PARAM+13].getValue())); + json_object_set_new(rootJ, "mute15", json_real(params[MUTE_PARAM+14].getValue())); + json_object_set_new(rootJ, "mute16", json_real(params[MUTE_PARAM+15].getValue())); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* initStartJ = json_object_get(rootJ, "initStart"); + if (initStartJ) + initStart = json_boolean_value(initStartJ); + + if (initStart) { + for (int i = 0; i < 8; i++) { + params[MUTE_PARAM+i].setValue(0.f); + } + } else { + json_t* mute1J = json_object_get(rootJ, "mute1"); + if (mute1J){ + mute[0] = json_real_value(mute1J); + if (mute[0] == 1.f) { + prevMute[0] = 1; + ampValue[0] = 0; + } + } + + json_t* mute2J = json_object_get(rootJ, "mute2"); + if (mute2J){ + mute[1] = json_real_value(mute2J); + if (mute[1] == 1.f) { + prevMute[1] = 1; + ampValue[1] = 0; + } + } + + json_t* mute3J = json_object_get(rootJ, "mute3"); + if (mute3J){ + mute[2] = json_real_value(mute3J); + if (mute[2] == 1.f) { + prevMute[2] = 1; + ampValue[2] = 0; + } + } + + json_t* mute4J = json_object_get(rootJ, "mute4"); + if (mute4J){ + mute[3] = json_real_value(mute4J); + if (mute[3] == 1.f) { + prevMute[3] = 1; + ampValue[3] = 0; + } + } + + json_t* mute5J = json_object_get(rootJ, "mute5"); + if (mute5J){ + mute[4] = json_real_value(mute5J); + if (mute[4] == 1.f) { + prevMute[4] = 1; + ampValue[4] = 0; + } + } + + json_t* mute6J = json_object_get(rootJ, "mute6"); + if (mute6J){ + mute[5] = json_real_value(mute6J); + if (mute[5] == 1.f) { + prevMute[5] = 1; + ampValue[5] = 0; + } + } + + json_t* mute7J = json_object_get(rootJ, "mute7"); + if (mute7J){ + mute[6] = json_real_value(mute7J); + if (mute[6] == 1.f) { + prevMute[6] = 1; + ampValue[6] = 0; + } + } + + json_t* mute8J = json_object_get(rootJ, "mute8"); + if (mute8J){ + mute[7] = json_real_value(mute8J); + if (mute[7] == 1.f) { + prevMute[7] = 1; + ampValue[7] = 0; + } + } + + json_t* mute9J = json_object_get(rootJ, "mute9"); + if (mute9J){ + mute[8] = json_real_value(mute9J); + if (mute[8] == 1.f) { + prevMute[8] = 1; + ampValue[8] = 0; + } + } + + json_t* mute10J = json_object_get(rootJ, "mute10"); + if (mute10J){ + mute[9] = json_real_value(mute9J); + if (mute[9] == 1.f) { + prevMute[9] = 1; + ampValue[9] = 0; + } + } + + json_t* mute11J = json_object_get(rootJ, "mute11"); + if (mute11J){ + mute[10] = json_real_value(mute11J); + if (mute[10] == 1.f) { + prevMute[10] = 1; + ampValue[10] = 0; + } + } + + json_t* mute12J = json_object_get(rootJ, "mute12"); + if (mute12J){ + mute[11] = json_real_value(mute12J); + if (mute[11] == 1.f) { + prevMute[11] = 1; + ampValue[11] = 0; + } + } + + json_t* mute13J = json_object_get(rootJ, "mute13"); + if (mute13J){ + mute[12] = json_real_value(mute13J); + if (mute[12] == 1.f) { + prevMute[12] = 1; + ampValue[12] = 0; + } + } + + json_t* mute14J = json_object_get(rootJ, "mute14"); + if (mute14J){ + mute[13] = json_real_value(mute14J); + if (mute[13] == 1.f) { + prevMute[13] = 1; + ampValue[13] = 0; + } + } + + json_t* mute15J = json_object_get(rootJ, "mute15"); + if (mute15J){ + mute[14] = json_real_value(mute15J); + if (mute[14] == 1.f) { + prevMute[14] = 1; + ampValue[14] = 0; + } + } + + json_t* mute16J = json_object_get(rootJ, "mute16"); + if (mute16J){ + mute[15] = json_real_value(mute16J); + if (mute[15] == 1.f) { + prevMute[15] = 1; + ampValue[15] = 0; + } + } + } + } + + void process(const ProcessArgs& args) override { + + fadeKnob = params[FADE_PARAM].getValue(); + + if (fadeKnob != prevFadeKnob) { + fadeValue = std::pow(10000.f, fadeKnob) / 1000; + prevFadeKnob = fadeKnob; + } + + inChans = std::max(1, inputs[IN_INPUT].getChannels()); + + for (int c = 0; c < 16; c++) { + mute[c] = params[MUTE_PARAM+c].getValue(); + lights[MUTE_LIGHT+c].setBrightness(mute[c]); + + if (mute[c] && !prevMute[c]) { // mute chan + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = -1 / fadeValue / args.sampleRate; + } else { + ampValue[c] = 0.f; + } + + } else if (!mute[c] && prevMute[c]) { // unmute chan + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = 1 / fadeValue / args.sampleRate; + } else { + ampValue[c] = 1.f; + } + } + prevMute[c] = mute[c]; + + if (fading[c]) { + ampValue[c] += ampDelta[c]; + if (ampValue[c] > 1.f) { + fading[c] = false; + ampValue[c] = 1.f; + } else if (ampValue[c] < 0.f) { + fading[c] = false; + ampValue[c] = 0.f; + } + } + + outputs[OUT_OUTPUT].setVoltage(inputs[IN_INPUT].getVoltage(c) * ampValue[c], c); + } + + outputs[OUT_OUTPUT].setChannels(inChans); + + } +}; + +struct PolyMuter16DisplayChan : TransparentWidget { + PolyMuter16 *module; + int frame = 0; + PolyMuter16DisplayChan() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/DSEG14ClassicMini-BoldItalic.ttf")); + nvgFontSize(args.vg, 16); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + if (module->inChans > 9) + nvgTextBox(args.vg, 4, 20.8, 60, to_string(module->inChans).c_str(), NULL); + else + nvgTextBox(args.vg, 17, 20.8, 60, to_string(module->inChans).c_str(), NULL); + } + } + Widget::drawLayer(args, layer); + } +}; +/* +struct PolyMuter16DebugDisplay : TransparentWidget { + PolyMuter16 *module; + int frame = 0; + PolyMuter16DebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + nvgTextBox(args.vg, 0, 0,120, module->debugDisplay.c_str(), NULL); + //nvgTextBox(args.vg, 9, 16,120, module->debugDisplay2.c_str(), NULL); + //nvgTextBox(args.vg, 129, 6,120, module->debugDisplay3.c_str(), NULL); + //nvgTextBox(args.vg, 129, 16,120, module->debugDisplay4.c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + +struct PolyMuter16Widget : ModuleWidget { + PolyMuter16Widget(PolyMuter16* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/PolyMuter16.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + { + PolyMuter16DisplayChan *display = new PolyMuter16DisplayChan(); + display->box.pos = mm2px(Vec(20.9, 8.2)); + display->box.size = mm2px(Vec(13.2, 8.6)); + display->module = module; + addChild(display); + } + /* + { + PolyMuter16DebugDisplay *display = new PolyMuter16DebugDisplay(); + display->box.pos = Vec(3, 25); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xCenter = 10.1; + const float xLeft = 12; + const float xRight = 21; + + const float yIn = 17; + const float yFade = 30; + const float xOut = 27.5; + const float yOut = 28; + + constexpr float yStart = 41.5; + constexpr float yStart2 = 46.5; + constexpr float y = 10.f; + + + addInput(createInputCentered(mm2px(Vec(xCenter, yIn)), module, PolyMuter16::IN_INPUT)); + + addParam(createParamCentered(mm2px(Vec(xCenter, yFade)), module, PolyMuter16::FADE_PARAM)); + + addOutput(createOutputCentered(mm2px(Vec(xOut, yOut)), module, PolyMuter16::OUT_OUTPUT)); + + for (int i = 0; i < 8; i++) { + addParam(createLightParamCentered>(mm2px(Vec(xLeft, yStart+(i*y))), module, PolyMuter16::MUTE_PARAM+i, PolyMuter16::MUTE_LIGHT+i)); + } + + for (int i = 0; i < 8; i++) { + addParam(createLightParamCentered>(mm2px(Vec(xRight, yStart2+(i*y))), module, PolyMuter16::MUTE_PARAM+8+i, PolyMuter16::MUTE_LIGHT+8+i)); + } + + } + + void appendContextMenu(Menu* menu) override { + PolyMuter16* module = dynamic_cast(this->module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Initialize on Start", "", &module->initStart)); + } +}; + +Model* modelPolyMuter16 = createModel("PolyMuter16"); \ No newline at end of file diff --git a/src/PolyMuter16Plus.cpp b/src/PolyMuter16Plus.cpp new file mode 100644 index 0000000..670cf30 --- /dev/null +++ b/src/PolyMuter16Plus.cpp @@ -0,0 +1,779 @@ +#define UNMUTED 0 +#define SOLOED 1 +#define MUTED_SOLOED 2 +#define MUTED 3 + +#define COLOR_LCD_RED 0xdd, 0x33, 0x33, 0xff + +#include "plugin.hpp" + + +using namespace std; + +// ---------------------------------------------------------------------------- + +template +struct LightEmittingWidget : BASE { + virtual bool isLit() = 0; + + void drawLayer(const typename BASE::DrawArgs& args, int layer) override { + if (layer == 1 && isLit()) { + drawLit(args); + } + BASE::drawLayer(args, layer); + } + + virtual void drawLit(const typename BASE::DrawArgs& args) {} +}; + +// ---------------------------------------------------------------------------- + +struct PM16SoloMuteButton : LightEmittingWidget { + std::vector> _frames; + SvgWidget* _svgWidget; + CircularShadow* shadow = NULL; + + PM16SoloMuteButton(); + void onButton(const event::Button& e) override; + void onChange(const event::Change& e) override; + bool isLit() override; + void draw(const DrawArgs& args) override; + void drawLit(const DrawArgs& args) override; +}; + + +// ---------------------------------------------------------------------------- + + +PM16SoloMuteButton::PM16SoloMuteButton() { + shadow = new CircularShadow(); + addChild(shadow); + + _svgWidget = new SvgWidget(); + addChild(_svgWidget); + + auto svg = APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_0.svg")); + _frames.push_back(svg); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_1_green.svg"))); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_3_red_green.svg"))); + //_frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_2_red.svg"))); + //_frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_1_green.svg"))); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_2_red.svg"))); + _svgWidget->setSvg(svg); + box.size = _svgWidget->box.size; + shadow->box.size = _svgWidget->box.size; + shadow->blurRadius = 1.0; + shadow->box.pos = Vec(0.0, 1.0); +} + +void PM16SoloMuteButton::onButton(const event::Button& e) { + if (!getParamQuantity() || !(e.action == GLFW_PRESS && (e.mods & RACK_MOD_MASK) == 0)) { + ParamWidget::onButton(e); + return; + } + + + float value = getParamQuantity()->getValue(); + + // 0 unmuted + // 1 soloed + // 2 soloed muted + // 3 muted + + + if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + if (value == 0.f) + getParamQuantity()->setValue(1.f); + else if (value == 1.f) + getParamQuantity()->setValue(0.f); + else if (value == 2.f) + getParamQuantity()->setValue(3.f); + else + getParamQuantity()->setValue(2.f); + } + else if (value == 0) { + getParamQuantity()->setValue(3.f); + } + else { + getParamQuantity()->setValue(0.0f); + } + + + /* + if (value >= 2.0f) { + //if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + getParamQuantity()->setValue(value + 2.0f); + } + else if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + //else if (value >= 2.0f) { + //getParamQuantity()->setValue(value - 2.0f); + getParamQuantity()->setValue(value - 2.0f); + } + else { + getParamQuantity()->setValue(value > 0.5f ? 0.0f : 1.0f); + } + */ + + if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + e.consume(this); + } else { + ParamWidget::onButton(e); + } +} + +void PM16SoloMuteButton::onChange(const event::Change& e) { + assert(_frames.size() == 4); + if (getParamQuantity()) { + float value = getParamQuantity()->getValue(); + assert(value >= 0.0f && value <= 3.0f); + _svgWidget->setSvg(_frames[(int)value]); + } + ParamWidget::onChange(e); +} + +bool PM16SoloMuteButton::isLit() { + return module && !module->isBypassed() && getParamQuantity() && getParamQuantity()->getValue() > 0.0f; +} + +void PM16SoloMuteButton::draw(const DrawArgs& args) { + if (!isLit() || !getParamQuantity() || getParamQuantity()->getValue() < 1.0f) { + ParamWidget::draw(args); + } +} + +void PM16SoloMuteButton::drawLit(const DrawArgs& args) { + if (getParamQuantity() && getParamQuantity()->getValue() >= 1.0f) { + ParamWidget::draw(args); + } +} +// ---------------------- + +struct PolyMuter16Plus : Module { + enum ParamId { + FADE_PARAM, + ENUMS(MUTE_PARAM, 16), + PARAMS_LEN + }; + enum InputId { + IN_INPUT, + INPUTS_LEN + }; + enum OutputId { + OUT_OUTPUT, + OUTPUTS_LEN + }; + enum LightId { + //ENUMS(MUTE_LIGHT, 16), + LIGHTS_LEN + }; + + + //************************************************************** + // DEBUG + + /* + std::string debugDisplay = "X"; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + std::string debugDisplay5 = "X"; + std::string debugDisplay6 = "X"; + std::string debugDisplay7 = "X"; + std::string debugDisplay8 = "X"; + std::string debugDisplay9 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + //std::string db[4] = {"unm", "sol", "m-s", "mut"}; + + bool initStart = false; + + //int inChans = 0; + int outChans = 0; + //int chan; + int buttonValue[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int prevButtonValue[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int status[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int prevStatus[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + float ampValue[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + float ampDelta[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + bool fading[16] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; + + float fadeKnob = 0.f; + float prevFadeKnob = 1.f; + + float fadeValue = 0; + + int soloChans = 0; + bool globalSolo = false; + bool prevGlobalSolo = false; + + long sampleRate = APP->engine->getSampleRate(); + + const float noEnvTime = 0.00101; + + PolyMuter16Plus() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + + configParam(FADE_PARAM, 0.f, 1.f, 0.25f, "Fade", "ms", 10000.f, 1.f); + configInput(IN_INPUT, "Poly"); + + configSwitch(MUTE_PARAM, 0.0f, 3.0f, 0.0f, "Mute #1", {"Unmuted", "Solo", "Solo", "Muted"}); + configSwitch(MUTE_PARAM+1, 0.0f, 3.0f, 0.0f, "Mute #2", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+2, 0.0f, 3.0f, 0.0f, "Mute #3", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+3, 0.0f, 3.0f, 0.0f, "Mute #4", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+4, 0.0f, 3.0f, 0.0f, "Mute #5", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+5, 0.0f, 3.0f, 0.0f, "Mute #6", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+6, 0.0f, 3.0f, 0.0f, "Mute #7", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+7, 0.0f, 3.0f, 0.0f, "Mute #8", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+8, 0.0f, 3.0f, 0.0f, "Mute #9", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+9, 0.0f, 3.0f, 0.0f, "Mute #10", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+10, 0.0f, 3.0f, 0.0f, "Mute #11", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+11, 0.0f, 3.0f, 0.0f, "Mute #12", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+12, 0.0f, 3.0f, 0.0f, "Mute #13", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+13, 0.0f, 3.0f, 0.0f, "Mute #14", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+14, 0.0f, 3.0f, 0.0f, "Mute #15", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+15, 0.0f, 3.0f, 0.0f, "Mute #16", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + + configOutput(OUT_OUTPUT, "Poly"); + } + + void onReset(const ResetEvent &e) override { + initStart = false; + + fadeKnob = 0.f; + prevFadeKnob = 1.f; + fadeValue = 0; + + soloChans = 0; + globalSolo = false; + prevGlobalSolo = false; + + for (int i = 0; i < 16; i++) { + buttonValue[i] = UNMUTED; + prevButtonValue[i] = UNMUTED; + status[i] = UNMUTED; + prevStatus[i] = UNMUTED; + ampValue[i] = 1; + ampDelta[i] = 1; + fading[i] = false; + } + + Module::onReset(e); + } + + void onSampleRateChange() override { + sampleRate = APP->engine->getSampleRate(); + } + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "initStart", json_boolean(initStart)); + json_object_set_new(rootJ, "status1", json_integer(params[MUTE_PARAM].getValue())); + json_object_set_new(rootJ, "status2", json_integer(params[MUTE_PARAM+1].getValue())); + json_object_set_new(rootJ, "status3", json_integer(params[MUTE_PARAM+2].getValue())); + json_object_set_new(rootJ, "status4", json_integer(params[MUTE_PARAM+3].getValue())); + json_object_set_new(rootJ, "status5", json_integer(params[MUTE_PARAM+4].getValue())); + json_object_set_new(rootJ, "status6", json_integer(params[MUTE_PARAM+5].getValue())); + json_object_set_new(rootJ, "status7", json_integer(params[MUTE_PARAM+6].getValue())); + json_object_set_new(rootJ, "status8", json_integer(params[MUTE_PARAM+7].getValue())); + json_object_set_new(rootJ, "status9", json_integer(params[MUTE_PARAM+8].getValue())); + json_object_set_new(rootJ, "status10", json_integer(params[MUTE_PARAM+9].getValue())); + json_object_set_new(rootJ, "status11", json_integer(params[MUTE_PARAM+10].getValue())); + json_object_set_new(rootJ, "status12", json_integer(params[MUTE_PARAM+11].getValue())); + json_object_set_new(rootJ, "status13", json_integer(params[MUTE_PARAM+12].getValue())); + json_object_set_new(rootJ, "status14", json_integer(params[MUTE_PARAM+13].getValue())); + json_object_set_new(rootJ, "status15", json_integer(params[MUTE_PARAM+14].getValue())); + json_object_set_new(rootJ, "status16", json_integer(params[MUTE_PARAM+15].getValue())); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* initStartJ = json_object_get(rootJ, "initStart"); + if (initStartJ) + initStart = json_boolean_value(initStartJ); + + if (initStart) { + for (int i = 0; i < 8; i++) { + params[MUTE_PARAM+i].setValue(0.f); + } + } else { + json_t* status1J = json_object_get(rootJ, "status1"); + if (status1J){ + buttonValue[0] = json_integer_value(status1J); + firstStatusCheck(0); + } + + json_t* status2J = json_object_get(rootJ, "status2"); + if (status2J){ + buttonValue[1] = json_integer_value(status2J); + firstStatusCheck(1); + } + + json_t* status3J = json_object_get(rootJ, "status3"); + if (status3J){ + buttonValue[2] = json_integer_value(status3J); + firstStatusCheck(2); + } + + json_t* status4J = json_object_get(rootJ, "status4"); + if (status4J){ + buttonValue[3] = json_integer_value(status4J); + firstStatusCheck(3); + } + + json_t* status5J = json_object_get(rootJ, "status5"); + if (status5J){ + buttonValue[4] = json_integer_value(status5J); + firstStatusCheck(4); + } + + json_t* status6J = json_object_get(rootJ, "status6"); + if (status6J){ + buttonValue[5] = json_integer_value(status6J); + firstStatusCheck(5); + } + + json_t* status7J = json_object_get(rootJ, "status7"); + if (status7J){ + buttonValue[6] = json_integer_value(status7J); + firstStatusCheck(6); + + } + + json_t* status8J = json_object_get(rootJ, "status8"); + if (status8J){ + buttonValue[7] = json_integer_value(status8J); + firstStatusCheck(7); + } + + json_t* status9J = json_object_get(rootJ, "status9"); + if (status9J){ + buttonValue[8] = json_integer_value(status9J); + firstStatusCheck(8); + } + + json_t* status10J = json_object_get(rootJ, "status10"); + if (status10J){ + buttonValue[9] = json_integer_value(status10J); + firstStatusCheck(9); + } + + json_t* status11J = json_object_get(rootJ, "status11"); + if (status11J){ + buttonValue[10] = json_integer_value(status11J); + firstStatusCheck(10); + } + + json_t* status12J = json_object_get(rootJ, "status12"); + if (status12J){ + buttonValue[11] = json_integer_value(status12J); + firstStatusCheck(11); + } + + json_t* status13J = json_object_get(rootJ, "status13"); + if (status13J){ + buttonValue[12] = json_integer_value(status13J); + firstStatusCheck(12); + } + + json_t* status14J = json_object_get(rootJ, "status14"); + if (status14J){ + buttonValue[13] = json_integer_value(status14J); + firstStatusCheck(13); + } + + json_t* status15J = json_object_get(rootJ, "status15"); + if (status15J){ + buttonValue[14] = json_integer_value(status15J); + firstStatusCheck(14); + } + + json_t* status16J = json_object_get(rootJ, "status16"); + if (status16J){ + buttonValue[15] = json_integer_value(status16J); + firstStatusCheck(15); + } + + } + } + + void inline firstStatusCheck(int c) { + if (buttonValue[c] >= 0 && buttonValue[c] < 4) { + switch (buttonValue[c]) { + case MUTED: + prevButtonValue[c] = MUTED; + prevStatus[c] = MUTED; + status[c] = MUTED; + ampValue[c] = 0; + break; + + case SOLOED: + prevGlobalSolo = false; + globalSolo = true; + prevStatus[c] = UNMUTED; + status[c] = SOLOED; + ampValue[c] = 1; + break; + + case MUTED_SOLOED: + prevGlobalSolo = false; + globalSolo = true; + prevStatus[c] = MUTED; + status[c] = MUTED_SOLOED; + ampValue[c] = 1; + break; + } + } else { + buttonValue[c] = 0; + } + + } + + void inline fadeIn(int c) { + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = 1 / fadeValue / sampleRate; + } else { + ampValue[c] = 1.f; + } + } + + void inline fadeOut(int c) { + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = -1 / fadeValue / sampleRate; + } else { + ampValue[c] = 0.f; + } + } + + void process(const ProcessArgs& args) override { + + fadeKnob = params[FADE_PARAM].getValue(); + + if (fadeKnob != prevFadeKnob) { + fadeValue = std::pow(10000.f, fadeKnob) / 1000; + prevFadeKnob = fadeKnob; + } + + if (globalSolo != prevGlobalSolo) { + if (globalSolo) { // solo on + for (int c = 0; c < 16; c++) { + if (status[c] == UNMUTED) { + fadeOut(c); + prevStatus[c] = status[c]; + + } else if (status[c] == MUTED) { + fadeOut(c); + prevStatus[c] = status[c]; + } + } + } else { // solo off + for (int c = 0; c < 16; c++) { + if (prevStatus[c] == UNMUTED) { + fadeIn(c); + + status[c] = UNMUTED; + + } else if (prevStatus[c] == MUTED) { + fadeOut(c); + + status[c] = MUTED; + } + + } + } + prevGlobalSolo = globalSolo; + } + + for (int c = 0; c < 16; c++) { + + // 0 unmuted + // 1 soloed + // 2 soloed muted + // 3 muted + + buttonValue[c] = int(params[MUTE_PARAM+c].getValue()); + + if (buttonValue[c] != prevButtonValue[c]) { + switch (prevButtonValue[c]) { + case UNMUTED: + switch (buttonValue[c]) { + case MUTED: + prevStatus[c] = MUTED; + fadeOut(c); + status[c] = MUTED; + break; + + case SOLOED: + soloChans++; + fadeIn(c); + status[c] = SOLOED; + if (!globalSolo) + prevStatus[c] = UNMUTED; + break; + + case MUTED_SOLOED: + soloChans++; + fadeIn(c); + status[c] = MUTED_SOLOED; + break; + + } + + break; + + // ------------------------------------- + + case MUTED: + switch (buttonValue[c]) { + case UNMUTED: + if (!globalSolo) + fadeIn(c); + prevStatus[c] = UNMUTED; + + + status[c] = UNMUTED; + + break; + + case SOLOED: + soloChans++; + fadeIn(c); + status[c] = SOLOED; + break; + + case MUTED_SOLOED: + soloChans++; + fadeIn(c); + status[c] = MUTED_SOLOED; + if (!globalSolo) + prevStatus[c] = MUTED; + break; + } + + break; + + // ------------------------------------- + + case MUTED_SOLOED: + switch (buttonValue[c]) { + case UNMUTED: + soloChans--; + if (globalSolo) { + if (prevStatus[c] == MUTED) { + fadeOut(c); + } else + fadeIn(c); + prevStatus[c] = UNMUTED; + } else { + fadeIn(c); + } + status[c] = UNMUTED; + break; + + case MUTED: + soloChans--; + fadeOut(c); + status[c] = MUTED; + break; + } + + break; + + // ------------------------------------- + + case SOLOED: + switch (buttonValue[c]) { + + case UNMUTED: + if (!globalSolo) { + fadeIn(c); + } else { + fadeOut(c); + } + soloChans--; + + status[c] = UNMUTED; + break; + + case MUTED: + fadeOut(c); + soloChans--; + status[c] = MUTED; + break; + + } + + break; + + } + } + prevButtonValue[c] = buttonValue[c]; + } + + if (soloChans == 0) { + globalSolo = false; + } else { + globalSolo = true; + } + + /* + debugDisplay = db[prevButtonValue[0]]; + debugDisplay2 = db[buttonValue[0]]; + debugDisplay3 = db[prevStatus[0]]; + debugDisplay4 = db[status[0]]; + debugDisplay5 = db[prevButtonValue[1]]; + debugDisplay6 = db[buttonValue[1]]; + debugDisplay7 = db[prevStatus[1]]; + debugDisplay8 = db[status[1]]; + debugDisplay9 = to_string(soloChans); + */ + + outChans = std::max(1, inputs[IN_INPUT].getChannels()); + + for (int c = 0; c < outChans; c++) { + + if (fading[c]) { + ampValue[c] += ampDelta[c]; + if (ampValue[c] > 1.f) { + fading[c] = false; + ampValue[c] = 1.f; + } else if (ampValue[c] < 0.f) { + fading[c] = false; + ampValue[c] = 0.f; + } + } + + outputs[OUT_OUTPUT].setVoltage(inputs[IN_INPUT].getVoltage(c) * ampValue[c], c); + } + + outputs[OUT_OUTPUT].setChannels(outChans); + + } +}; + +struct PolyMuter16PlusDisplayChan : TransparentWidget { + PolyMuter16Plus *module; + int frame = 0; + PolyMuter16PlusDisplayChan() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/DSEG14ClassicMini-BoldItalic.ttf")); + nvgFontSize(args.vg, 16); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + //if (module->inChans > 9) + if (module->outChans > 9) + nvgTextBox(args.vg, 4, 20.8, 60, to_string(module->outChans).c_str(), NULL); + else + nvgTextBox(args.vg, 17, 20.8, 60, to_string(module->outChans).c_str(), NULL); + } + } + Widget::drawLayer(args, layer); + } +}; +/* +struct PolyMuter16PlusDebugDisplay : TransparentWidget { + PolyMuter16Plus *module; + int frame = 0; + PolyMuter16PlusDebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + nvgTextBox(args.vg, 0, 0,120, module->debugDisplay.c_str(), NULL); + //nvgTextBox(args.vg, 9, 16,120, module->debugDisplay2.c_str(), NULL); + //nvgTextBox(args.vg, 129, 6,120, module->debugDisplay3.c_str(), NULL); + //nvgTextBox(args.vg, 129, 16,120, module->debugDisplay4.c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + +struct PolyMuter16PlusWidget : ModuleWidget { + PolyMuter16PlusWidget(PolyMuter16Plus* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/PolyMuter16Plus.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, 0))); + addChild(createWidget(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + { + PolyMuter16PlusDisplayChan *display = new PolyMuter16PlusDisplayChan(); + display->box.pos = mm2px(Vec(20.9, 8.2)); + display->box.size = mm2px(Vec(13.2, 8.6)); + display->module = module; + addChild(display); + } + /* + { + PolyMuter16DebugDisplay *display = new PolyMuter16DebugDisplay(); + display->box.pos = Vec(3, 25); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xCenter = 10.1; + const float xLeft = 12; + const float xRight = 21; + + const float yIn = 17; + const float yFade = 30; + const float xOut = 27.5; + const float yOut = 28; + + constexpr float yStart = 41.5; + constexpr float yStart2 = 46.5; + constexpr float y = 10.f; + + + addInput(createInputCentered(mm2px(Vec(xCenter, yIn)), module, PolyMuter16Plus::IN_INPUT)); + + addParam(createParamCentered(mm2px(Vec(xCenter, yFade)), module, PolyMuter16Plus::FADE_PARAM)); + + addOutput(createOutputCentered(mm2px(Vec(xOut, yOut)), module, PolyMuter16Plus::OUT_OUTPUT)); + + for (int i = 0; i < 8; i++) { + addParam(createParamCentered(mm2px(Vec(xLeft, yStart+(i*y))), module, PolyMuter16Plus::MUTE_PARAM+i)); + } + + for (int i = 0; i < 8; i++) { + addParam(createParamCentered(mm2px(Vec(xRight, yStart2+(i*y))), module, PolyMuter16Plus::MUTE_PARAM+8+i)); + } + + } + + void appendContextMenu(Menu* menu) override { + PolyMuter16Plus* module = dynamic_cast(this->module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("Right-click on buttons")); + menu->addChild(createMenuLabel("to SOLO channel")); + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Initialize on Start", "", &module->initStart)); + } +}; + +Model* modelPolyMuter16Plus = createModel("PolyMuter16Plus"); \ No newline at end of file diff --git a/src/PolyMuter8.cpp b/src/PolyMuter8.cpp new file mode 100644 index 0000000..1119bd9 --- /dev/null +++ b/src/PolyMuter8.cpp @@ -0,0 +1,391 @@ +#define COLOR_LCD_RED 0xdd, 0x33, 0x33, 0xff + +#include "plugin.hpp" + +using namespace std; + +struct PolyMuter8 : Module { + enum ParamId { + FADE_PARAM, + ENUMS(MUTE_PARAM, 8), + PARAMS_LEN + }; + enum InputId { + IN_INPUT, + INPUTS_LEN + }; + enum OutputId { + OUT_OUTPUT, + OUTPUTS_LEN + }; + enum LightId { + ENUMS(MUTE_LIGHT, 8), + LIGHTS_LEN + }; + + + //************************************************************** + // DEBUG + + /* + std::string debugDisplay = "X"; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + bool initStart = false; + + int inChans = 0; + int outChans = 0; + int chan; + float mute[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + float prevMute[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + float ampValue[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + float ampDelta[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + bool fading[8] = {false, false, false, false, false, false, false, false}; + + float fadeKnob = 0.f; + float prevFadeKnob = 1.f; + + float fadeValue = 0; + + const float noEnvTime = 0.00101; + + PolyMuter8() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + + configParam(FADE_PARAM, 0.f, 1.f, 0.25f, "Fade", "ms", 10000.f, 1.f); + configInput(IN_INPUT, "Poly"); + + configSwitch(MUTE_PARAM, 0.f, 1.f, 0.f, "Mute #1", {"Off", "On"}); + configSwitch(MUTE_PARAM+1, 0.f, 1.f, 0.f, "Mute #1", {"Off", "On"}); + configSwitch(MUTE_PARAM+2, 0.f, 1.f, 0.f, "Mute #2", {"Off", "On"}); + configSwitch(MUTE_PARAM+3, 0.f, 1.f, 0.f, "Mute #3", {"Off", "On"}); + configSwitch(MUTE_PARAM+4, 0.f, 1.f, 0.f, "Mute #4", {"Off", "On"}); + configSwitch(MUTE_PARAM+5, 0.f, 1.f, 0.f, "Mute #5", {"Off", "On"}); + configSwitch(MUTE_PARAM+6, 0.f, 1.f, 0.f, "Mute #6", {"Off", "On"}); + configSwitch(MUTE_PARAM+7, 0.f, 1.f, 0.f, "Mute #7", {"Off", "On"}); + /* + configSwitch(MUTE_PARAM+8, 0.f, 1.f, 0.f, "Mute #8", {"Off", "On"}); + configSwitch(MUTE_PARAM+9, 0.f, 1.f, 0.f, "Mute #9", {"Off", "On"}); + configSwitch(MUTE_PARAM+10, 0.f, 1.f, 0.f, "Mute #10", {"Off", "On"}); + configSwitch(MUTE_PARAM+11, 0.f, 1.f, 0.f, "Mute #11", {"Off", "On"}); + configSwitch(MUTE_PARAM+12, 0.f, 1.f, 0.f, "Mute #12", {"Off", "On"}); + configSwitch(MUTE_PARAM+13, 0.f, 1.f, 0.f, "Mute #13", {"Off", "On"}); + configSwitch(MUTE_PARAM+14, 0.f, 1.f, 0.f, "Mute #14", {"Off", "On"}); + configSwitch(MUTE_PARAM+15, 0.f, 1.f, 0.f, "Mute #15", {"Off", "On"}); + */ + + configOutput(OUT_OUTPUT, "Poly"); + } + + void onReset(const ResetEvent &e) override { + initStart = false; + + fadeKnob = 0.f; + prevFadeKnob = 1.f; + fadeValue = 0; + + for (int i = 0; i < 8; i++) { + mute[i] = 0; + prevMute[i] = 0; + ampValue[i] = 1; + ampDelta[i] = 1; + fading[i] = false; + } + + Module::onReset(e); + } + + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "initStart", json_boolean(initStart)); + json_object_set_new(rootJ, "mute1", json_real(params[MUTE_PARAM].getValue())); + json_object_set_new(rootJ, "mute2", json_real(params[MUTE_PARAM+1].getValue())); + json_object_set_new(rootJ, "mute3", json_real(params[MUTE_PARAM+2].getValue())); + json_object_set_new(rootJ, "mute4", json_real(params[MUTE_PARAM+3].getValue())); + json_object_set_new(rootJ, "mute5", json_real(params[MUTE_PARAM+4].getValue())); + json_object_set_new(rootJ, "mute6", json_real(params[MUTE_PARAM+5].getValue())); + json_object_set_new(rootJ, "mute7", json_real(params[MUTE_PARAM+6].getValue())); + json_object_set_new(rootJ, "mute8", json_real(params[MUTE_PARAM+7].getValue())); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* initStartJ = json_object_get(rootJ, "initStart"); + if (initStartJ) + initStart = json_boolean_value(initStartJ); + + if (initStart) { + for (int i = 0; i < 8; i++) { + params[MUTE_PARAM+i].setValue(0.f); + } + } else { + json_t* mute1J = json_object_get(rootJ, "mute1"); + if (mute1J){ + mute[0] = json_real_value(mute1J); + if (mute[0] == 1.f) { + prevMute[0] = 1; + ampValue[0] = 0; + } + } + + json_t* mute2J = json_object_get(rootJ, "mute2"); + if (mute2J){ + mute[1] = json_real_value(mute2J); + if (mute[1] == 1.f) { + prevMute[1] = 1; + ampValue[1] = 0; + } + } + + json_t* mute3J = json_object_get(rootJ, "mute3"); + if (mute3J){ + mute[2] = json_real_value(mute3J); + if (mute[2] == 1.f) { + prevMute[2] = 1; + ampValue[2] = 0; + } + } + + json_t* mute4J = json_object_get(rootJ, "mute4"); + if (mute4J){ + mute[3] = json_real_value(mute4J); + if (mute[3] == 1.f) { + prevMute[3] = 1; + ampValue[3] = 0; + } + } + + json_t* mute5J = json_object_get(rootJ, "mute5"); + if (mute5J){ + mute[4] = json_real_value(mute5J); + if (mute[4] == 1.f) { + prevMute[4] = 1; + ampValue[4] = 0; + } + } + + json_t* mute6J = json_object_get(rootJ, "mute6"); + if (mute6J){ + mute[5] = json_real_value(mute6J); + if (mute[5] == 1.f) { + prevMute[5] = 1; + ampValue[5] = 0; + } + } + + json_t* mute7J = json_object_get(rootJ, "mute7"); + if (mute7J){ + mute[6] = json_real_value(mute7J); + if (mute[6] == 1.f) { + prevMute[6] = 1; + ampValue[6] = 0; + } + } + + json_t* mute8J = json_object_get(rootJ, "mute8"); + if (mute8J){ + mute[7] = json_real_value(mute8J); + if (mute[7] == 1.f) { + prevMute[7] = 1; + ampValue[7] = 0; + } + } + } + } + + + + void process(const ProcessArgs& args) override { + + fadeKnob = params[FADE_PARAM].getValue(); + + if (fadeKnob != prevFadeKnob) { + fadeValue = std::pow(10000.f, fadeKnob) / 1000; + prevFadeKnob = fadeKnob; + } + + /* + chan = std::max(1, inputs[IN_INPUT].getChannels()); + inChans = chan; + if (inChans > 8) + outChans = 8; + */ + + inChans = std::max(1, inputs[IN_INPUT].getChannels()); + + if (inChans < 9) + outChans = inChans; + else + outChans = 8; + + for (int c = 0; c < 8; c++) { + mute[c] = params[MUTE_PARAM+c].getValue(); + lights[MUTE_LIGHT+c].setBrightness(mute[c]); + + if (mute[c] && !prevMute[c]) { // mute chan + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = -1 / fadeValue / args.sampleRate; + } else { + ampValue[c] = 0.f; + } + + } else if (!mute[c] && prevMute[c]) { // unmute chan + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = 1 / fadeValue / args.sampleRate; + } else { + ampValue[c] = 1.f; + } + } + prevMute[c] = mute[c]; + + if (fading[c]) { + ampValue[c] += ampDelta[c]; + if (ampValue[c] > 1.f) { + fading[c] = false; + ampValue[c] = 1.f; + } else if (ampValue[c] < 0.f) { + fading[c] = false; + ampValue[c] = 0.f; + } + } + + outputs[OUT_OUTPUT].setVoltage(inputs[IN_INPUT].getVoltage(c) * ampValue[c], c); + } + + //outputs[OUT_OUTPUT].setChannels(chan); + outputs[OUT_OUTPUT].setChannels(outChans); + + } +}; + +struct PolyMuter8DisplayChan : TransparentWidget { + PolyMuter8 *module; + int frame = 0; + PolyMuter8DisplayChan() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/DSEG14ClassicMini-BoldItalic.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + if (module->inChans > 9) + nvgTextBox(args.vg, 1.5, 17, 60, to_string(module->inChans).c_str(), NULL); + else + nvgTextBox(args.vg, 9.8, 17, 60, to_string(module->inChans).c_str(), NULL); + } + } + Widget::drawLayer(args, layer); + } +}; +/* +struct PolyMuter8DebugDisplay : TransparentWidget { + PolyMuter8 *module; + int frame = 0; + PolyMuter8DebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + nvgTextBox(args.vg, 0, 0,120, module->debugDisplay.c_str(), NULL); + //nvgTextBox(args.vg, 9, 16,120, module->debugDisplay2.c_str(), NULL); + //nvgTextBox(args.vg, 129, 6,120, module->debugDisplay3.c_str(), NULL); + //nvgTextBox(args.vg, 129, 16,120, module->debugDisplay4.c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + +struct PolyMuter8Widget : ModuleWidget { + PolyMuter8Widget(PolyMuter8* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/PolyMuter8.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + { + PolyMuter8DisplayChan *display = new PolyMuter8DisplayChan(); + //display->box.pos = mm2px(Vec(13.1, 31.2)); + display->box.pos = mm2px(Vec(10.7, 13)); + display->box.size = mm2px(Vec(8, 8)); + display->module = module; + addChild(display); + } + /* + { + PolyMuter8DebugDisplay *display = new PolyMuter8DebugDisplay(); + display->box.pos = Vec(3, 25); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xCenter = 10.1f; + const float xLeft = 6.5f; + const float xRight = 14.f; + + const float yIn = 17; + const float yFade = 30; + const float yOut = 116; + + constexpr float yStart = 42.5; + constexpr float yStart2 = 50.5; + constexpr float y = 8.f; + + addInput(createInputCentered(mm2px(Vec(xLeft, yIn)), module, PolyMuter8::IN_INPUT)); + + addParam(createParamCentered(mm2px(Vec(xCenter, yFade)), module, PolyMuter8::FADE_PARAM)); + + for (int i = 0; i < 8; i=i+2) { + addParam(createLightParamCentered>(mm2px(Vec(xLeft, yStart+(i*y))), module, PolyMuter8::MUTE_PARAM+i, PolyMuter8::MUTE_LIGHT+i)); + } + + for (int i = 1; i < 8; i=i+2) { + addParam(createLightParamCentered>(mm2px(Vec(xRight, yStart2+((i-1)*y))), module, PolyMuter8::MUTE_PARAM+i, PolyMuter8::MUTE_LIGHT+i)); + } + + /* + for (int i = 8; i < 16; i++) { + addParam(createLightParamCentered>(mm2px(Vec(xRight, yStart+((i-8)*y))), module, PolyMuter8::MUTE_PARAM+i, PolyMuter8::MUTE_LIGHT+i)); + } + */ + + addOutput(createOutputCentered(mm2px(Vec(xCenter, yOut)), module, PolyMuter8::OUT_OUTPUT)); + + + + } + + void appendContextMenu(Menu* menu) override { + PolyMuter8* module = dynamic_cast(this->module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Initialize on Start", "", &module->initStart)); + } +}; + +Model* modelPolyMuter8 = createModel("PolyMuter8"); \ No newline at end of file diff --git a/src/PolyMuter8Plus.cpp b/src/PolyMuter8Plus.cpp new file mode 100644 index 0000000..abfb3ae --- /dev/null +++ b/src/PolyMuter8Plus.cpp @@ -0,0 +1,734 @@ +#define UNMUTED 0 +#define SOLOED 1 +#define MUTED_SOLOED 2 +#define MUTED 3 + +#define COLOR_LCD_RED 0xdd, 0x33, 0x33, 0xff + +#include "plugin.hpp" + + +using namespace std; + +// ---------------------------------------------------------------------------- + +template +struct LightEmittingWidget : BASE { + virtual bool isLit() = 0; + + void drawLayer(const typename BASE::DrawArgs& args, int layer) override { + if (layer == 1 && isLit()) { + drawLit(args); + } + BASE::drawLayer(args, layer); + } + + virtual void drawLit(const typename BASE::DrawArgs& args) {} +}; + +// ---------------------------------------------------------------------------- + +struct PM8SoloMuteButton : LightEmittingWidget { + std::vector> _frames; + SvgWidget* _svgWidget; + CircularShadow* shadow = NULL; + + PM8SoloMuteButton(); + void onButton(const event::Button& e) override; + void onChange(const event::Change& e) override; + bool isLit() override; + void draw(const DrawArgs& args) override; + void drawLit(const DrawArgs& args) override; +}; + + +// ---------------------------------------------------------------------------- + + +PM8SoloMuteButton::PM8SoloMuteButton() { + shadow = new CircularShadow(); + addChild(shadow); + + _svgWidget = new SvgWidget(); + addChild(_svgWidget); + + auto svg = APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_0.svg")); + _frames.push_back(svg); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_1_green.svg"))); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_3_red_green.svg"))); + //_frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_2_red.svg"))); + //_frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_1_green.svg"))); + _frames.push_back(APP->window->loadSvg(asset::plugin(pluginInstance, "res/component/SickoMuteButton_2_red.svg"))); + _svgWidget->setSvg(svg); + box.size = _svgWidget->box.size; + shadow->box.size = _svgWidget->box.size; + shadow->blurRadius = 1.0; + shadow->box.pos = Vec(0.0, 1.0); +} + +void PM8SoloMuteButton::onButton(const event::Button& e) { + if (!getParamQuantity() || !(e.action == GLFW_PRESS && (e.mods & RACK_MOD_MASK) == 0)) { + ParamWidget::onButton(e); + return; + } + + + float value = getParamQuantity()->getValue(); + + // 0 unmuted + // 1 soloed + // 2 soloed muted + // 3 muted + + + if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + if (value == 0.f) + getParamQuantity()->setValue(1.f); + else if (value == 1.f) + getParamQuantity()->setValue(0.f); + else if (value == 2.f) + getParamQuantity()->setValue(3.f); + else + getParamQuantity()->setValue(2.f); + } + else if (value == 0) { + getParamQuantity()->setValue(3.f); + } + else { + getParamQuantity()->setValue(0.0f); + } + + + /* + if (value >= 2.0f) { + //if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + getParamQuantity()->setValue(value + 2.0f); + } + else if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + //else if (value >= 2.0f) { + //getParamQuantity()->setValue(value - 2.0f); + getParamQuantity()->setValue(value - 2.0f); + } + else { + getParamQuantity()->setValue(value > 0.5f ? 0.0f : 1.0f); + } + */ + + if (e.button == GLFW_MOUSE_BUTTON_RIGHT) { + e.consume(this); + } else { + ParamWidget::onButton(e); + } +} + +void PM8SoloMuteButton::onChange(const event::Change& e) { + assert(_frames.size() == 4); + if (getParamQuantity()) { + float value = getParamQuantity()->getValue(); + assert(value >= 0.0f && value <= 3.0f); + _svgWidget->setSvg(_frames[(int)value]); + } + ParamWidget::onChange(e); +} + +bool PM8SoloMuteButton::isLit() { + return module && !module->isBypassed() && getParamQuantity() && getParamQuantity()->getValue() > 0.0f; +} + +void PM8SoloMuteButton::draw(const DrawArgs& args) { + if (!isLit() || !getParamQuantity() || getParamQuantity()->getValue() < 1.0f) { + ParamWidget::draw(args); + } +} + +void PM8SoloMuteButton::drawLit(const DrawArgs& args) { + if (getParamQuantity() && getParamQuantity()->getValue() >= 1.0f) { + ParamWidget::draw(args); + } +} +// ---------------------- + +struct PolyMuter8Plus : Module { + enum ParamId { + FADE_PARAM, + ENUMS(MUTE_PARAM, 8), + PARAMS_LEN + }; + enum InputId { + IN_INPUT, + INPUTS_LEN + }; + enum OutputId { + OUT_OUTPUT, + OUTPUTS_LEN + }; + enum LightId { + //ENUMS(MUTE_LIGHT, 8), + LIGHTS_LEN + }; + + + //************************************************************** + // DEBUG + + /* + std::string debugDisplay = "X"; + std::string debugDisplay2 = "X"; + std::string debugDisplay3 = "X"; + std::string debugDisplay4 = "X"; + std::string debugDisplay5 = "X"; + std::string debugDisplay6 = "X"; + std::string debugDisplay7 = "X"; + std::string debugDisplay8 = "X"; + std::string debugDisplay9 = "X"; + int debugInt = 0; + bool debugBool = false; + */ + + //std::string db[4] = {"unm", "sol", "m-s", "mut"}; + + bool initStart = false; + + int inChans = 0; + int outChans = 0; + //int chan; + int buttonValue[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int prevButtonValue[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int status[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int prevStatus[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + float ampValue[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + float ampDelta[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + bool fading[8] = {false, false, false, false, false, false, false, false}; + + float fadeKnob = 0.f; + float prevFadeKnob = 1.f; + + float fadeValue = 0; + + int soloChans = 0; + bool globalSolo = false; + bool prevGlobalSolo = false; + + long sampleRate = APP->engine->getSampleRate(); + + const float noEnvTime = 0.00101; + + PolyMuter8Plus() { + config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN); + + configParam(FADE_PARAM, 0.f, 1.f, 0.25f, "Fade", "ms", 10000.f, 1.f); + configInput(IN_INPUT, "Poly"); + + configSwitch(MUTE_PARAM, 0.0f, 3.0f, 0.0f, "Mute #1", {"Unmuted", "Solo", "Solo", "Muted"}); + configSwitch(MUTE_PARAM+1, 0.0f, 3.0f, 0.0f, "Mute #2", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+2, 0.0f, 3.0f, 0.0f, "Mute #3", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+3, 0.0f, 3.0f, 0.0f, "Mute #4", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+4, 0.0f, 3.0f, 0.0f, "Mute #5", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+5, 0.0f, 3.0f, 0.0f, "Mute #6", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+6, 0.0f, 3.0f, 0.0f, "Mute #7", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + configSwitch(MUTE_PARAM+7, 0.0f, 3.0f, 0.0f, "Mute #8", {"Unmuted", "Solo", "Solo/Mute", "Muted"}); + + /* + configSwitch(MUTE_PARAM+9, 0.f, 1.f, 0.f, "Mute #9", {"Off", "On"}); + configSwitch(MUTE_PARAM+10, 0.f, 1.f, 0.f, "Mute #10", {"Off", "On"}); + configSwitch(MUTE_PARAM+11, 0.f, 1.f, 0.f, "Mute #11", {"Off", "On"}); + configSwitch(MUTE_PARAM+12, 0.f, 1.f, 0.f, "Mute #12", {"Off", "On"}); + configSwitch(MUTE_PARAM+13, 0.f, 1.f, 0.f, "Mute #13", {"Off", "On"}); + configSwitch(MUTE_PARAM+14, 0.f, 1.f, 0.f, "Mute #14", {"Off", "On"}); + configSwitch(MUTE_PARAM+15, 0.f, 1.f, 0.f, "Mute #15", {"Off", "On"}); + */ + + configOutput(OUT_OUTPUT, "Poly"); + } + + void onReset(const ResetEvent &e) override { + initStart = false; + + fadeKnob = 0.f; + prevFadeKnob = 1.f; + fadeValue = 0; + + soloChans = 0; + globalSolo = false; + prevGlobalSolo = false; + + for (int i = 0; i < 8; i++) { + buttonValue[i] = UNMUTED; + prevButtonValue[i] = UNMUTED; + status[i] = UNMUTED; + prevStatus[i] = UNMUTED; + ampValue[i] = 1; + ampDelta[i] = 1; + fading[i] = false; + } + + Module::onReset(e); + } + + void onSampleRateChange() override { + sampleRate = APP->engine->getSampleRate(); + } + + json_t* dataToJson() override { + json_t* rootJ = json_object(); + json_object_set_new(rootJ, "initStart", json_boolean(initStart)); + json_object_set_new(rootJ, "status1", json_integer(params[MUTE_PARAM].getValue())); + json_object_set_new(rootJ, "status2", json_integer(params[MUTE_PARAM+1].getValue())); + json_object_set_new(rootJ, "status3", json_integer(params[MUTE_PARAM+2].getValue())); + json_object_set_new(rootJ, "status4", json_integer(params[MUTE_PARAM+3].getValue())); + json_object_set_new(rootJ, "status5", json_integer(params[MUTE_PARAM+4].getValue())); + json_object_set_new(rootJ, "status6", json_integer(params[MUTE_PARAM+5].getValue())); + json_object_set_new(rootJ, "status7", json_integer(params[MUTE_PARAM+6].getValue())); + json_object_set_new(rootJ, "status8", json_integer(params[MUTE_PARAM+7].getValue())); + return rootJ; + } + + void dataFromJson(json_t* rootJ) override { + json_t* initStartJ = json_object_get(rootJ, "initStart"); + if (initStartJ) + initStart = json_boolean_value(initStartJ); + + if (initStart) { + for (int i = 0; i < 8; i++) { + params[MUTE_PARAM+i].setValue(0.f); + } + } else { + json_t* status1J = json_object_get(rootJ, "status1"); + if (status1J){ + buttonValue[0] = json_integer_value(status1J); + firstStatusCheck(0); + } + + json_t* status2J = json_object_get(rootJ, "status2"); + if (status2J){ + buttonValue[1] = json_integer_value(status2J); + firstStatusCheck(1); + } + + json_t* status3J = json_object_get(rootJ, "status3"); + if (status3J){ + buttonValue[2] = json_integer_value(status3J); + firstStatusCheck(2); + } + + json_t* status4J = json_object_get(rootJ, "status4"); + if (status4J){ + buttonValue[3] = json_integer_value(status4J); + firstStatusCheck(3); + } + + json_t* status5J = json_object_get(rootJ, "status5"); + if (status5J){ + buttonValue[4] = json_integer_value(status5J); + firstStatusCheck(4); + } + + json_t* status6J = json_object_get(rootJ, "status6"); + if (status6J){ + buttonValue[5] = json_integer_value(status6J); + firstStatusCheck(5); + } + + json_t* status7J = json_object_get(rootJ, "status7"); + if (status7J){ + buttonValue[6] = json_integer_value(status7J); + firstStatusCheck(6); + + } + + json_t* status8J = json_object_get(rootJ, "status8"); + if (status8J){ + buttonValue[7] = json_integer_value(status8J); + firstStatusCheck(7); + } + } + } + + void inline firstStatusCheck(int c) { + if (buttonValue[c] >= 0 && buttonValue[c] < 4) { + switch (buttonValue[c]) { + case MUTED: + prevButtonValue[c] = MUTED; + prevStatus[c] = MUTED; + status[c] = MUTED; + ampValue[c] = 0; + break; + + case SOLOED: + prevGlobalSolo = false; + globalSolo = true; + prevStatus[c] = UNMUTED; + status[c] = SOLOED; + ampValue[c] = 1; + break; + + case MUTED_SOLOED: + prevGlobalSolo = false; + globalSolo = true; + prevStatus[c] = MUTED; + status[c] = MUTED_SOLOED; + ampValue[c] = 1; + break; + } + } else { + buttonValue[c] = 0; + } + + } + + void inline fadeIn(int c) { + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = 1 / fadeValue / sampleRate; + } else { + ampValue[c] = 1.f; + } + } + + void inline fadeOut(int c) { + if (fadeValue > noEnvTime) { + fading[c] = true; + ampDelta[c] = -1 / fadeValue / sampleRate; + } else { + ampValue[c] = 0.f; + } + } + + void process(const ProcessArgs& args) override { + + fadeKnob = params[FADE_PARAM].getValue(); + + if (fadeKnob != prevFadeKnob) { + fadeValue = std::pow(10000.f, fadeKnob) / 1000; + prevFadeKnob = fadeKnob; + } + + if (globalSolo != prevGlobalSolo) { + if (globalSolo) { // solo on + for (int c = 0; c < 8; c++) { + if (status[c] == UNMUTED) { + fadeOut(c); + prevStatus[c] = status[c]; + + } else if (status[c] == MUTED) { + fadeOut(c); + prevStatus[c] = status[c]; + } + } + } else { // solo off + for (int c = 0; c < 8; c++) { + if (prevStatus[c] == UNMUTED) { + fadeIn(c); + + status[c] = UNMUTED; + + } else if (prevStatus[c] == MUTED) { + fadeOut(c); + + status[c] = MUTED; + } + + } + } + prevGlobalSolo = globalSolo; + } + + for (int c = 0; c < 8; c++) { + + // 0 unmuted + // 1 soloed + // 2 soloed muted + // 3 muted + + buttonValue[c] = int(params[MUTE_PARAM+c].getValue()); + + if (buttonValue[c] != prevButtonValue[c]) { + switch (prevButtonValue[c]) { + case UNMUTED: + switch (buttonValue[c]) { + case MUTED: + prevStatus[c] = MUTED; + fadeOut(c); + status[c] = MUTED; + break; + + case SOLOED: + soloChans++; + fadeIn(c); + status[c] = SOLOED; + if (!globalSolo) + prevStatus[c] = UNMUTED; + break; + + case MUTED_SOLOED: + soloChans++; + fadeIn(c); + status[c] = MUTED_SOLOED; + break; + + } + + break; + + // ------------------------------------- + + case MUTED: + switch (buttonValue[c]) { + case UNMUTED: + if (!globalSolo) + fadeIn(c); + prevStatus[c] = UNMUTED; + + + status[c] = UNMUTED; + + break; + + case SOLOED: + soloChans++; + fadeIn(c); + status[c] = SOLOED; + break; + + case MUTED_SOLOED: + soloChans++; + fadeIn(c); + status[c] = MUTED_SOLOED; + if (!globalSolo) + prevStatus[c] = MUTED; + break; + } + + break; + + // ------------------------------------- + + case MUTED_SOLOED: + switch (buttonValue[c]) { + case UNMUTED: + soloChans--; + if (globalSolo) { + if (prevStatus[c] == MUTED) { + fadeOut(c); + } else + fadeIn(c); + prevStatus[c] = UNMUTED; + } else { + fadeIn(c); + } + status[c] = UNMUTED; + break; + + case MUTED: + soloChans--; + fadeOut(c); + status[c] = MUTED; + break; + } + + break; + + // ------------------------------------- + + case SOLOED: + switch (buttonValue[c]) { + + case UNMUTED: + if (!globalSolo) { + fadeIn(c); + } else { + fadeOut(c); + } + soloChans--; + + status[c] = UNMUTED; + break; + + case MUTED: + fadeOut(c); + soloChans--; + status[c] = MUTED; + break; + + } + + break; + + } + } + prevButtonValue[c] = buttonValue[c]; + } + + if (soloChans == 0) { + globalSolo = false; + } else { + globalSolo = true; + } + + /* + debugDisplay = db[prevButtonValue[0]]; + debugDisplay2 = db[buttonValue[0]]; + debugDisplay3 = db[prevStatus[0]]; + debugDisplay4 = db[status[0]]; + debugDisplay5 = db[prevButtonValue[1]]; + debugDisplay6 = db[buttonValue[1]]; + debugDisplay7 = db[prevStatus[1]]; + debugDisplay8 = db[status[1]]; + debugDisplay9 = to_string(soloChans); + */ + + inChans = std::max(1, inputs[IN_INPUT].getChannels()); + + if (inChans < 9) + outChans = inChans; + else + outChans = 8; + + + for (int c = 0; c < outChans; c++) { + + if (fading[c]) { + ampValue[c] += ampDelta[c]; + if (ampValue[c] > 1.f) { + fading[c] = false; + ampValue[c] = 1.f; + } else if (ampValue[c] < 0.f) { + fading[c] = false; + ampValue[c] = 0.f; + } + } + + outputs[OUT_OUTPUT].setVoltage(inputs[IN_INPUT].getVoltage(c) * ampValue[c], c); + } + + outputs[OUT_OUTPUT].setChannels(outChans); + + } +}; + +struct PolyMuter8PlusDisplayChan : TransparentWidget { + PolyMuter8Plus *module; + int frame = 0; + PolyMuter8PlusDisplayChan() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::plugin(pluginInstance, "res/DSEG14ClassicMini-BoldItalic.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + + nvgFillColor(args.vg, nvgRGBA(COLOR_LCD_RED)); + if (module->inChans > 9) + nvgTextBox(args.vg, 1.5, 17, 60, to_string(module->inChans).c_str(), NULL); + else + nvgTextBox(args.vg, 9.8, 17, 60, to_string(module->inChans).c_str(), NULL); + } + } + Widget::drawLayer(args, layer); + } +}; + +/* +struct PolyMuter8PlusDebugDisplay : TransparentWidget { + PolyMuter8Plus *module; + int frame = 0; + PolyMuter8PlusDebugDisplay() { + } + + void drawLayer(const DrawArgs &args, int layer) override { + if (module) { + if (layer ==1) { + shared_ptr font = APP->window->loadFont(asset::system("res/fonts/Nunito-bold.ttf")); + nvgFontSize(args.vg, 10); + nvgFontFaceId(args.vg, font->handle); + nvgTextLetterSpacing(args.vg, 0); + nvgFillColor(args.vg, nvgRGBA(0xff, 0xff, 0xff, 0xff)); + + nvgTextBox(args.vg, 30, 0,120, module->debugDisplay.c_str(), NULL); + nvgTextBox(args.vg, 30, 10,120, module->debugDisplay2.c_str(), NULL); + nvgTextBox(args.vg, 30, 20,120, module->debugDisplay3.c_str(), NULL); + nvgTextBox(args.vg, 30, 30,120, module->debugDisplay4.c_str(), NULL); + + nvgTextBox(args.vg, 60, 0,120, module->debugDisplay5.c_str(), NULL); + nvgTextBox(args.vg, 60, 10,120, module->debugDisplay6.c_str(), NULL); + nvgTextBox(args.vg, 60, 20,120, module->debugDisplay7.c_str(), NULL); + nvgTextBox(args.vg, 60, 30,120, module->debugDisplay8.c_str(), NULL); + nvgTextBox(args.vg, 60, 40,120, module->debugDisplay9.c_str(), NULL); + + } + } + Widget::drawLayer(args, layer); + } +}; +*/ + + +struct PolyMuter8PlusWidget : ModuleWidget { + PolyMuter8PlusWidget(PolyMuter8Plus* module) { + setModule(module); + setPanel(createPanel(asset::plugin(pluginInstance, "res/PolyMuter8Plus.svg"))); + + addChild(createWidget(Vec(0, 0))); + addChild(createWidget(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); + + { + PolyMuter8PlusDisplayChan *display = new PolyMuter8PlusDisplayChan(); + display->box.pos = mm2px(Vec(10.7, 13)); + display->box.size = mm2px(Vec(8, 8)); + display->module = module; + addChild(display); + } + + /* + { + PolyMuter8PlusDebugDisplay *display = new PolyMuter8PlusDebugDisplay(); + display->box.pos = Vec(3, 25); + display->box.size = Vec(307, 100); + display->module = module; + addChild(display); + } + */ + + const float xCenter = 10.1f; + const float xLeft = 6.5f; + const float xRight = 14.f; + + const float yIn = 17; + const float yFade = 30; + const float yOut = 116; + + constexpr float yStart = 42.5; + constexpr float yStart2 = 50.5; + constexpr float y = 8.f; + + addInput(createInputCentered(mm2px(Vec(xLeft, yIn)), module, PolyMuter8Plus::IN_INPUT)); + + addParam(createParamCentered(mm2px(Vec(xCenter, yFade)), module, PolyMuter8Plus::FADE_PARAM)); + + for (int i = 0; i < 8; i=i+2) { + addParam(createParamCentered(mm2px(Vec(xLeft, yStart+(i*y))), module, PolyMuter8Plus::MUTE_PARAM+i)); + } + + for (int i = 1; i < 8; i=i+2) { + addParam(createParamCentered(mm2px(Vec(xRight, yStart2+((i-1)*y))), module, PolyMuter8Plus::MUTE_PARAM+i)); + } + + addOutput(createOutputCentered(mm2px(Vec(xCenter, yOut)), module, PolyMuter8Plus::OUT_OUTPUT)); + + } + + void appendContextMenu(Menu* menu) override { + PolyMuter8Plus* module = dynamic_cast(this->module); + + menu->addChild(new MenuSeparator()); + menu->addChild(createMenuLabel("Right-click on buttons")); + menu->addChild(createMenuLabel("to SOLO channel")); + menu->addChild(new MenuSeparator()); + menu->addChild(createBoolPtrMenuItem("Initialize on Start", "", &module->initStart)); + } +}; + +Model* modelPolyMuter8Plus = createModel("PolyMuter8Plus"); \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index e37e6fc..03fe235 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -33,7 +33,12 @@ void init(Plugin* p) { p->addModel(modelHolder8); p->addModel(modelModulator); p->addModel(modelModulator7); + p->addModel(modelModulator7Compact); p->addModel(modelParking); + p->addModel(modelPolyMuter8); + p->addModel(modelPolyMuter8Plus); + p->addModel(modelPolyMuter16); + p->addModel(modelPolyMuter16Plus); p->addModel(modelShifter); p->addModel(modelSickoAmp); p->addModel(modelSickoLooper1); diff --git a/src/plugin.hpp b/src/plugin.hpp index f543a47..431b083 100644 --- a/src/plugin.hpp +++ b/src/plugin.hpp @@ -33,7 +33,12 @@ extern Model* modelHolderCompact; extern Model* modelHolder8; extern Model* modelModulator; extern Model* modelModulator7; +extern Model* modelModulator7Compact; extern Model* modelParking; +extern Model* modelPolyMuter8; +extern Model* modelPolyMuter8Plus; +extern Model* modelPolyMuter16; +extern Model* modelPolyMuter16Plus; extern Model* modelShifter; extern Model* modelSickoAmp; extern Model* modelSickoLooper1;