diff --git a/plugins/community/repos/SubmarineFree/Makefile b/plugins/community/repos/SubmarineFree/Makefile
index aafe02db..27f0c4f5 100644
--- a/plugins/community/repos/SubmarineFree/Makefile
+++ b/plugins/community/repos/SubmarineFree/Makefile
@@ -2,7 +2,7 @@
SLUG = SubmarineFree
# Must follow the format in the Versioning section of https://vcvrack.com/manual/PluginDevelopmentTutorial.html
-VERSION = 0.6.4
+VERSION = 0.6.8
# FLAGS will be passed to both the C and C++ compiler
FLAGS +=
diff --git a/plugins/community/repos/SubmarineFree/README.md b/plugins/community/repos/SubmarineFree/README.md
index cd836c76..40798426 100644
--- a/plugins/community/repos/SubmarineFree/README.md
+++ b/plugins/community/repos/SubmarineFree/README.md
@@ -1,9 +1,16 @@
# SubmarineFree
Free plugins for VCV Rack
+[.plan (what I'm working on)](https://github.com/david-c14/SubmarineFree/issues/23)
+
# [Manual](https://github.com/david-c14/SubmarineFree/blob/master/manual/index.md)
-# [Builds](https://github.com/david-c14/SubmarineFree/issues/9)
+# [Builds](https://github.com/david-c14/SubmarineFree/releases/tag/v0.6.8)
+###### [0.6.8](https://github.com/david-c14/SubmarineFree/releases/tag/v0.6.8)
+###### [0.6.7](https://github.com/david-c14/SubmarineFree/releases/tag/v0.6.7)
+###### [0.6.6](https://github.com/david-c14/SubmarineFree/issues/20)
+###### [0.6.5](https://github.com/david-c14/SubmarineFree/issues/19)
+###### [0.6.4](https://github.com/david-c14/SubmarineFree/issues/14)
###### [0.6.3](https://github.com/david-c14/SubmarineFree/issues/9)
###### [0.6.2](https://github.com/david-c14/SubmarineFree/issues/4)
@@ -11,3 +18,5 @@ Free plugins for VCV Rack
Source code licensed under BSD-3-Clause by carbon14 (David O'Rourke)
All graphics in res/ and src/res are © 2018 by carbon14 (David O'Rourke)
+
+Some portions of this source code are based on code © 2016 by Andrew Belt
diff --git a/plugins/community/repos/SubmarineFree/make.objects b/plugins/community/repos/SubmarineFree/make.objects
index 8c00e537..d0008398 100644
--- a/plugins/community/repos/SubmarineFree/make.objects
+++ b/plugins/community/repos/SubmarineFree/make.objects
@@ -1,25 +1,32 @@
ALL_OBJ= \
- src/AG-106.o \
- src/BB-120.o \
- src/BP.o \
+ src/AG1.o \
+ src/AO1.o \
+ src/BB1.o \
+ src/BP1.o \
src/DS.o \
- src/FF-110.o \
- src/FF-120.o \
- src/FF-212.o \
- src/LA-108.o \
- src/LD-106.o \
- src/NG-112.o \
- src/OG-106.o \
- src/PG-112.o \
- src/PO.o \
+ src/EO1.o \
+ src/FF1.o \
+ src/FF2.o \
+ src/LA1.o \
+ src/LD1.o \
+ src/NG1.o \
+ src/OG1.o \
+ src/PG1.o \
+ src/PO12.o \
+ src/SS1.o \
src/SubmarineFree.o \
- src/WK-101.o \
+ src/TD1.o \
+ src/TD2.o \
+ src/TF1.o \
+ src/TM1.o \
+ src/WK12.o \
src/XF-101.o \
src/XF-102.o \
src/XF-104.o \
src/XF-201.o \
src/XF-202.o \
src/XF.o \
- src/XG-106.o \
+ src/XG1.o \
src/torpedo.o \
- src/ComponentLibrary/LightKnob.o
+ src/ComponentLibrary/LightKnob.o \
+ src/ComponentLibrary/Port.o
diff --git a/plugins/community/repos/SubmarineFree/manual/AG.md b/plugins/community/repos/SubmarineFree/manual/AG.md
index 8034311a..59c85eeb 100644
--- a/plugins/community/repos/SubmarineFree/manual/AG.md
+++ b/plugins/community/repos/SubmarineFree/manual/AG.md
@@ -1,5 +1,6 @@
# Logical AND Gates
### [Part of the Digital Suite](DS.md)
+#### AG-104 AND Gates
#### AG-106 AND Gates
@@ -18,3 +19,7 @@ The voltage range of the digital gates can be configured from the context menu.
Any output not connected will be normalled into a third input on the gate below. In this way multi-input gates can be created up to a maximum of 12 inputs.
An input not connected will be ignored.
+
+## AG-104
+
+The AG-104 provides four two-input AND gates in a small form factor. Otherwise functionality is as for the AG-106 above.
diff --git a/plugins/community/repos/SubmarineFree/manual/AO-118.png b/plugins/community/repos/SubmarineFree/manual/AO-118.png
new file mode 100644
index 00000000..4c72e0be
Binary files /dev/null and b/plugins/community/repos/SubmarineFree/manual/AO-118.png differ
diff --git a/plugins/community/repos/SubmarineFree/manual/AO-example-osc.md b/plugins/community/repos/SubmarineFree/manual/AO-example-osc.md
new file mode 100644
index 00000000..811dbd37
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/manual/AO-example-osc.md
@@ -0,0 +1,64 @@
+# A sine wave oscillator using AO-106
+
+
+
+In the example above we have an AO-106 6-algorithm device functioning as a voltage controlled sine wave oscillator.
+An SS-221 on the left is providing a reference 1v/oct CV to both the AO-106 and a Frank Buss Formula for comparison.
+
+The first block in the AO-106 is providing a small delta value.
+
+The second block takes that delta value and adds it to the X-input which is fed from...
+
+...the third block which takes the accumulated value and reduces it modulo 1.
+
+The result is that we have a signal in a feedback loop from the third block back to the second block, which gradually grows over time,
+and cycles around every time it reaches 1. Effectively a sawtooth wave with a frequency that depends on the input voltage, and a range from
+0V to 1V.
+
+The fourth block multiplies this sawtooth amplitude by tau (2 pi) to give an amplitude that ranges from 0V to tauV.
+
+The fifth block takes the sine of this sawtooth. Since a range of 0 to tau in radians represents one complete cycle, the resulting signal
+is a sine wave with a range of -1V to +1V
+
+The sixth block multiplies the signal by 5 to give the final -5V to +5V sine wave.
+
+The complicated part of this patch is in the first block where the algorithm is 2x+c. Where does this value of C = -7.52 come from?
+
+To acheive our basic requirement of 1v/oct, we need to raise 2 to the power of the CV input. For each increase 1V increase in the CV,
+the resulting value doubles, just like the frequency doubling as we move up an octave. But we have some further requirements.
+
+We want our signal frequency to be middle C (261.6Hz) when the CV is at 0V, so we should multiply our power of 2 by 261.6.
+That gives us a value in cycles per second. But then we need to divide this value by our sample rate, in this case I'm running at 48,000Hz
+sample rate. We divide by this number so that our delta accumulates to the right number of cycles every second.
+
+Our algorithm for the delta value then is
+
+2x × 261.6 ÷ 48000
+
+or
+
+2x × 0.00545
+
+But we could represent 0.00545 as a 2 raised to some power... 2n
+
+n would be the base-2 logarithm of 0.00545 which is -7.51953
+
+Now we can say that our delta value is
+
+2x × 2-7.51953
+
+or
+
+2x - 7.51953
+
+This is where the figures in the first block come from.
+
+The AO-1xx series of devices offer only two decimal places of precision in the constant setting, as a result this example oscillator is ever so slightly flat.
+However this discrepancy is less that the drift in the Fundamental VCO1.
+
+Note also that changing the engine sample rate would change the frequency of the oscillator. Doubling the engine sample rate to 96,000 Hz
+would require adjusting the constant from -7.52 to -8.52.
+
+The Frank Buss Formula has a built in sawtooth oscillator which automatically compensates for changes in the engine sample rate.
+
+
diff --git a/plugins/community/repos/SubmarineFree/manual/AO-example-osc.png b/plugins/community/repos/SubmarineFree/manual/AO-example-osc.png
new file mode 100644
index 00000000..f3a2b179
Binary files /dev/null and b/plugins/community/repos/SubmarineFree/manual/AO-example-osc.png differ
diff --git a/plugins/community/repos/SubmarineFree/manual/AO-example-square.png b/plugins/community/repos/SubmarineFree/manual/AO-example-square.png
new file mode 100644
index 00000000..21dfc9a7
Binary files /dev/null and b/plugins/community/repos/SubmarineFree/manual/AO-example-square.png differ
diff --git a/plugins/community/repos/SubmarineFree/manual/AO-list.md b/plugins/community/repos/SubmarineFree/manual/AO-list.md
new file mode 100644
index 00000000..729daae7
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/manual/AO-list.md
@@ -0,0 +1,230 @@
+# AO-1xx Algorithms
+
+## Arithmetical
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| | | Pass-through. X passes across unchanged, Y passes down unchanged |
+| C | c | The value of the selected constant C |
+| X+C | x + c | X added to C |
+| Y+C | y + c | Y added to C|
+| X+Y+C | x + y + c | X and Y added to C |
+| C-X | c - x | X subtracted from C |
+| C-Y | c - y | Y subtracted from C |
+| X-(Y+C) | x - ( y + c ) | Y added to C then all subtracted from X |
+| (X+C)-Y | ( x + c ) - y | Y subtracted from X and C |
+| Y-(X+C) | y - ( x + c ) | X added to C then all subtracted from Y |
+| (Y+C)-X | ( y + c ) - x | X subtracted from Y and C |
+| (X⨯Y)+C | ( x * y ) + c | X times Y added to C |
+| (X+C)⨯Y | ( x + c ) * y | X and C multiplied by Y |
+| X⨯(Y+C) | x * ( y + c ) | Y and C multiplied by X |
+| X⨯C | x * c | X times C |
+| Y⨯C | y * c | Y times C |
+| X⨯Y⨯C | x * y * c | X times Y times C|
+| π⨯(X+C) | M_PI * ( x + c ) | X and C multiplied by pi |
+| π⨯(Y+C) | M_PI * ( y + c ) | Y and C multiplied by pi |
+| τ⨯(X+C) | 2 * M_PI * ( x + c ) | X and C multiplied by tau |
+| τ⨯(Y+C) | 2 * M_PI * ( y + c ) | Y and C multiplied by tau |
+| X÷C | x / c | X divided by C |
+| C÷X | c / x | C divided by X |
+| Y÷C | y / c | Y divided by C |
+| C÷Y | c / y | C divided by Y |
+| C+(X÷Y) | c + ( x / y ) | C added to X divided by Y |
+| C+(Y÷X) | c + ( y / x ) | C added to Y divided by X |
+| X+(Y÷C) | x + ( y / c ) | X added to Y divided by C |
+| X+(C÷Y) | x + ( c / y ) | X added to C divided by Y |
+| Y+(X÷C) | y + ( x / c ) | Y added to X divided by C |
+| Y+(C÷X) | y + ( c / x ) | Y added to C divided by X |
+| (X+C)÷Y | ( x + c ) / y | X and C divided by Y |
+| X÷(Y+C) | x / ( y + c ) | X divided by Y and C |
+| (Y+C)÷X | ( y + c ) / x | Y and C divided by X |
+| Y÷(X+C) | y / ( x + c ) | Y divided by X and C |
+## Modular
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| (X+C)%Y | fmodf( x + c , y ) | The remainder of X and C divided by Y |
+| (Y+C)%X | fmodf( y + c , x ) | The remainder of Y and C divided by X |
+| X%(Y+C) | fmodf( x , y + c ) | The remainder of X divided by Y and C |
+| Y%(X+C) | fmodf( y , x + c) | The remainder of Y divided by X and C |
+| X%C | fmodf( x , c ) | The remainder of X divided by C |
+| Y%C | fmodf( y , c ) | The remainder of Y divided by C |
+## Quadratic
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| X²+C | x * x + c | X squared added to C |
+| Y²+C | y * y + c | Y squared added to C |
+| (X+C)² | ( x + c ) * ( x + c ) | X and C squared |
+| (Y+C)² | ( y + c ) * ( y + c ) | Y and C squared |
+| X²+Y+C | x * x + y + c | X squared added to Y and C |
+| Y²+X+C | y * y + x + c | Y squared added to X and C |
+| X²+CY | x * x + c * y | X squared added to Y multiplied by C |
+| Y²+CX | y * y + c * x | Y squared added to X multiplied by C |
+## Powers
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| √(X+C) | sqrt( x + c ) | The square root of X and C |
+| √(Y+C) | sqrt( y + c ) | The square root of Y and C |
+| Cˣ | powf( c , x ) | C raised to the power of X |
+| Cʸ | powf( c , y ) | C raised to the power of Y |
+| Cˣ⁺ʸ | powf( c , x + y ) | C raised to the power of X and Y |
+| Cˣʸ | powf( c , x * y ) | C raised to the power of X multiplied by Y |
+| Xᶜ | powf( x , c ) | X raised to the power of C |
+| Yᶜ | powf( y , c ) | Y raised to the power of C |
+| Xʸ⁺ᶜ | powf( x , y + c ) | X raised to the power of Y and C |
+| Yˣ⁺ᶜ | powf( y , x + c ) | Y raised to the power of X and C |
+| Xᶜʸ | powf( x , c * y ) | X raised to the power of Y multiplied by C |
+| Yᶜˣ | powf( y , c * x ) | Y raised to the power of X multiplied by C |
+## Positive values only
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| |X+C| | abs( x + c ) | X Added to C with any minus sign removed |
+| |Y+C| | abs( y + c ) | Y added to C with any minus sign removed |
+## Maximum and Minimum
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| min(X+C,Y) | min( x + c, y ) | The smaller of X Added to C, or Y |
+| min(X,C) | min( x, c ) | The smaller of X or C |
+| min(Y,C) | min( y, c ) | The smaller of Y or C |
+| max(X+C,Y) | max( x + c, y ) | The larger of X added to C, or Y |
+| max(X,C) | max( x, c ) | The larger of X or C |
+| max(Y,C) | max( y, c ) | The larger of Y or C |
+## Trigonometric
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| sin(X+C) | sin( x + c ) | The sine of X and C |
+| sin(Y+C) | sin( y + c ) | The sine of Y and C |
+| sin(X+Y) | sin( x + y ) | The sine of X and Y |
+| sin(CX) | sin( c * x ) | The sine of X mulitplied by C |
+| sin(CY) | sin( c * y ) | The sine of Y multiplied by C |
+| sin(XY) | sin( x * y ) | The sine of X multiplied by Y |
+| cos(X+C) | cos( x + c ) | The cosine of X and C |
+| cos(Y+C) | cos( y + c ) ||
+| cos(X+Y) | cos( x + y ) ||
+| cos(CX) | cos( c * x ) ||
+| cos(CY) | cos( c * y ) ||
+| cos(XY) | cos( x * y ) ||
+| tan(X+C) | tan( x + c ) | The tangent of X and C |
+| tan(Y+C) | tan( y + c ) ||
+| tan(X+Y) | tan( x + y ) ||
+| tan(CX) | tan( c * x ) ||
+| tan(CY) | tan( c * y ) ||
+| tan(XY) | tan( x * y ) ||
+| asin(X+C) | asin( x + c ) | The arcsine of X and C |
+| asin(Y+C) | asin( y + c ) ||
+| asin(X+Y) | asin( x + y ) ||
+| asin(CX) | asin( c * x ) ||
+| asin(CY) | asin( c * y ) ||
+| asin(XY) | asin( x * y ) ||
+| acos(X+C) | acos( x + c ) | The arcosine of X and C |
+| acos(Y+C) | acos( y + c ) ||
+| acos(X+Y) | acos( x + y ) ||
+| acos(CX) | acos( c * x ) ||
+| acos(CY) | acos( c * y ) ||
+| acos(XY) | acos( x * y ) ||
+| atan(X+C) | atan( x + c ) | The arctangent of X and C |
+| atan(Y+C) | atan( y + c ) ||
+| atan(X+Y) | atan( x + y ) ||
+| atan(CX) | atan( c * x ) ||
+| atan(CY) | atan( c * y ) ||
+| atan(XY) | atan( x * y ) ||
+## Logarithmic
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| log(X+C) | log( x + c ) | The natural logarithm of X and C |
+| log(Y+C) | log( y + c ) | The natural logarithm of Y and C |
+| log₂(X+C) | log2( x + c ) | The base-2 logarithm of X and C |
+| log₂(Y+C) | log2( y + c ) | The base-2 logarithm of Y and C |
+| log₁₀(X+C) | log10( x + c ) | The base-10 logarithm of X and C |
+| log₁₀(Y+C) | log10( y + c ) | The base-10 logarithm of Y and C |
+## Exponential
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| ℯˣ⁺ᶜ | exp( x + c ) | e raised to the power of X and C |
+| ℯʸ⁺ᶜ | exp( y + c ) | e raised to the power of Y and C |
+| ℯᶜˣ | exp( c * x ) | e raised to the power of X multiplied by C |
+| ℯᶜʸ | exp( c * y ) | e raised to the power of Y multiplied by C |
+| 2ˣ⁺ᶜ | powf( 2, x + c ) | 2 raised to the power of X and C |
+| 2ʸ⁺ᶜ | powf( 2, y + c ) | 2 raised to the power of Y and C |
+| 2ᶜˣ | powf( 2, c * x ) | 2 raised to the power of X multiplied by C |
+| 2ᶜʸ | powf( 2, c * y ) | 2 raised to the power of Y multiplied by C |
+| 10ˣ⁺ᶜ | powf( 10, x + c ) | 10 raised to the power of X and C |
+| 10ʸ⁺ᶜ | powf( 10, y + c ) | 10 raised to the power of Y and C |
+| 10ᶜˣ | powf( 10, c * x ) | 10 raised to the power of X multiplied by C |
+| 10ᶜʸ | powf( 10, c * y ) | 10 raised to the power of Y multiplied by C |
+## Conditional
+| Display | Code | Description |
+| ------------------------ | ------------------ | -------------------- |
+| if X>0↣Y/C | (x > 0) ? y : c | Y if X is greater than 0 otherwise C |
+| if X<0↣Y/C | (x < 0) ? y : c | Y if X is less than 0 otherwise C |
+| if X=0↣Y/C | (x == 0) ? y : c | Y if X is 0 otherwise C |
+| if X>0↣C/Y | (x > 0) ? c : y | C if X is greater than 0 otherwise Y |
+| if X<0↣C/Y | (x < 0) ? c : y | C if X is less that 0 otherwise Y |
+| if X=0↣C/Y | (x == 0) ? c : y | C if X is 0 otherwise Y |
+| if X>0↣1/0 | (x > 0) ? 1 : 0 | 1 if X is greater than 0 otherwise 0 |
+| if X<0↣1/0 | (x < 0) ? 1 : 0 ||
+| if X=0↣1/0 | (x == 0) ? 1 : 0 ||
+| if X>0↣X/C | (x > 0) ? x : c | X if X is greater than 0 otherwise C |
+| if X<0↣X/C | (x < 0) ? x : c ||
+| if X=0↣X/C | (x == 0) ? x : c ||
+| if X>0↣C/X | (x > 0) ? c : x | C if X is greater than 0 otherwise X |
+| if X<0↣C/X | (x < 0) ? c : x ||
+| if X=0↣C/X | (x == 0) ? c : x ||
+| if Y>0↣X/C | (y > 0) ? x : c | X if Y is greater than 0 otherwise C |
+| if Y<0↣X/C | (y < 0) ? x : c ||
+| if Y=0↣X/C | (y == 0) ? x : c ||
+| if Y>0↣C/X | (y > 0) ? c : x | C if Y is greater than 0 otherwise X |
+| if Y<0↣C/X | (y < 0) ? c : x ||
+| if Y=0↣C/X | (y == 0) ? c : x ||
+| if Y>0↣1/0 | (y > 0) ? 1 : 0 ||
+| if Y<0↣1/0 | (y < 0) ? 1 : 0 ||
+| if Y=0↣1/0 | (y == 0) ? 1 : 0 ||
+| if Y>0↣Y/C | (y > 0) ? y : c ||
+| if Y<0↣Y/C | (y < 0) ? y : c ||
+| if Y=0↣Y/C | (y == 0) ? y : c ||
+| if Y>0↣C/Y | (y > 0) ? c : y ||
+| if Y<0↣C/Y | (y < 0) ? c : y ||
+| if Y=0↣C/Y | (y == 0) ? c : y ||
+| if X>Y↣C/0 | (x > y) ? c : 0 | C if X is greater than Y otherwise 0 |
+| if XX↣C/0 | (y > x) ? c : 0 ||
+| if YY↣X/0 | (x > y) ? x : 0 | X if X is greater than Y otherwise 0 |
+| if XX↣X/0 | (y > x) ? x : 0 ||
+| if YY↣Y/0 | (x > y) ? y : 0 ||
+| if XX↣Y/0 | (y > x) ? y : 0 ||
+| if YC↣Y/0 | (x > c) ? y : 0 ||
+| if XX↣Y/0 | (c > x) ? y : 0 ||
+| if CC↣X/0 | (x > c) ? x : 0 ||
+| if XX↣X/0 | (c > x) ? x : 0 ||
+| if CC↣X/Y | (x > c) ? x : y ||
+| if XX↣X/Y | (c > x) ? x : y ||
+| if CC↣X/0 | (y > c) ? x : 0 ||
+| if YY↣X/0 | (c > y) ? x : 0 ||
+| if CC↣Y/0 | (y > c) ? y : 0 ||
+| if YY↣Y/0 | (c > y) ? y : 0 ||
+| if CC↣Y/X | (y > c) ? y : x | Y if Y is greater than C otherwise X |
+| if YY↣Y/X | (c > y) ? y : x | Y if C is greater than Y otherwise X |
+| if C
+
diff --git a/plugins/community/repos/SubmarineFree/res/AO-106.svg b/plugins/community/repos/SubmarineFree/res/AO-106.svg
new file mode 100644
index 00000000..2c712ebc
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/AO-106.svg
@@ -0,0 +1,424 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/AO-112.svg b/plugins/community/repos/SubmarineFree/res/AO-112.svg
new file mode 100644
index 00000000..acbf6198
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/AO-112.svg
@@ -0,0 +1,475 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/AO-118.svg b/plugins/community/repos/SubmarineFree/res/AO-118.svg
new file mode 100644
index 00000000..162c8c4b
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/AO-118.svg
@@ -0,0 +1,562 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/AO-124.svg b/plugins/community/repos/SubmarineFree/res/AO-124.svg
new file mode 100644
index 00000000..1f1385fe
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/AO-124.svg
@@ -0,0 +1,649 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/AO-136.svg b/plugins/community/repos/SubmarineFree/res/AO-136.svg
new file mode 100644
index 00000000..0ac2c0a9
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/AO-136.svg
@@ -0,0 +1,823 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large.svg
deleted file mode 100644
index 36fe70c8..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large_a.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large_a.svg
deleted file mode 100644
index dc3aaf68..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_large_a.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med.svg
deleted file mode 100644
index 5e0a882e..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med_a.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med_a.svg
deleted file mode 100644
index 85058fa8..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_med_a.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small.svg
deleted file mode 100644
index 8374f96e..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small_a.svg b/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small_a.svg
deleted file mode 100644
index e0dedda0..00000000
--- a/plugins/community/repos/SubmarineFree/res/Components/sub_knob_small_a.svg
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
diff --git a/plugins/community/repos/SubmarineFree/res/EO-102.svg b/plugins/community/repos/SubmarineFree/res/EO-102.svg
new file mode 100644
index 00000000..c5a47552
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/EO-102.svg
@@ -0,0 +1,649 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/FF-206.svg b/plugins/community/repos/SubmarineFree/res/FF-206.svg
new file mode 100644
index 00000000..6c243796
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/FF-206.svg
@@ -0,0 +1,108 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/LD-103.svg b/plugins/community/repos/SubmarineFree/res/LD-103.svg
new file mode 100644
index 00000000..944d9fa4
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/LD-103.svg
@@ -0,0 +1,145 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/NG-106.svg b/plugins/community/repos/SubmarineFree/res/NG-106.svg
new file mode 100644
index 00000000..9dabce81
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/NG-106.svg
@@ -0,0 +1,104 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/OG-104.svg b/plugins/community/repos/SubmarineFree/res/OG-104.svg
new file mode 100644
index 00000000..131190dd
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/OG-104.svg
@@ -0,0 +1,100 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/PG-104.svg b/plugins/community/repos/SubmarineFree/res/PG-104.svg
new file mode 100644
index 00000000..b7fb29d5
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/PG-104.svg
@@ -0,0 +1,96 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/SS-112.svg b/plugins/community/repos/SubmarineFree/res/SS-112.svg
new file mode 100644
index 00000000..053e9e53
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/SS-112.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/SS-208.svg b/plugins/community/repos/SubmarineFree/res/SS-208.svg
new file mode 100644
index 00000000..f5a326ba
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/SS-208.svg
@@ -0,0 +1,165 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/SS-212.svg b/plugins/community/repos/SubmarineFree/res/SS-212.svg
new file mode 100644
index 00000000..4147c199
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/SS-212.svg
@@ -0,0 +1,104 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/SS-220.svg b/plugins/community/repos/SubmarineFree/res/SS-220.svg
new file mode 100644
index 00000000..f67f4a99
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/SS-220.svg
@@ -0,0 +1,153 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/SS-221.svg b/plugins/community/repos/SubmarineFree/res/SS-221.svg
new file mode 100644
index 00000000..9ba5787c
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/SS-221.svg
@@ -0,0 +1,414 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/TD-116.svg b/plugins/community/repos/SubmarineFree/res/TD-116.svg
new file mode 100644
index 00000000..4337fa87
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/TD-116.svg
@@ -0,0 +1,181 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/TD-202.svg b/plugins/community/repos/SubmarineFree/res/TD-202.svg
new file mode 100644
index 00000000..0911b5ae
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/TD-202.svg
@@ -0,0 +1,81 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/TF-101.svg b/plugins/community/repos/SubmarineFree/res/TF-101.svg
new file mode 100644
index 00000000..bbbcfc0a
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/TF-101.svg
@@ -0,0 +1,329 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/TM-105.svg b/plugins/community/repos/SubmarineFree/res/TM-105.svg
new file mode 100644
index 00000000..2867cabd
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/TM-105.svg
@@ -0,0 +1,109 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/res/XG-104.svg b/plugins/community/repos/SubmarineFree/res/XG-104.svg
new file mode 100644
index 00000000..90e488ad
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/res/XG-104.svg
@@ -0,0 +1,100 @@
+
+
diff --git a/plugins/community/repos/SubmarineFree/src/AG-106.cpp b/plugins/community/repos/SubmarineFree/src/AG-106.cpp
deleted file mode 100644
index 5a2040aa..00000000
--- a/plugins/community/repos/SubmarineFree/src/AG-106.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct AG_106 : DS_Module {
- static const int deviceCount = 6;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_A_1,
- INPUT_A_2,
- INPUT_A_3,
- INPUT_A_4,
- INPUT_A_5,
- INPUT_A_6,
- INPUT_B_1,
- INPUT_B_2,
- INPUT_B_3,
- INPUT_B_4,
- INPUT_B_5,
- INPUT_B_6,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- AG_106() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void AG_106::step() {
- int connCount = 0;
- int setCount = 0;
- for (int i = 0; i < deviceCount; i++) {
- if (inputs[INPUT_A_1 + i].active) {
- connCount++;
- if (inputs[INPUT_A_1 + i].value > midpoint())
- setCount++;
- }
- if (inputs[INPUT_B_1 + i].active) {
- connCount++;
- if (inputs[INPUT_B_1 + i].value > midpoint())
- setCount++;
- }
- if (outputs[OUTPUT_1 + i].active) {
- if (connCount)
- outputs[OUTPUT_1 + i].value = (connCount == setCount)?voltage1:voltage0;
- else
- outputs[OUTPUT_1 + i].value = voltage0;
- connCount = 0;
- setCount = 0;
- }
- }
-}
-
-struct AG106 : ModuleWidget {
- AG106(AG_106 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/AG-106.svg")));
-
- for (int i = 0; i < AG_106::deviceCount; i++) {
- int offset = 58 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, AG_106::INPUT_A_1 + i));
- addInput(Port::create(Vec(4,47 + offset), Port::INPUT, module, AG_106::INPUT_B_1 + i));
-
- addOutput(Port::create(Vec(62,33 + offset), Port::OUTPUT, module, AG_106::OUTPUT_1 + i));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, AG106) {
- Model *modelAG106 = Model::create("SubmarineFree", "AG-106", "AG-106 AND Gates", LOGIC_TAG, MULTIPLE_TAG);
- return modelAG106;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/AG1.cpp b/plugins/community/repos/SubmarineFree/src/AG1.cpp
new file mode 100644
index 00000000..12e3d410
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/AG1.cpp
@@ -0,0 +1,97 @@
+#include "DS.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct AG_1 : DS_Module {
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_A_1,
+ INPUT_B_1 = x,
+ NUM_INPUTS = x + x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ AG_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+
+ void step() override {
+ int connCount = 0;
+ int setCount = 0;
+ for (int i = 0; i < x; i++) {
+ if (inputs[INPUT_A_1 + i].active) {
+ connCount++;
+ if (inputs[INPUT_A_1 + i].value > midpoint())
+ setCount++;
+ }
+ if (inputs[INPUT_B_1 + i].active) {
+ connCount++;
+ if (inputs[INPUT_B_1 + i].value > midpoint())
+ setCount++;
+ }
+ if (outputs[OUTPUT_1 + i].active) {
+ if (connCount)
+ outputs[OUTPUT_1 + i].value = (connCount == setCount)?voltage1:voltage0;
+ else
+ outputs[OUTPUT_1 + i].value = voltage0;
+ connCount = 0;
+ setCount = 0;
+ }
+ }
+ }
+};
+
+struct AG104 : ModuleWidget {
+ AG104(AG_1<4> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/AG-104.svg")));
+
+ for (int i = 0; i < 4; i++) {
+ int offset = 87 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, AG_1<4>::INPUT_A_1 + i));
+ addInput(Port::create(Vec(2.5,47 + offset), Port::INPUT, module, AG_1<4>::INPUT_B_1 + i));
+
+ addOutput(Port::create(Vec(2.5,75 + offset), Port::OUTPUT, module, AG_1<4>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct AG106 : ModuleWidget {
+ AG106(AG_1<6> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/AG-106.svg")));
+
+ for (int i = 0; i < 6; i++) {
+ int offset = 58 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, AG_1<6>::INPUT_A_1 + i));
+ addInput(Port::create(Vec(4,47 + offset), Port::INPUT, module, AG_1<6>::INPUT_B_1 + i));
+
+ addOutput(Port::create(Vec(62,33 + offset), Port::OUTPUT, module, AG_1<6>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AG104) {
+ Model *modelAG104 = Model::create, AG104>("Submarine (Free)", "AG-104", "AG-104 AND Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelAG104;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AG106) {
+ Model *modelAG106 = Model::create, AG106>("Submarine (Free)", "AG-106", "AG-106 AND Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelAG106;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/AO1.cpp b/plugins/community/repos/SubmarineFree/src/AO1.cpp
new file mode 100644
index 00000000..89e4b69d
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/AO1.cpp
@@ -0,0 +1,449 @@
+#include "SubmarineFree.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+namespace SubmarineAO {
+
+ typedef float (*func_t)(float, float, float);
+
+ struct Functor {
+ std::string name;
+ func_t func;
+ };
+
+#define sMIN(a,b) (((a)>(b))?(b):(a))
+#define sMAX(a,b) (((a)>(b))?(a):(b))
+
+#define LAMBDA(e) [](float x, float y, float c)->float { return e ; }
+#define X "X" // X
+#define Y "Y" // Y
+#define C "C" // C
+#define A "+" // Addition symbol
+#define S "-" // Subtraction symbol
+#define O "%" // Modulo symbol
+#define OP "(" // Open Parenthesis
+#define CP ")" // Close Parenthesis
+#define P "|" // Pipe symbol
+#define M "\xe2\xa8\xaf"
+#define D "\xc3\xb7" // Division symbol
+#define R "\xe2\x88\x9a" // Root symbol
+#define S2 "\xc2\xb2" // Superscript 2
+#define S3 "\xc2\xb3" // Superscript 3
+#define s0 "\xe2\x82\x80" // Subscript 0
+#define s1 "\xe2\x82\x81" // Subscript 1
+#define s2 "\xe2\x82\x82" // Subscript 2
+#define E "\xe2\x84\xaf" // e
+#define SA "\xe2\x81\xba" // Superscript +
+#define SX "\xcb\xa3" // Superscript x
+#define SY "\xca\xb8" // Superscript y
+#define SC "\xe1\xb6\x9c" // Superscript c
+#define MIN "min" // Minimum function
+#define MAX "max" // Maximum function
+#define COMMA "," // Comma symbol
+#define SIN "sin" // sine function
+#define COS "cos" // cosine function
+#define TAN "tan" // tangent function
+#define ASIN "asin" // arcsine function
+#define ACOS "acos" // arcosine function
+#define ATAN "atan" // arctangent function
+#define LOG "log" // log function
+#define LOG2 LOG s2 // base-2 log function
+#define LOG10 LOG s1 s0 // base-10 log function
+#define IF "if " // if conditional
+#define G ">" // Greater Than symbol
+#define L "<" // Less Than symbol
+#define Q "=" // Equality symbol
+#define Z "0" // Zero
+#define W "1" // One
+#define T "\xe2\x86\xa3" // Right arrow
+#define H "/" // Slash
+#define Pi "\xcf\x80" // PI
+#define TAU "\xcf\x84" // TAU
+
+ std::vector functions {
+ { "", LAMBDA( 0 ) }, // Passthrough
+ { C, LAMBDA( c ) }, // Addition
+ { X A C, LAMBDA( x + c ) },
+ { Y A C, LAMBDA( y + c ) },
+ { X A Y A C, LAMBDA( x + y + c ) },
+ { C S X, LAMBDA( c - x ) }, // Subtraction
+ { C S Y, LAMBDA( c - y ) },
+ { X S OP Y A C CP, LAMBDA( x - ( y + c ) ) },
+ { OP X A C CP S Y, LAMBDA( ( x + c ) - y ) },
+ { Y S OP X A C CP, LAMBDA( y - ( x + c ) ) },
+ { OP Y A C CP S X, LAMBDA( ( y + c ) - x ) },
+ { OP X M Y CP A C, LAMBDA( ( x * y ) + c ) }, // Multiplication
+ { OP X A C CP M Y, LAMBDA( ( x + c ) * y ) },
+ { X M OP Y A C CP, LAMBDA( x * ( y + c ) ) },
+ { X M C, LAMBDA( x * c ) },
+ { Y M C, LAMBDA( y * c ) },
+ { X M Y M C, LAMBDA( x * y * c ) },
+ { Pi M OP X A C CP, LAMBDA( M_PI * ( x + c ) ) },
+ { Pi M OP Y A C CP, LAMBDA( M_PI * ( y + c ) ) },
+ { TAU M OP X A C CP, LAMBDA( 2 * M_PI * ( x + c ) ) },
+ { TAU M OP Y A C CP, LAMBDA( 2 * M_PI * ( y + c ) ) },
+ { X D C, LAMBDA( x / c ) }, // Division
+ { C D X, LAMBDA( c / x ) },
+ { Y D C, LAMBDA( y / c ) },
+ { C D Y, LAMBDA( c / y ) },
+ { C A OP X D Y CP, LAMBDA( c + ( x / y ) ) },
+ { C A OP Y D X CP, LAMBDA( c + ( y / x ) ) },
+ { X A OP Y D C CP, LAMBDA( x + ( y / c ) ) },
+ { X A OP C D Y CP, LAMBDA( x + ( c / y ) ) },
+ { Y A OP X D C CP, LAMBDA( y + ( x / c ) ) },
+ { Y A OP C D X CP, LAMBDA( y + ( c / x ) ) },
+ { OP X A C CP D Y, LAMBDA( ( x + c ) / y ) },
+ { X D OP Y A C CP, LAMBDA( x / ( y + c ) ) },
+ { OP Y A C CP D X, LAMBDA( ( y + c ) / x ) },
+ { Y D OP X A C CP, LAMBDA( y / ( x + c ) ) },
+ { OP X A C CP O Y, LAMBDA( fmodf( x + c , y ) ) }, // Modulo
+ { OP Y A C CP O X, LAMBDA( fmodf( y + c , x ) ) },
+ { X O OP Y A C CP, LAMBDA( fmodf( x , y + c ) ) },
+ { Y O OP X A C CP, LAMBDA( fmodf( y , x + c) ) },
+ { X O C, LAMBDA( fmodf( x , c ) ) },
+ { Y O C, LAMBDA( fmodf( y , c ) ) },
+ { X S2 A C, LAMBDA( x * x + c ) }, // Quadratic
+ { Y S2 A C, LAMBDA( y * y + c ) },
+ { OP X A C CP S2, LAMBDA( ( x + c ) * ( x + c ) ) },
+ { OP Y A C CP S2, LAMBDA( ( y + c ) * ( y + c ) ) },
+ { X S2 A Y A C, LAMBDA( x * x + y + c ) },
+ { Y S2 A X A C, LAMBDA( y * y + x + c ) },
+ { X S2 A C Y, LAMBDA( x * x + c * y ) },
+ { Y S2 A C X, LAMBDA( y * y + c * x ) },
+ { R OP X A C CP, LAMBDA( sqrt( x + c ) ) }, // Square Root
+ { R OP Y A C CP, LAMBDA( sqrt( y + c ) ) },
+ { C SX, LAMBDA( powf( c , x ) ) }, // Powers
+ { C SY, LAMBDA( powf( c , y ) ) },
+ { C SX SA SY, LAMBDA( powf( c , x + y ) ) },
+ { C SX SY, LAMBDA( powf( c , x * y ) ) },
+ { X SC, LAMBDA( powf( x , c ) ) },
+ { Y SC, LAMBDA( powf( y , c ) ) },
+ { X SY SA SC, LAMBDA( powf( x , y + c ) ) },
+ { Y SX SA SC, LAMBDA( powf( y , x + c ) ) },
+ { X SC SY, LAMBDA( powf( x , c * y ) ) },
+ { Y SC SX, LAMBDA( powf( y , c * x ) ) },
+ { P X A C P, LAMBDA( abs( x + c ) ) }, // Modulus
+ { P Y A C P, LAMBDA( abs( y + c ) ) },
+ { MIN OP X A C COMMA Y CP, LAMBDA( sMIN( x + c, y ) ) }, // Minmax
+ { MIN OP X COMMA C CP, LAMBDA( sMIN( x, c ) ) },
+ { MIN OP Y COMMA C CP, LAMBDA( sMIN( y, c ) ) },
+ { MAX OP X A C COMMA Y CP, LAMBDA( sMAX( x + c, y ) ) },
+ { MAX OP X COMMA C CP, LAMBDA( sMAX( x, c ) ) },
+ { MAX OP Y COMMA C CP, LAMBDA( sMAX( y, c ) ) },
+ { SIN OP X A C CP, LAMBDA( sin( x + c ) ) }, // Trigonometric
+ { SIN OP Y A C CP, LAMBDA( sin( y + c ) ) },
+ { SIN OP X A Y CP, LAMBDA( sin( x + y ) ) },
+ { SIN OP C X CP, LAMBDA( sin( c * x ) ) },
+ { SIN OP C Y CP, LAMBDA( sin( c * y ) ) },
+ { SIN OP X Y CP, LAMBDA( sin( x * y ) ) },
+ { COS OP X A C CP, LAMBDA( cos( x + c ) ) },
+ { COS OP Y A C CP, LAMBDA( cos( y + c ) ) },
+ { COS OP X A Y CP, LAMBDA( cos( x + y ) ) },
+ { COS OP C X CP, LAMBDA( cos( c * x ) ) },
+ { COS OP C Y CP, LAMBDA( cos( c * y ) ) },
+ { COS OP X Y CP, LAMBDA( cos( x * y ) ) },
+ { TAN OP X A C CP, LAMBDA( tan( x + c ) ) },
+ { TAN OP Y A C CP, LAMBDA( tan( y + c ) ) },
+ { TAN OP X A Y CP, LAMBDA( tan( x + y ) ) },
+ { TAN OP C X CP, LAMBDA( tan( c * x ) ) },
+ { TAN OP C Y CP, LAMBDA( tan( c * y ) ) },
+ { TAN OP X Y CP, LAMBDA( tan( x * y ) ) },
+ { ASIN OP X A C CP, LAMBDA( asin( x + c ) ) },
+ { ASIN OP Y A C CP, LAMBDA( asin( y + c ) ) },
+ { ASIN OP X A Y CP, LAMBDA( asin( x + y ) ) },
+ { ASIN OP C X CP, LAMBDA( asin( c * x ) ) },
+ { ASIN OP C Y CP, LAMBDA( asin( c * y ) ) },
+ { ASIN OP X Y CP, LAMBDA( asin( x * y ) ) },
+ { ACOS OP X A C CP, LAMBDA( acos( x + c ) ) },
+ { ACOS OP Y A C CP, LAMBDA( acos( y + c ) ) },
+ { ACOS OP X A Y CP, LAMBDA( acos( x + y ) ) },
+ { ACOS OP C X CP, LAMBDA( acos( c * x ) ) },
+ { ACOS OP C Y CP, LAMBDA( acos( c * y ) ) },
+ { ACOS OP X Y CP, LAMBDA( acos( x * y ) ) },
+ { ATAN OP X A C CP, LAMBDA( atan( x + c ) ) },
+ { ATAN OP Y A C CP, LAMBDA( atan( y + c ) ) },
+ { ATAN OP X A Y CP, LAMBDA( atan( x + y ) ) },
+ { ATAN OP C X CP, LAMBDA( atan( c * x ) ) },
+ { ATAN OP C Y CP, LAMBDA( atan( c * y ) ) },
+ { ATAN OP X Y CP, LAMBDA( atan( x * y ) ) },
+ { LOG OP X A C CP, LAMBDA( log( x + c ) ) }, // Logarithmic
+ { LOG OP Y A C CP, LAMBDA( log( y + c ) ) },
+ { LOG2 OP X A C CP, LAMBDA( log2( x + c ) ) },
+ { LOG2 OP Y A C CP, LAMBDA( log2( y + c ) ) },
+ { LOG10 OP X A C CP, LAMBDA( log10( x + c ) ) },
+ { LOG10 OP Y A C CP, LAMBDA( log10( y + c ) ) },
+ { E SX SA SC, LAMBDA( exp( x + c ) ) }, // Exponential
+ { E SY SA SC, LAMBDA( exp( y + c ) ) },
+ { E SC SX, LAMBDA( exp( c * x ) ) },
+ { E SC SY, LAMBDA( exp( c * y ) ) },
+ { "2" SX SA SC, LAMBDA( powf( 2, x + c ) ) },
+ { "2" SY SA SC, LAMBDA( powf( 2, y + c ) ) },
+ { "2" SC SX, LAMBDA( powf( 2, c * x ) ) },
+ { "2" SC SY, LAMBDA( powf( 2, c * y ) ) },
+ { "10" SX SA SC, LAMBDA( powf( 10, x + c ) ) },
+ { "10" SY SA SC, LAMBDA( powf( 10, y + c ) ) },
+ { "10" SC SX, LAMBDA( powf( 10, c * x ) ) },
+ { "10" SC SY, LAMBDA( powf( 10, c * y ) ) },
+
+ { IF X G Z T Y H C, LAMBDA( (x > 0) ? y : c ) }, // Conditional
+ { IF X L Z T Y H C, LAMBDA( (x < 0) ? y : c ) },
+ { IF X Q Z T Y H C, LAMBDA( (x == 0) ? y : c ) },
+ { IF X G Z T C H Y, LAMBDA( (x > 0) ? c : y ) },
+ { IF X L Z T C H Y, LAMBDA( (x < 0) ? c : y ) },
+ { IF X Q Z T C H Y, LAMBDA( (x == 0) ? c : y ) },
+ { IF X G Z T W H Z, LAMBDA( (x > 0) ? 1 : 0 ) },
+ { IF X L Z T W H Z, LAMBDA( (x < 0) ? 1 : 0 ) },
+ { IF X Q Z T W H Z, LAMBDA( (x == 0) ? 1 : 0 ) },
+ { IF X G Z T X H C, LAMBDA( (x > 0) ? x : c ) },
+ { IF X L Z T X H C, LAMBDA( (x < 0) ? x : c ) },
+ { IF X Q Z T X H C, LAMBDA( (x == 0) ? x : c ) },
+ { IF X G Z T C H X, LAMBDA( (x > 0) ? c : x ) },
+ { IF X L Z T C H X, LAMBDA( (x < 0) ? c : x ) },
+ { IF X Q Z T C H X, LAMBDA( (x == 0) ? c : x ) },
+
+ { IF Y G Z T X H C, LAMBDA( (y > 0) ? x : c ) },
+ { IF Y L Z T X H C, LAMBDA( (y < 0) ? x : c ) },
+ { IF Y Q Z T X H C, LAMBDA( (y == 0) ? x : c ) },
+ { IF Y G Z T C H X, LAMBDA( (y > 0) ? c : x ) },
+ { IF Y L Z T C H X, LAMBDA( (y < 0) ? c : x ) },
+ { IF Y Q Z T C H X, LAMBDA( (y == 0) ? c : x ) },
+ { IF Y G Z T W H Z, LAMBDA( (y > 0) ? 1 : 0 ) },
+ { IF Y L Z T W H Z, LAMBDA( (y < 0) ? 1 : 0 ) },
+ { IF Y Q Z T W H Z, LAMBDA( (y == 0) ? 1 : 0 ) },
+ { IF Y G Z T Y H C, LAMBDA( (y > 0) ? y : c ) },
+ { IF Y L Z T Y H C, LAMBDA( (y < 0) ? y : c ) },
+ { IF Y Q Z T Y H C, LAMBDA( (y == 0) ? y : c ) },
+ { IF Y G Z T C H Y, LAMBDA( (y > 0) ? c : y ) },
+ { IF Y L Z T C H Y, LAMBDA( (y < 0) ? c : y ) },
+ { IF Y Q Z T C H Y, LAMBDA( (y == 0) ? c : y ) },
+
+ { IF X G Y T C H Z, LAMBDA( (x > y) ? c : 0 ) },
+ { IF X L Y T C H Z, LAMBDA( (x < y) ? c : 0 ) },
+ { IF X Q Y T C H Z, LAMBDA( (x == y) ? c : 0 ) },
+ { IF Y G X T C H Z, LAMBDA( (y > x) ? c : 0 ) },
+ { IF Y L X T C H Z, LAMBDA( (y < x) ? c : 0 ) },
+ { IF X G Y T X H Z, LAMBDA( (x > y) ? x : 0 ) },
+ { IF X L Y T X H Z, LAMBDA( (x < y) ? x : 0 ) },
+ { IF X Q Y T X H Z, LAMBDA( (x == y) ? x : 0 ) },
+ { IF Y G X T X H Z, LAMBDA( (y > x) ? x : 0 ) },
+ { IF Y L X T X H Z, LAMBDA( (y < x) ? x : 0 ) },
+ { IF X G Y T Y H Z, LAMBDA( (x > y) ? y : 0 ) },
+ { IF X L Y T Y H Z, LAMBDA( (x < y) ? y : 0 ) },
+ { IF X Q Y T Y H Z, LAMBDA( (x == y) ? y : 0 ) },
+ { IF Y G X T Y H Z, LAMBDA( (y > x) ? y : 0 ) },
+ { IF Y L X T Y H Z, LAMBDA( (y < x) ? y : 0 ) },
+
+ { IF X G C T Y H Z, LAMBDA( (x > c) ? y : 0 ) },
+ { IF X L C T Y H Z, LAMBDA( (x < c) ? y : 0 ) },
+ { IF X Q C T Y H Z, LAMBDA( (x == c) ? y : 0 ) },
+ { IF C G X T Y H Z, LAMBDA( (c > x) ? y : 0 ) },
+ { IF C L X T Y H Z, LAMBDA( (c < x) ? y : 0 ) },
+ { IF X G C T X H Z, LAMBDA( (x > c) ? x : 0 ) },
+ { IF X L C T X H Z, LAMBDA( (x < c) ? x : 0 ) },
+ { IF X Q C T X H Z, LAMBDA( (x == c) ? x : 0 ) },
+ { IF C G X T X H Z, LAMBDA( (c > x) ? x : 0 ) },
+ { IF C L X T X H Z, LAMBDA( (c < x) ? x : 0 ) },
+ { IF X G C T X H Y, LAMBDA( (x > c) ? x : y ) },
+ { IF X L C T X H Y, LAMBDA( (x < c) ? x : y ) },
+ { IF X Q C T X H Y, LAMBDA( (x == c) ? x : y ) },
+ { IF C G X T X H Y, LAMBDA( (c > x) ? x : y ) },
+ { IF C L X T X H Y, LAMBDA( (c < x) ? x : y ) },
+
+ { IF Y G C T X H Z, LAMBDA( (y > c) ? x : 0 ) },
+ { IF Y L C T X H Z, LAMBDA( (y < c) ? x : 0 ) },
+ { IF Y Q C T X H Z, LAMBDA( (y == c) ? x : 0 ) },
+ { IF C G Y T X H Z, LAMBDA( (c > y) ? x : 0 ) },
+ { IF C L Y T X H Z, LAMBDA( (c < y) ? x : 0 ) },
+ { IF Y G C T Y H Z, LAMBDA( (y > c) ? y : 0 ) },
+ { IF Y L C T Y H Z, LAMBDA( (y < c) ? y : 0 ) },
+ { IF Y Q C T Y H Z, LAMBDA( (y == c) ? y : 0 ) },
+ { IF C G Y T Y H Z, LAMBDA( (c > y) ? y : 0 ) },
+ { IF C L Y T Y H Z, LAMBDA( (c < y) ? y : 0 ) },
+ { IF Y G C T Y H X, LAMBDA( (y > c) ? y : x ) },
+ { IF Y L C T Y H X, LAMBDA( (y < c) ? y : x ) },
+ { IF Y Q C T Y H X, LAMBDA( (y == c) ? y : x ) },
+ { IF C G Y T Y H X, LAMBDA( (c > y) ? y : x ) },
+ { IF C L Y T Y H X, LAMBDA( (c < y) ? y : x ) },
+
+ };
+
+#undef X
+#undef Y
+#undef C
+#undef A
+#undef S
+#undef O
+#undef OP
+#undef CP
+#undef P
+#undef M
+#undef D
+#undef R
+#undef S2
+#undef S3
+#undef s0
+#undef s1
+#undef s2
+#undef E
+#undef SA
+#undef SX
+#undef SY
+#undef SC
+#undef COMMA
+#undef MIN
+#undef MAX
+#undef SIN
+#undef COS
+#undef TAN
+#undef ASIN
+#undef ACOS
+#undef ATAN
+#undef LOG
+#undef LOG2
+#undef LOG10
+#undef IF
+#undef G
+#undef L
+#undef Q
+#undef Z
+#undef W
+#undef T
+#undef H
+#undef Pi
+#undef TAU
+
+} // end namespace SubmarineA0
+
+struct AOFuncDisplay : Knob {
+ std::shared_ptr font;
+ AOFuncDisplay() {
+ box.size.x = 80;
+ box.size.y = 15;
+ snap = true;
+ smooth = false;
+ speed = 0.5f;
+ font = Font::load(assetGlobal("res/fonts/DejaVuSans.ttf"));
+ }
+ void draw(NVGcontext *vg) override {
+ nvgFontSize(vg, 16);
+ nvgFontFaceId(vg, font->handle);
+ nvgFillColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xff));
+ nvgTextAlign(vg, NVG_ALIGN_CENTER);
+ nvgText(vg, 41.5, 13, SubmarineAO::functions[value].name.c_str(), NULL);
+ }
+};
+
+struct AOConstDisplay : Knob {
+ std::shared_ptr font;
+ AOConstDisplay() {
+ box.size.x = 80;
+ box.size.y = 15;
+ snap = true;
+ speed = 0.005;
+ font = Font::load(assetGlobal("res/fonts/DejaVuSans.ttf"));
+ }
+ void draw(NVGcontext *vg) override {
+ char mtext[41];
+ sprintf(mtext, "C=%4.2f", ((int)value)/100.0f);
+ nvgFontSize(vg, 16);
+ nvgFontFaceId(vg, font->handle);
+ nvgFillColor(vg, nvgRGBA(0x28, 0xb0, 0xf3, 0xff));
+ nvgTextAlign(vg, NVG_ALIGN_CENTER);
+ nvgText(vg, 41.5, 13, mtext, NULL);
+ }
+};
+
+template
+struct AO1 : Module {
+ enum ParamIds {
+ PARAM_FUNC_1,
+ PARAM_CONST_1 = x * y,
+ NUM_PARAMS = 2 * x * y
+ };
+ enum InputIds {
+ INPUT_X_1,
+ INPUT_Y_1 = x,
+ NUM_INPUTS = x + y
+ };
+ enum OutputIds {
+ OUTPUT_X_1,
+ OUTPUT_Y_1 = x,
+ NUM_OUTPUTS = x + y
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ AO1() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ float vx[x];
+ for (unsigned int ix = 0; ix < x; ix++) {
+ vx[ix] = inputs[INPUT_X_1 + ix].value;
+ }
+ for (unsigned int iy = 0; iy < y; iy++) {
+ float vy = inputs[INPUT_Y_1 + iy].value;
+ for (unsigned int ix = 0; ix < x; ix++) {
+ unsigned int f = params[PARAM_FUNC_1 + ix + iy * x].value;
+ if (f >= SubmarineAO::functions.size())
+ f = SubmarineAO::functions.size() - 1;
+ if (f > 0)
+ vy = vx[ix] = SubmarineAO::functions[f].func(vx[ix], vy, ((int)params[PARAM_CONST_1 + ix + iy * x].value)/100.0f);
+ // if f is equal to 0, then both x and y pass (crossing) through the module unchanged.
+ }
+ outputs[OUTPUT_Y_1 + iy].value = std::isfinite(vy)?vy:0.0f;
+ }
+ for (unsigned int ix = 0; ix < x; ix++) {
+ outputs[OUTPUT_X_1 + ix].value = std::isfinite(vx[ix])?vx[ix]:0.0f;
+ }
+ }
+};
+
+template
+struct AOWidget : ModuleWidget {
+ AOWidget(AO1 *module) : ModuleWidget(module) {
+ setPanel(SubHelper::LoadPanel(plugin, "AO-1", x*y));
+ for (unsigned int ix = 0; ix < x; ix++) {
+ addInput(Port::create(Vec(4, 61 + ix * 46), Port::INPUT, module, AO1::INPUT_X_1 + ix));
+ addOutput(Port::create(Vec(46 + y * 90, 61 + ix * 46), Port::OUTPUT, module, AO1::OUTPUT_X_1 + ix));
+ }
+ for (unsigned int iy = 0; iy < y; iy++) {
+ addInput(Port::create(Vec(70 + 90 * iy, 19), Port::INPUT, module, AO1::INPUT_Y_1 + iy));
+ addOutput(Port::create(Vec(70 + 90 * iy, 335), Port::OUTPUT, module, AO1::OUTPUT_Y_1 + iy));
+ }
+ for (unsigned int iy = 0; iy < y; iy++) {
+ for (unsigned int ix = 0; ix < x; ix++) {
+ addParam(ParamWidget::create(Vec(42.5 + 90 * iy, 59 + 46 * ix), module, AO1::PARAM_FUNC_1 + ix + iy * x, 0.0f, SubmarineAO::functions.size() - 1.0f, 0.0f ));
+ addParam(ParamWidget::create(Vec(42.5 + 90 * iy, 78 + 46 * ix), module, AO1::PARAM_CONST_1 + ix + iy * x, -10000.0f, 10000.0f, 0.0f));
+ }
+ }
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AO106) {
+ Model *modelAO106 = Model::create, AOWidget<6,1>>("Submarine (Free)", "A0-106", "A0-106 Arithmetic Operators", UTILITY_TAG, MULTIPLE_TAG);
+ return modelAO106;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AO112) {
+ Model *modelAO112 = Model::create, AOWidget<6,2>>("Submarine (Free)", "A0-112", "A0-112 Arithmetic Operators", UTILITY_TAG, MULTIPLE_TAG);
+ return modelAO112;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AO118) {
+ Model *modelAO118 = Model::create, AOWidget<6,3>>("Submarine (Free)", "A0-118", "A0-118 Arithmetic Operators", UTILITY_TAG, MULTIPLE_TAG);
+ return modelAO118;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AO124) {
+ Model *modelAO124 = Model::create, AOWidget<6,4>>("Submarine (Free)", "A0-124", "A0-124 Arithmetic Operators", UTILITY_TAG, MULTIPLE_TAG);
+ return modelAO124;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, AO136) {
+ Model *modelAO136 = Model::create, AOWidget<6,6>>("Submarine (Free)", "A0-136", "A0-136 Arithmetic Operators", UTILITY_TAG, MULTIPLE_TAG);
+ return modelAO136;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/BB-120.cpp b/plugins/community/repos/SubmarineFree/src/BB-120.cpp
deleted file mode 100644
index 9b4cfc93..00000000
--- a/plugins/community/repos/SubmarineFree/src/BB-120.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct BB_120 : DS_Module {
- static const int deviceCount = 20;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_CLK,
- INPUT_CV,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- OUTPUT_11,
- OUTPUT_12,
- OUTPUT_13,
- OUTPUT_14,
- OUTPUT_15,
- OUTPUT_16,
- OUTPUT_17,
- OUTPUT_18,
- OUTPUT_19,
- OUTPUT_20,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- float sample[deviceCount] = {};
- DS_Schmitt schmittTrigger;
-
- BB_120() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void BB_120::step() {
- int triggered = true;
- if (inputs[INPUT_CLK].active) {
- triggered = schmittTrigger.redge(this, inputs[INPUT_CLK].value);
- }
- if (triggered) {
- for (int i = deviceCount - 1; i; i--)
- sample[i] = sample[i - 1];
- sample[0] = inputs[INPUT_CV].value;
- }
- for (int i = 0; i < deviceCount; i++)
- outputs[OUTPUT_1 + i].value = sample[i];
-}
-
-struct BB120 : ModuleWidget {
- BB120(BB_120 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BB-120.svg")));
-
- addInput(Port::create(Vec(4.5,19), Port::INPUT, module, BB_120::INPUT_CLK));
- addInput(Port::create(Vec(31.5,34), Port::INPUT, module, BB_120::INPUT_CV));
-
- for (int i = 0; i < BB_120::deviceCount; i+=2) {
- int offset = 15 * i;
-
- addOutput(Port::create(Vec(4,53 + offset), Port::OUTPUT, module, BB_120::OUTPUT_1 + i));
- addOutput(Port::create(Vec(31,68 + offset), Port::OUTPUT, module, BB_120::OUTPUT_1 + i + 1));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BB120) {
- Model *modelBB120 = Model::create("SubmarineFree", "BB-120", "BB-120 20-Stage Bucket Brigade Sample and Hold", LOGIC_TAG, DELAY_TAG, SAMPLE_AND_HOLD_TAG, MULTIPLE_TAG);
- return modelBB120;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/BB1.cpp b/plugins/community/repos/SubmarineFree/src/BB1.cpp
new file mode 100644
index 00000000..f59bc0ef
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/BB1.cpp
@@ -0,0 +1,107 @@
+#include
+#include
+#include "DS.hpp"
+#include
+#include
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct BB_1 : DS_Module {
+ int doResetFlag = 0;
+ int doRandomFlag = 0;
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_CLK,
+ INPUT_CV,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ float sample[x] = {};
+ DS_Schmitt schmittTrigger;
+
+ BB_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ if (doResetFlag) doReset();
+ if (doRandomFlag) doRandomize();
+ int triggered = true;
+ if (inputs[INPUT_CLK].active) {
+ triggered = schmittTrigger.redge(this, inputs[INPUT_CLK].value);
+ }
+ if (triggered) {
+ for (int i = x - 1; i; i--)
+ sample[i] = sample[i - 1];
+ sample[0] = inputs[INPUT_CV].value;
+ }
+ for (int i = 0; i < x; i++)
+ outputs[OUTPUT_1 + i].value = sample[i];
+ }
+ void doRandomize() {
+ doRandomFlag = 0;
+ std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+ std::uniform_real_distribution distribution(voltage0, voltage1);
+ for (int i = 0; i < x; i++) {
+ outputs[OUTPUT_1 + i].value = sample[i] = distribution(generator);
+ }
+ }
+ void doReset() {
+ doResetFlag = 0;
+ for (int i = 0; i < x; i++)
+ outputs[OUTPUT_1 + i].value = sample[i] = 0.0f;
+ }
+ void onRandomize() override {
+ if (rack::global->gPaused) {
+ doRandomize();
+ }
+ else {
+ doResetFlag = 0;
+ doRandomFlag = 1;
+ }
+ }
+ void onReset() override {
+ if (rack::global->gPaused) {
+ doReset();
+ }
+ else {
+ doRandomFlag = 0;
+ doResetFlag = 1;
+ }
+ }
+};
+
+struct BB120 : ModuleWidget {
+ BB120(BB_1<20> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/BB-120.svg")));
+
+ addInput(Port::create(Vec(4.5,19), Port::INPUT, module, BB_1<20>::INPUT_CLK));
+ addInput(Port::create(Vec(31.5,34), Port::INPUT, module, BB_1<20>::INPUT_CV));
+
+ for (int i = 0; i < 20; i+=2) {
+ int offset = 15 * i;
+
+ addOutput(Port::create(Vec(4,53 + offset), Port::OUTPUT, module, BB_1<20>::OUTPUT_1 + i));
+ addOutput(Port::create(Vec(31,68 + offset), Port::OUTPUT, module, BB_1<20>::OUTPUT_1 + i + 1));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BB120) {
+ Model *modelBB120 = Model::create, BB120>("Submarine (Free)", "BB-120", "BB-120 20-Stage Bucket Brigade Sample and Hold", LOGIC_TAG, DELAY_TAG, SAMPLE_AND_HOLD_TAG, MULTIPLE_TAG);
+ return modelBB120;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/BP.cpp b/plugins/community/repos/SubmarineFree/src/BP.cpp
deleted file mode 100644
index 0a05e4c5..00000000
--- a/plugins/community/repos/SubmarineFree/src/BP.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-#include "SubmarineFree.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct BP : Module {
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- NUM_INPUTS
- };
- enum OutputIds {
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- BP() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-
- // For more advanced Module features, read Rack's engine.hpp header file
- // - toJson, fromJson: serialization of internal data
- // - onSampleRateChange: event triggered by a change of sample rate
- // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
-};
-
-void BP::step() {
-}
-
-struct BP101 : ModuleWidget {
- BP101(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-101.svg")));
- }
-};
-
-struct BP102 : ModuleWidget {
- BP102(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-102.svg")));
- }
-};
-
-struct BP104 : ModuleWidget {
- BP104(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-104.svg")));
- }
-};
-
-struct BP108 : ModuleWidget {
- BP108(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-108.svg")));
- }
-};
-
-struct BP110 : ModuleWidget {
- BP110(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-110.svg")));
- }
-};
-
-struct BP112 : ModuleWidget {
- BP112(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-112.svg")));
- }
-};
-
-struct BP116 : ModuleWidget {
- BP116(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-116.svg")));
- }
-};
-
-struct BP120 : ModuleWidget {
- BP120(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-120.svg")));
- }
-};
-
-struct BP124 : ModuleWidget {
- BP124(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-124.svg")));
- }
-};
-
-struct BP132 : ModuleWidget {
- BP132(BP *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/BP-132.svg")));
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-// Specify the Module and ModuleWidget subclass, human-readable
-// author name for categorization per plugin, module slug (should never
-// change), human-readable module name, and any number of tags
-// (found in `include/tags.hpp`) separated by commas.
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP101) {
- Model *modelBP101 = Model::create("SubmarineFree", "BP-101", "BP-101 Blanking Plate", BLANK_TAG);
- return modelBP101;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP102) {
- Model *modelBP102 = Model::create("SubmarineFree", "BP-102", "BP-102 Blanking Plate", BLANK_TAG);
- return modelBP102;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP104) {
- Model *modelBP104 = Model::create("SubmarineFree", "BP-104", "BP-104 Blanking Plate", BLANK_TAG);
- return modelBP104;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP108) {
- Model *modelBP108 = Model::create("SubmarineFree", "BP-108", "BP-108 Blanking Plate", BLANK_TAG);
- return modelBP108;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP110) {
- Model *modelBP110 = Model::create("SubmarineFree", "BP-110", "BP-110 Blanking Plate", BLANK_TAG);
- return modelBP110;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP112) {
- Model *modelBP112 = Model::create("SubmarineFree", "BP-112", "BP-112 Blanking Plate", BLANK_TAG);
- return modelBP112;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP116) {
- Model *modelBP116 = Model::create("SubmarineFree", "BP-116", "BP-116 Blanking Plate", BLANK_TAG);
- return modelBP116;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP120) {
- Model *modelBP120 = Model::create("SubmarineFree", "BP-120", "BP-120 Blanking Plate", BLANK_TAG);
- return modelBP120;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP124) {
- Model *modelBP124 = Model::create("SubmarineFree", "BP-124", "BP-124 Blanking Plate", BLANK_TAG);
- return modelBP124;
-}
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP132) {
- Model *modelBP132 = Model::create("SubmarineFree", "BP-132", "BP-132 Blanking Plate", BLANK_TAG);
- return modelBP132;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/BP1.cpp b/plugins/community/repos/SubmarineFree/src/BP1.cpp
new file mode 100644
index 00000000..307af4f1
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/BP1.cpp
@@ -0,0 +1,64 @@
+#include "SubmarineFree.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct BP1 : ModuleWidget {
+ BP1(Module *module) : ModuleWidget(module) {
+ setPanel(SubHelper::LoadPanel(plugin, "BP-1", x));
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP101) {
+ Model *modelBP101 = Model::create>("Submarine (Free)", "BP-101", "BP-101 Blanking Plate", BLANK_TAG);
+ return modelBP101;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP102) {
+ Model *modelBP102 = Model::create>("Submarine (Free)", "BP-102", "BP-102 Blanking Plate", BLANK_TAG);
+ return modelBP102;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP104) {
+ Model *modelBP104 = Model::create>("Submarine (Free)", "BP-104", "BP-104 Blanking Plate", BLANK_TAG);
+ return modelBP104;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP108) {
+ Model *modelBP108 = Model::create>("Submarine (Free)", "BP-108", "BP-108 Blanking Plate", BLANK_TAG);
+ return modelBP108;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP110) {
+ Model *modelBP110 = Model::create>("Submarine (Free)", "BP-110", "BP-110 Blanking Plate", BLANK_TAG);
+ return modelBP110;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP112) {
+ Model *modelBP112 = Model::create>("Submarine (Free)", "BP-112", "BP-112 Blanking Plate", BLANK_TAG);
+ return modelBP112;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP116) {
+ Model *modelBP116 = Model::create>("Submarine (Free)", "BP-116", "BP-116 Blanking Plate", BLANK_TAG);
+ return modelBP116;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP120) {
+ Model *modelBP120 = Model::create>("Submarine (Free)", "BP-120", "BP-120 Blanking Plate", BLANK_TAG);
+ return modelBP120;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP124) {
+ Model *modelBP124 = Model::create>("Submarine (Free)", "BP-124", "BP-124 Blanking Plate", BLANK_TAG);
+ return modelBP124;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, BP132) {
+ Model *modelBP132 = Model::create>("Submarine (Free)", "BP-132", "BP-132 Blanking Plate", BLANK_TAG);
+ return modelBP132;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/ComponentLibrary/Port.cpp b/plugins/community/repos/SubmarineFree/src/ComponentLibrary/Port.cpp
new file mode 100644
index 00000000..f2806af0
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/ComponentLibrary/Port.cpp
@@ -0,0 +1,62 @@
+#include "../SubmarineFree.hpp"
+#include "util/color.hpp"
+
+void SilverPort::draw(NVGcontext *vg) {
+ float radius = box.size.x / 2.0f;
+
+ // Shadow
+ {
+ nvgBeginPath(vg);
+ nvgCircle(vg, radius, radius * 1.2, radius);
+ nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.15));
+ nvgFill(vg);
+ }
+
+ // Switch
+ {
+ nvgBeginPath(vg);
+ nvgRect(vg, 6, 6, 13, 13);
+ nvgFillColor(vg, nvgRGB(0,0,0));
+ nvgFill(vg);
+ nvgBeginPath(vg);
+ nvgRect(vg, 10, 6, 5, 13);
+ nvgFillPaint(vg, nvgLinearGradient(vg, radius, 19, radius, radius, nvgRGB(0x60, 0x60, 0x60), nvgRGB(0,0,0)));
+ nvgFill(vg);
+ }
+
+ // Port body
+ {
+ nvgBeginPath(vg);
+ nvgCircle(vg, radius, radius, 7);
+ nvgStrokeWidth(vg, 4);
+ nvgStrokePaint(vg, nvgRadialGradient(vg, radius + 0.3, radius + 1, 0, 9, nvgRGB(0x20, 0x20, 0x20), col));
+ nvgStroke(vg);
+ }
+
+ // Outer rim
+ {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, 0.66556777, 9.9934984);
+ nvgArc(vg, radius, radius, radius - 1, M_PI + 0.2076, -0.2076, NVG_CW);
+ nvgArc(vg, 24.5, 12.5, 2.5, 1.467 - M_PI, M_PI - 1.467, NVG_CCW);
+ nvgArc(vg, radius, radius, radius - 1, 0.2076, M_PI - 0.2076, NVG_CW);
+ nvgArc(vg, 0.5, 12.5, 2.5, 1.467, -1.467, NVG_CCW);
+ nvgClosePath(vg);
+ //nvgCircle(vg, radius, radius, 8.5709);
+ nvgCircle(vg, radius, radius, 8.0);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgStrokeColor(vg, nvgRGB(0x66,0x66,0x66));
+ nvgStrokeWidth(vg, 0.80645);
+ if (type == Port::OUTPUT) {
+ nvgFillPaint(vg, nvgRadialGradient(vg, radius + 0.3, radius + 1, 1, 12, col, nvgRGB(0x3f, 0x3f, 0x3f)));
+ nvgStrokeColor(vg, nvgRGB(0x36,0x36,0x36));
+ }
+ else {
+ nvgFillPaint(vg, nvgRadialGradient(vg, radius + 0.3, radius + 1, 1, 12, col, nvgRGB(0xff, 0xff, 0xff)));
+ nvgStrokeColor(vg, nvgRGB(0x66,0x66,0x66));
+ }
+ nvgFill(vg);
+ nvgStroke(vg);
+ }
+
+}
diff --git a/plugins/community/repos/SubmarineFree/src/ComponentLibrary/components.hpp b/plugins/community/repos/SubmarineFree/src/ComponentLibrary/components.hpp
index c3fe5a43..08120cc8 100644
--- a/plugins/community/repos/SubmarineFree/src/ComponentLibrary/components.hpp
+++ b/plugins/community/repos/SubmarineFree/src/ComponentLibrary/components.hpp
@@ -2,6 +2,27 @@
// Ports
//////////////////
+struct SilverPort : Port {
+ NVGcolor col = nvgRGB(0xf0, 0xf0, 0xf0);
+ SilverPort() {
+ box.size.x = 25;
+ box.size.y = 25;
+ }
+ void draw(NVGcontext *vg) override;
+};
+
+struct RedPort : SilverPort {
+ RedPort() { col = nvgRGB(0xff, 0x20, 0x20); }
+};
+
+struct BluePort : SilverPort {
+ BluePort() { col = nvgRGB(0x29, 0xb2, 0xef); }
+};
+
+struct BlackPort : SilverPort {
+ BlackPort() { col = nvgRGB(0x40, 0x40, 0x40); }
+};
+/*
struct sub_port : SVGPort {
sub_port() {
setSVG(SVG::load(assetPlugin(plugin, "res/Components/sub_port.svg")));
@@ -26,6 +47,7 @@ struct sub_port_black : SVGPort {
}
};
+*/
//////////////////
// Switches
//////////////////
@@ -83,6 +105,9 @@ struct sub_btn : SVGSwitch, ToggleSwitch {
addFrame(SVG::load(assetPlugin(plugin, "res/Components/sub_btn.svg")));
addFrame(SVG::load(assetPlugin(plugin, "res/Components/sub_btn_a.svg")));
}
+ void step() override {
+ setValue(module->params[paramId].value);
+ }
};
//////////////////
@@ -96,76 +121,52 @@ struct LightKnob : Knob {
/** Radii in standard units */
float radius = 19.0;
int enabled = 1;
- LightKnob() {}
+ LightKnob() {smooth = false;}
void draw(NVGcontext *vg) override;
void setEnabled(int val);
void setRadius(int r);
};
-struct sub_knob_small : LightKnob {
- sub_knob_small() {
- setRadius(12.0);
- }
-};
-
-struct sub_knob_med : LightKnob {
- sub_knob_med() {
- setRadius(19.0);
- }
-};
-
-struct sub_knob_large : LightKnob {
- sub_knob_large() {
- setRadius(27.0);
- }
-};
-
-struct sub_knob_small_narrow : sub_knob_small {
- sub_knob_small_narrow() {
- minAngle = -0.75*M_PI;
- maxAngle = 0.75*M_PI;
- }
-};
-
-struct sub_knob_med_narrow : sub_knob_med {
- sub_knob_med_narrow() {
- minAngle = -0.75*M_PI;
- maxAngle = 0.75*M_PI;
+template
+struct TinyKnob : K {
+ TinyKnob() {
+ K::setRadius(9.0f);
}
};
-struct sub_knob_large_narrow : sub_knob_large {
- sub_knob_large_narrow() {
- minAngle = -0.75*M_PI;
- maxAngle = 0.75*M_PI;
+template
+struct SmallKnob : K {
+ SmallKnob() {
+ K::setRadius(12.0f);
}
};
-struct sub_knob_small_snap : sub_knob_small {
- sub_knob_small_snap() {
- snap = true;
- smooth = false;
+template
+struct MedKnob : K {
+ MedKnob() {
+ K::setRadius(19.0f);
}
};
-struct sub_knob_med_snap : sub_knob_med {
- sub_knob_med_snap() {
- snap = true;
- smooth = false;
+template
+struct LargeKnob : K {
+ LargeKnob() {
+ K::setRadius(27.0f);
}
};
-struct sub_knob_large_snap : sub_knob_large {
- sub_knob_large_snap() {
- snap = true;
- smooth = false;
+template
+struct SnapKnob : K {
+ SnapKnob() {
+ K::snap = true;
}
};
-struct sub_knob_med_snap_narrow : sub_knob_med_snap {
- sub_knob_med_snap_narrow() {
- minAngle = -0.75*M_PI;
- maxAngle = 0.75*M_PI;
+template
+struct NarrowKnob : K {
+ NarrowKnob() {
+ K::minAngle = -0.75*M_PI;
+ K::maxAngle = 0.75*M_PI;
}
};
diff --git a/plugins/community/repos/SubmarineFree/src/DS.cpp b/plugins/community/repos/SubmarineFree/src/DS.cpp
index 8acbd6fa..99bfc34d 100644
--- a/plugins/community/repos/SubmarineFree/src/DS.cpp
+++ b/plugins/community/repos/SubmarineFree/src/DS.cpp
@@ -22,7 +22,7 @@ void DS_Module::fromJson(json_t *rootJ) {
void DS_Module::onReset() {
voltage0 = 0.0f;
- voltage1 = 5.0f;
+ voltage1 = 10.0f;
}
float DS_Module::output(int state) {
diff --git a/plugins/community/repos/SubmarineFree/src/EO1.cpp b/plugins/community/repos/SubmarineFree/src/EO1.cpp
new file mode 100644
index 00000000..7c96c645
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/EO1.cpp
@@ -0,0 +1,457 @@
+/* Portions of this code derive from Fundamental/src/Scope.cpp - Copyright 2017 by Andrew Belt */
+#include
+#include "SubmarineFree.hpp"
+#include "dsp/digital.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+#define BUFFER_SIZE 800
+#define PRE_SIZE 100
+
+#define sMAX(a,b) (((a)>(b))?(a):(b))
+
+struct EO_102 : Module {
+ enum ParamIds {
+ PARAM_SCALE_1,
+ PARAM_SCALE_2,
+ PARAM_OFFSET_1,
+ PARAM_OFFSET_2,
+ PARAM_TRIGGER,
+ PARAM_TIME,
+ PARAM_INDEX_1,
+ PARAM_INDEX_2,
+ PARAM_INDEX_3,
+ PARAM_RUNMODE,
+ PARAM_RUN,
+ PARAM_PRE,
+ PARAM_MODE_1,
+ PARAM_MODE_2,
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_1,
+ INPUT_2,
+ INPUT_EXT,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ NUM_OUTPUTS
+ };
+ enum LightIds {
+ LIGHT_TRIGGER,
+ NUM_LIGHTS
+ };
+
+ float buffer[2][BUFFER_SIZE] = {};
+ int bufferIndex = 0;
+ float frameIndex = 0;
+
+ float preBuffer[2][PRE_SIZE] = {};
+ int preBufferIndex = 0;
+ float preFrameIndex = 0;
+ int preCount = 0;
+
+ SchmittTrigger trigger;
+ PulseGenerator triggerLight;
+ float runMode;
+ int traceMode[2];
+ int traceStep;
+
+ EO_102() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override;
+ void startFrame(void);
+};
+
+void EO_102::startFrame() {
+ triggerLight.trigger(0.1f);
+ frameIndex = 0;
+ preCount = (int)(params[PARAM_PRE].value + 0.5f);
+ for (int i = 0; i < 2; i++) {
+ for (int s = 0; s < preCount; s++) {
+ buffer[i][s] = preBuffer[i][(preBufferIndex + (PRE_SIZE * 2) - preCount + s) % PRE_SIZE];
+ }
+ traceMode[i] = (int)(params[PARAM_MODE_1 + i].value + 0.5f);
+ }
+ bufferIndex = preCount;
+ traceStep = 1;
+}
+
+void EO_102::step() {
+ if (runMode > 0.5f) {
+ if (params[PARAM_RUNMODE].value < 0.5f)
+ // runningButtonWidget->setValue(1.0f);
+ engineSetParam(this, PARAM_RUN, 1.0f);
+ }
+ runMode = params[PARAM_RUNMODE].value;
+ // Compute time
+ float deltaTime = powf(2.0f, params[PARAM_TIME].value);
+ int frameCount = (int)ceilf(deltaTime * engineGetSampleRate());
+ lights[LIGHT_TRIGGER].value = triggerLight.process(engineGetSampleTime());
+
+ // Add frame to preBuffer
+ for (int i = 0; i < 2; i++) {
+ if (params[PARAM_MODE_1 + i].value > 0.5f) {
+ if (traceStep) {
+ preBuffer[i][preBufferIndex] = fabs(inputs[INPUT_1 + i].value);
+ }
+ preBuffer[i][preBufferIndex] = sMAX(preBuffer[i][preBufferIndex], (float)fabs(inputs[INPUT_1 + i].value));
+ }
+ }
+ if (++preFrameIndex >= frameCount) {
+ preFrameIndex = 0;
+ for (int i = 0; i < 2; i++) {
+ if (params[PARAM_MODE_1 + i].value < 0.5f) {
+ preBuffer[i][preBufferIndex] = inputs[INPUT_1 + i].value;
+ }
+ }
+ preBufferIndex++;
+ if (preBufferIndex >= PRE_SIZE) {
+ preBufferIndex = 0;
+ }
+ }
+
+ // Add frame to buffer
+ if (bufferIndex < BUFFER_SIZE) {
+ for (int i = 0; i < 2; i++) {
+ if (traceMode[i]) {
+ if (traceStep) {
+ buffer[i][bufferIndex] = fabs(inputs[INPUT_1 + i].value);
+ }
+ buffer[i][bufferIndex] = sMAX(buffer[i][bufferIndex], (float)fabs(inputs[INPUT_1 + i].value));
+ }
+ }
+ traceStep = 0;
+ if (++frameIndex >= frameCount) {
+ frameIndex = 0;
+ for (int i = 0; i < 2; i++) {
+ if (!traceMode[i]) {
+ buffer[i][bufferIndex] = inputs[INPUT_1 + i].value;
+ }
+ }
+ bufferIndex++;
+ traceStep = 1;
+ }
+ }
+
+ int triggerInput = INPUT_1;
+ if (inputs[INPUT_EXT].active)
+ triggerInput = INPUT_EXT;
+
+ // Are we waiting on the next trigger?
+ if (bufferIndex >= BUFFER_SIZE) {
+ // Trigger immediately if nothing connected to trigger input
+ if (!inputs[triggerInput].active) {
+ startFrame();
+ return;
+ }
+
+ // Reset the Schmitt trigger so we don't trigger immediately if the input is high
+ if (frameIndex == 0) {
+ trigger.reset();
+ }
+ frameIndex++;
+
+ float gate = inputs[triggerInput].value;
+ int triggered = trigger.process(rescale(gate, params[PARAM_TRIGGER].value - 0.1f, params[PARAM_TRIGGER].value, 0.0f, 1.0f));
+
+ if (params[PARAM_RUN].value > 0.5f) {
+ if (triggered) {
+ startFrame();
+ if (runMode > 0.5f) // Continuous run mode
+ engineSetParam(this, PARAM_RUN, 0.0f);
+ return;
+ }
+ }
+ }
+}
+
+struct EO_Display : TransparentWidget {
+ EO_102 *module;
+
+ void drawTrace(NVGcontext *vg, float *values, float offset, float scale, NVGcolor col, int mode) {
+ if (!values)
+ return;
+ float scaling = powf(2.0, scale);
+ nvgSave(vg);
+ Rect b = Rect(Vec(0, 0), box.size);
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+ nvgBeginPath(vg);
+ for (int i = 0; i < BUFFER_SIZE; i++) {
+ float x, y;
+ x = (float)i / (BUFFER_SIZE - 1) * b.size.x;
+ y = ((values[i] * scaling + offset ) / 20.0f - 0.8f) * -b.size.y;
+ if (i == 0)
+ nvgMoveTo(vg, x, y);
+ else
+ nvgLineTo(vg, x, y);
+ }
+ if (mode) {
+ nvgLineTo(vg, b.size.x, (offset / 20.0f - 0.8f) * -b.size.y);
+ nvgLineTo(vg, 0, (offset / 20.0f - 0.8f) * -b.size.y);
+ nvgClosePath(vg);
+ nvgFillColor(vg, col);
+ nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
+ nvgFill(vg);
+ }
+ else {
+ nvgStrokeColor(vg, col);
+ nvgLineCap(vg, NVG_ROUND);
+ nvgMiterLimit(vg, 2.0f);
+ nvgStrokeWidth(vg, 1.5f);
+ nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
+ nvgStroke(vg);
+ }
+ nvgResetScissor(vg);
+ nvgRestore(vg);
+ }
+
+ void drawIndex(NVGcontext *vg, float value) {
+ Rect b = Rect(Vec(0, 0), box.size);
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+ value = value * b.size.x;
+
+ nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
+ {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, value, 0);
+ nvgLineTo(vg, value, b.size.y);
+ nvgClosePath(vg);
+ }
+ nvgStroke(vg);
+ nvgResetScissor(vg);
+ }
+ void drawIndexV(NVGcontext *vg, float value) {
+ Rect b = Rect(Vec(0, 0), box.size);
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+ value = (1-value) * b.size.y;
+
+ nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
+ {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, 0, value);
+ nvgLineTo(vg, b.size.x, value);
+ nvgClosePath(vg);
+ }
+ nvgStroke(vg);
+ nvgResetScissor(vg);
+ }
+
+ void drawTrigger(NVGcontext *vg, float value, float offset, float scale) {
+ Rect b = Rect(Vec(0, 0), box.size);
+ float scaling = powf(2.0f, scale);
+ float y = ((value * scaling + offset ) / 20.0f - 0.8f) * -b.size.y;
+ if (y < 0) return;
+ if (y > b.size.y) return;
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+
+ nvgStrokeColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0x40));
+ {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, 0, y);
+ nvgLineTo(vg, b.size.x, y);
+ nvgClosePath(vg);
+ }
+ nvgStroke(vg);
+ nvgResetScissor(vg);
+ }
+
+ void drawPre(NVGcontext *vg, float value) {
+ if (value == 0.0f)
+ return;
+ Rect b = Rect(Vec(0, 0), box.size);
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+ value = value * b.size.x;
+
+ nvgStrokeColor(vg, nvgRGBA(0xff, 0x40, 0x40, 0x80));
+ {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, value, 0);
+ nvgLineTo(vg, value, b.size.y);
+ nvgClosePath(vg);
+ }
+ nvgStroke(vg);
+ nvgResetScissor(vg);
+ }
+
+ void drawMask(NVGcontext *vg, float value) {
+ if (value == 0.0f)
+ return;
+ Rect b = Rect(Vec(0, 0), box.size);
+ nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
+ value = value * b.size.x;
+
+ nvgFillColor(vg, nvgRGBA(0xff, 0x40, 0x40, 0x40));
+ {
+ nvgBeginPath(vg);
+ nvgRect(vg, 0, 0, value, b.size.y);
+ nvgClosePath(vg);
+ }
+ nvgFill(vg);
+ nvgResetScissor(vg);
+ }
+
+ void draw(NVGcontext *vg) override {
+ NVGcolor col = nvgRGBA(0x28, 0xb0, 0xf3, 0xc0);
+ for (int i = 0; i < 2; i++) {
+ if (module->inputs[EO_102::INPUT_1 + i].active) {
+ drawTrace(vg, module->buffer[i], module->params[EO_102::PARAM_OFFSET_1 + i].value, module->params[EO_102::PARAM_SCALE_1 + i].value, col, module->traceMode[i]);
+ }
+ col = nvgRGBA(0xed, 0x2c, 0x24, 0xc0);
+ }
+ drawIndex(vg, clamp(module->params[EO_102::PARAM_INDEX_1].value, 0.0f, 1.0f));
+ drawIndex(vg, clamp(module->params[EO_102::PARAM_INDEX_2].value, 0.0f, 1.0f));
+ drawIndexV(vg, clamp(module->params[EO_102::PARAM_INDEX_3].value, 0.0f, 1.0f));
+ if (module->inputs[EO_102::INPUT_EXT].active)
+ drawTrigger(vg, module->params[EO_102::PARAM_TRIGGER].value, 0.0f, 1.0f);
+ else
+ drawTrigger(vg, module->params[EO_102::PARAM_TRIGGER].value, module->params[EO_102::PARAM_OFFSET_1].value, module->params[EO_102::PARAM_SCALE_1].value);
+ drawMask(vg, clamp(module->params[EO_102::PARAM_PRE].value, 0.0f, 1.0f * PRE_SIZE) / BUFFER_SIZE);
+ drawPre(vg, 1.0f * module->preCount / BUFFER_SIZE);
+ }
+};
+
+struct EO_Measure : TransparentWidget {
+ std::shared_ptr font;
+ EO_102 *module;
+ char measureText[41];
+ NVGcolor col;
+
+ EO_Measure() {
+ font = Font::load(assetGlobal( "res/fonts/DejaVuSans.ttf"));
+ }
+
+ virtual void updateText() {
+ }
+
+ void draw(NVGcontext *vg) override {
+ updateText();
+ nvgFontSize(vg, 14);
+ nvgFontFaceId(vg, font->handle);
+ nvgFillColor(vg, col);
+ nvgTextAlign(vg, NVG_ALIGN_CENTER);
+ nvgText(vg, box.size.x / 2, 12, measureText, NULL);
+ }
+};
+
+struct EO_Measure_Horz : EO_Measure {
+ void updateText() override {
+ float deltaTime = powf(2.0f, module->params[EO_102::PARAM_TIME].value);
+ int frameCount = (int)ceilf(deltaTime * engineGetSampleRate());
+ frameCount *= BUFFER_SIZE;
+ float width = (float)frameCount * fabs(module->params[EO_102::PARAM_INDEX_1].value - module->params[EO_102::PARAM_INDEX_2].value) / engineGetSampleRate();
+
+ if (width < 0.00000995f)
+ sprintf(measureText, "%4.3f\xc2\xb5s", width * 1000000.0f);
+ else if (width < 0.0000995f)
+ sprintf(measureText, "%4.2f\xc2\xb5s", width * 1000000.0f);
+ else if (width < 0.000995f)
+ sprintf(measureText, "%4.1f\xc2\xb5s", width * 1000000.0f);
+ else if (width < 0.00995f)
+ sprintf(measureText, "%4.3fms", width * 1000.0f);
+ else if (width < 0.0995f)
+ sprintf(measureText, "%4.2fms", width * 1000.0f);
+ else if (width < 0.995f)
+ sprintf(measureText, "%4.1fms", width * 1000.0f);
+ else if (width < 9.95f)
+ sprintf(measureText, "%4.3fs", width);
+ else if (width < 99.5f)
+ sprintf(measureText, "%4.2fs", width);
+ else
+ sprintf(measureText, "%4.1fs", width);
+ }
+};
+
+struct EO_Measure_Vert : EO_Measure {
+ int index = 0;
+ void updateText() override {
+ float height = ((module->params[EO_102::PARAM_INDEX_3].value - 0.2f) * 20.0f - module->params[EO_102::PARAM_OFFSET_1 + index].value) / powf(2, module->params[EO_102::PARAM_SCALE_1 + index].value);
+
+ float ah = fabs(height);
+ if (ah < 0.00000995f)
+ sprintf(measureText, "%4.3f\xc2\xb5V", height * 1000000.0f);
+ else if (ah < 0.0000995f)
+ sprintf(measureText, "%4.2f\xc2\xb5V", height * 1000000.0f);
+ else if (ah < 0.000995f)
+ sprintf(measureText, "%4.1f\xc2\xb5V", height * 1000000.0f);
+ else if (ah < 0.00995f)
+ sprintf(measureText, "%4.3fmV", height * 1000.0f);
+ else if (ah < 0.0995f)
+ sprintf(measureText, "%4.2fmV", height * 1000.0f);
+ else if (ah < 0.995f)
+ sprintf(measureText, "%4.1fmV", height * 1000.0f);
+ else if (ah < 9.95f)
+ sprintf(measureText, "%4.3fV", height);
+ else if (ah < 99.5f)
+ sprintf(measureText, "%4.2fV", height);
+ else
+ sprintf(measureText, "%4.1fV", height);
+ }
+};
+
+struct EO102 : ModuleWidget {
+ EO102(EO_102 *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/EO-102.svg")));
+
+ {
+ EO_Display * display = new EO_Display();
+ display->module = module;
+ display->box.pos = Vec(2.5, 14);
+ display->box.size = Vec(box.size.x - 5, 236);
+ addChild(display);
+ }
+ {
+ EO_Measure_Horz * display = new EO_Measure_Horz();
+ display->module = module;
+ display->box.pos = Vec(284, 272);
+ display->box.size = Vec(54, 16);
+ display->col = nvgRGBA(0xff, 0xff, 0xff, 0xff);
+ addChild(display);
+ }
+ {
+ EO_Measure_Vert * display = new EO_Measure_Vert();
+ display->module = module;
+ display->box.pos = Vec(341, 254);
+ display->box.size = Vec(62, 16);
+ display->index = 0;
+ display->col = nvgRGBA(0x28, 0xb0, 0xf3, 0xff);
+ addChild(display);
+ }
+ {
+ EO_Measure_Vert * display = new EO_Measure_Vert();
+ display->module = module;
+ display->box.pos = Vec(341, 272);
+ display->box.size = Vec(62, 16);
+ display->index = 1;
+ display->col = nvgRGBA(0xed, 0x2c, 0x24, 0xff);
+ addChild(display);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ addInput(createInputCentered(Vec(16.5 + 75 * i, 326.5), module, EO_102::INPUT_1 + i));
+ addParam(createParamCentered(Vec(16.5 + 75 * i, 280), module, EO_102::PARAM_MODE_1 + i, 0.0f, 1.0f, 0.0f));
+ addParam(createParamCentered>(Vec(50 + 75 * i, 320), module, EO_102::PARAM_OFFSET_1 + i, -10.0f, 10.0f, 0.0f));
+ addParam(createParamCentered>>(Vec(50 + 75 * i, 270), module, EO_102::PARAM_SCALE_1 + i, -5.0f, 5.0f, 0.0f));
+ }
+ addParam(createParamCentered>(Vec(172.5, 320), module, EO_102::PARAM_TIME, -6.0f, -16.0f, -14.0f));
+ addParam(createParamCentered>>(Vec(172.5, 270), module, EO_102::PARAM_PRE, 0.0f, 1.0f * PRE_SIZE, 0.0f));
+
+ addInput(createInputCentered(Vec(211.5, 326.5), module, EO_102::INPUT_EXT));
+ addParam(createParamCentered>(Vec(245, 320), module, EO_102::PARAM_TRIGGER, -10.0f, 10.0f, 0.0f));
+ addChild(createLightCentered>(Vec(226, 333), module, EO_102::LIGHT_TRIGGER));
+ addParam(createParamCentered(Vec(211.5, 280), module, EO_102::PARAM_RUNMODE, 0.0f, 1.0f, 0.0f));
+ addParam(createParamCentered(Vec(245, 280), module, EO_102::PARAM_RUN, 0.0f, 1.0f, 1.0f));
+
+ addParam(createParamCentered>(Vec(290, 320), module, EO_102::PARAM_INDEX_1, 0.0f, 1.0f, 0.0f));
+ addParam(createParamCentered>(Vec(332, 320), module, EO_102::PARAM_INDEX_2, 0.0f, 1.0f, 1.0f));
+ addParam(createParamCentered>(Vec(376, 320), module, EO_102::PARAM_INDEX_3, 0.0f, 1.0f, 0.2f));
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, EO102) {
+ Model *modelEO102 = Model::create("Submarine (Free)", "EO-102", "EO-102 Envelope Oscilloscope", VISUAL_TAG);
+ return modelEO102;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/FF-110.cpp b/plugins/community/repos/SubmarineFree/src/FF-110.cpp
deleted file mode 100644
index 82e47134..00000000
--- a/plugins/community/repos/SubmarineFree/src/FF-110.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct FF_110 : DS_Module {
- static const int deviceCount = 10;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- int state[deviceCount] = {0,0,0,0,0,0,0,0,0,0};
- DS_Schmitt schmittTrigger[deviceCount];
-
- FF_110() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void FF_110::step() {
- if (inputs[INPUT].active) {
- if (schmittTrigger[0].redge(this, inputs[INPUT].value))
- state[0] = !state[0];
- }
- outputs[OUTPUT_1].value = state[0]?voltage1:voltage0;
- for (int i = 1; i < deviceCount; i++) {
- if (schmittTrigger[i].redge(this, state[i-1]?voltage0:voltage1))
- state[i] = !state[i];
- outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
- }
-}
-
-struct FF110 : ModuleWidget {
- FF110(FF_110 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/FF-110.svg")));
-
- addInput(Port::create(Vec(2.5,19), Port::INPUT, module, FF_110::INPUT));
-
- for (int i = 0; i < FF_110::deviceCount; i++) {
- int offset = 29 * i;
-
- addOutput(Port::create(Vec(2.5,77 + offset), Port::OUTPUT, module, FF_110::OUTPUT_1 + i));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF110) {
- Model *modelFF110 = Model::create("SubmarineFree", "FF-110", "FF-110 10-Stage Flip-Flop Counter", LOGIC_TAG, MULTIPLE_TAG);
- return modelFF110;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/FF-120.cpp b/plugins/community/repos/SubmarineFree/src/FF-120.cpp
deleted file mode 100644
index 82b3d7a9..00000000
--- a/plugins/community/repos/SubmarineFree/src/FF-120.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct FF_120 : DS_Module {
- static const int deviceCount = 20;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- OUTPUT_11,
- OUTPUT_12,
- OUTPUT_13,
- OUTPUT_14,
- OUTPUT_15,
- OUTPUT_16,
- OUTPUT_17,
- OUTPUT_18,
- OUTPUT_19,
- OUTPUT_20,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- int state[deviceCount] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- DS_Schmitt schmittTrigger[deviceCount];
-
- FF_120() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void FF_120::step() {
- if (inputs[INPUT].active) {
- if (schmittTrigger[0].redge(this, inputs[INPUT].value))
- state[0] = !state[0];
- }
- outputs[OUTPUT_1].value = state[0]?voltage1:voltage0;
- for (int i = 1; i < deviceCount; i++) {
- if (schmittTrigger[i].redge(this, state[i-1]?voltage0:voltage1))
- state[i] = !state[i];
- outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
- }
-}
-
-struct FF120 : ModuleWidget {
- FF120(FF_120 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/FF-120.svg")));
-
- addInput(Port::create(Vec(17.5,19), Port::INPUT, module, FF_120::INPUT));
-
- for (int i = 0; i < FF_120::deviceCount; i+=2) {
- int offset = 15 * i;
-
- addOutput(Port::create(Vec(4,53 + offset), Port::OUTPUT, module, FF_120::OUTPUT_1 + i));
- addOutput(Port::create(Vec(31,68 + offset), Port::OUTPUT, module, FF_120::OUTPUT_1 + i + 1));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF120) {
- Model *modelFF120 = Model::create("SubmarineFree", "FF-120", "FF-120 20-Stage Flip-Flop Counter", LOGIC_TAG, MULTIPLE_TAG);
- return modelFF120;
-}
-
diff --git a/plugins/community/repos/SubmarineFree/src/FF-212.cpp b/plugins/community/repos/SubmarineFree/src/FF-212.cpp
deleted file mode 100644
index 4dd0a210..00000000
--- a/plugins/community/repos/SubmarineFree/src/FF-212.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct FF_212 : DS_Module {
- static const int deviceCount = 12;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_1,
- INPUT_2,
- INPUT_3,
- INPUT_4,
- INPUT_5,
- INPUT_6,
- INPUT_7,
- INPUT_8,
- INPUT_9,
- INPUT_10,
- INPUT_11,
- INPUT_12,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- OUTPUT_11,
- OUTPUT_12,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- int state[deviceCount] = {0,0,0,0,0,0,0,0,0,0,0,0};
- DS_Schmitt schmittTrigger[deviceCount];
-
- FF_212() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void FF_212::step() {
- for (int i = 0; i < deviceCount; i++) {
- if (inputs[INPUT_1 + i].active) {
- if (schmittTrigger[i].redge(this, inputs[INPUT_1 + i].value))
- state[i] = !state[i];
- }
- else {
- if (i) {
- if (schmittTrigger[i].redge(this, state[i-1]?voltage0:voltage1))
- state[i] = !state[i];
- }
- }
- outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
- }
-}
-
-struct FF212 : ModuleWidget {
- FF212(FF_212 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/FF-212.svg")));
-
- for (int i = 0; i < FF_212::deviceCount; i++) {
- int offset = 29 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, FF_212::INPUT_1 + i));
-
- addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, FF_212::OUTPUT_1 + i));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF212) {
- Model *modelFF212 = Model::create("SubmarineFree", "FF-212", "FF-212 Edge Triggered Flip-Flops", LOGIC_TAG, MULTIPLE_TAG);
- return modelFF212;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/FF1.cpp b/plugins/community/repos/SubmarineFree/src/FF1.cpp
new file mode 100644
index 00000000..3aa92bce
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/FF1.cpp
@@ -0,0 +1,134 @@
+#include
+#include
+#include "DS.hpp"
+#include
+#include
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct FF_1 : DS_Module {
+ int doResetFlag = 0;
+ int doRandomFlag = 0;
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = deviceCount
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ int state[deviceCount] = {};
+ DS_Schmitt schmittTrigger[deviceCount];
+
+ FF_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+
+ void step() override {
+ if (doResetFlag) doReset();
+ if (doRandomFlag) doRandomize();
+ if (inputs[INPUT].active) {
+ if (schmittTrigger[0].redge(this, inputs[INPUT].value))
+ state[0] = !state[0];
+ }
+ outputs[OUTPUT_1].value = state[0]?voltage1:voltage0;
+ for (int i = 1; i < deviceCount; i++) {
+ if (schmittTrigger[i].fedge(this, state[i-1]?voltage1:voltage0))
+ state[i] = !state[i];
+ outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
+ }
+ }
+ void doRandomize() {
+ doRandomFlag = 0;
+ std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+ std::uniform_int_distribution distribution(0,1);
+ state[0] = distribution(generator);
+ outputs[OUTPUT_1].value = state[0]?voltage1:voltage0;
+ for (int i = 1; i < deviceCount; i++) {
+ state[i] = distribution(generator);
+ schmittTrigger[i].set(state[i-1]);
+ outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
+ }
+ }
+ void doReset() {
+ doResetFlag = 0;
+ for (int i = 0; i < deviceCount; i++) {
+ state[i] = 0;
+ if (i) schmittTrigger[i].reset();
+ outputs[OUTPUT_1 + i].value = voltage0;
+ }
+ }
+ void onRandomize() override {
+ if (rack::global->gPaused) {
+ doRandomize();
+ }
+ else {
+ doResetFlag = 0;
+ doRandomFlag = 1;
+ }
+ }
+ void onReset() override {
+ if (rack::global->gPaused) {
+ doReset();
+ }
+ else {
+ doRandomFlag = 0;
+ doResetFlag = 1;
+ }
+ }
+};
+
+struct FF110 : ModuleWidget {
+ FF110(FF_1<10> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/FF-110.svg")));
+
+ addInput(Port::create(Vec(2.5,19), Port::INPUT, module, FF_1<10>::INPUT));
+
+ for (int i = 0; i < 10; i++) {
+ int offset = 29 * i;
+
+ addOutput(Port::create(Vec(2.5,77 + offset), Port::OUTPUT, module, FF_1<10>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct FF120 : ModuleWidget {
+ FF120(FF_1<20> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/FF-120.svg")));
+
+ addInput(Port::create(Vec(17.5,19), Port::INPUT, module, FF_1<20>::INPUT));
+
+ for (int i = 0; i < 20; i+=2) {
+ int offset = 15 * i;
+
+ addOutput(Port::create(Vec(4,53 + offset), Port::OUTPUT, module, FF_1<20>::OUTPUT_1 + i));
+ addOutput(Port::create(Vec(31,68 + offset), Port::OUTPUT, module, FF_1<20>::OUTPUT_1 + i + 1));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF110) {
+ Model *modelFF110 = Model::create, FF110>("Submarine (Free)", "FF-110", "FF-110 10-Stage Flip-Flop Counter", LOGIC_TAG, MULTIPLE_TAG);
+ return modelFF110;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF120) {
+ Model *modelFF120 = Model::create, FF120>("Submarine (Free)", "FF-120", "FF-120 20-Stage Flip-Flop Counter", LOGIC_TAG, MULTIPLE_TAG);
+ return modelFF120;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/FF2.cpp b/plugins/community/repos/SubmarineFree/src/FF2.cpp
new file mode 100644
index 00000000..6975868e
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/FF2.cpp
@@ -0,0 +1,131 @@
+#include
+#include
+#include "DS.hpp"
+#include
+#include
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct FF_2 : DS_Module {
+ int doResetFlag = 0;
+ int doRandomFlag = 0;
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_1,
+ NUM_INPUTS = x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ int state[x] = {};
+ DS_Schmitt schmittTrigger[x];
+
+ FF_2() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ if (doResetFlag) doReset();
+ if (doRandomFlag) doRandomize();
+ for (int i = 0; i < x; i++) {
+ if (inputs[INPUT_1 + i].active) {
+ if (schmittTrigger[i].redge(this, inputs[INPUT_1 + i].value))
+ state[i] = !state[i];
+ }
+ else {
+ if (i) {
+ if (schmittTrigger[i].fedge(this, state[i-1]?voltage1:voltage0))
+ state[i] = !state[i];
+ }
+ }
+ outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
+ }
+ }
+ void doRandomize() {
+ doRandomFlag = 0;
+ std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+ std::uniform_int_distribution distribution(0,1);
+ for (int i = 0; i < x; i++) {
+ state[i] = distribution(generator);
+ if (i) if (!inputs[INPUT_1 + i].active) schmittTrigger[i].set(state[i-1]);
+ outputs[OUTPUT_1 + i].value = state[i]?voltage1:voltage0;
+ }
+ }
+ void doReset() {
+ doResetFlag = 0;
+ for (int i = 0; i < x; i++) {
+ state[i] = 0;
+ if (!inputs[INPUT_1 + i].active) schmittTrigger[i].reset();
+ outputs[OUTPUT_1 + i].value = voltage0;
+ }
+ }
+ void onRandomize() override {
+ if (rack::global->gPaused) {
+ doRandomize();
+ }
+ else {
+ doResetFlag = 0;
+ doRandomFlag = 1;
+ }
+ }
+ void onReset() override {
+ if (rack::global->gPaused) {
+ doReset();
+ }
+ else {
+ doRandomFlag = 0;
+ doResetFlag = 1;
+ }
+ }
+};
+
+struct FF206 : ModuleWidget {
+ FF206(FF_2<6> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/FF-206.svg")));
+
+ for (int i = 0; i < 6; i++) {
+ int offset = 58 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, FF_2<6>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(2.5,47 + offset), Port::OUTPUT, module, FF_2<6>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct FF212 : ModuleWidget {
+ FF212(FF_2<12> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/FF-212.svg")));
+
+ for (int i = 0; i < 12; i++) {
+ int offset = 29 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, FF_2<12>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, FF_2<12>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF206) {
+ Model *modelFF206 = Model::create, FF206>("Submarine (Free)", "FF-206", "FF-206 Edge Triggered Flip-Flops", LOGIC_TAG, MULTIPLE_TAG);
+ return modelFF206;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, FF212) {
+ Model *modelFF212 = Model::create, FF212>("Submarine (Free)", "FF-212", "FF-212 Edge Triggered Flip-Flops", LOGIC_TAG, MULTIPLE_TAG);
+ return modelFF212;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/LA-108.cpp b/plugins/community/repos/SubmarineFree/src/LA1.cpp
similarity index 87%
rename from plugins/community/repos/SubmarineFree/src/LA-108.cpp
rename to plugins/community/repos/SubmarineFree/src/LA1.cpp
index d500cd12..66c1cc76 100644
--- a/plugins/community/repos/SubmarineFree/src/LA-108.cpp
+++ b/plugins/community/repos/SubmarineFree/src/LA1.cpp
@@ -1,10 +1,11 @@
+/* Portions of this code derive from Fundamental/src/Scope.cpp - Copyright © 2016 by Andrew Belt */
#include
#include "DS.hpp"
-#define BUFFER_SIZE 512
-
namespace rack_plugin_SubmarineFree {
+#define BUFFER_SIZE 512
+
struct LA_108 : DS_Module {
enum ParamIds {
PARAM_TRIGGER,
@@ -55,7 +56,6 @@ struct LA_108 : DS_Module {
int preCount = 0;
DS_Schmitt trigger;
- sub_btn *resetButtonWidget;
LA_108() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
void step() override;
@@ -126,7 +126,7 @@ void LA_108::step() {
int triggered = trigger.edge(this, gate, edge);
if (params[PARAM_RUN].value < 0.5f) { // Continuous run mode
- resetButtonWidget->setValue(0.0f);
+ engineSetParam(this, PARAM_RESET, 0.0f);
// Reset if triggered
float holdTime = 0.1f;
if (triggered) {
@@ -144,7 +144,7 @@ void LA_108::step() {
if (params[PARAM_RESET].value > 0.5f) {
if (triggered) {
startFrame();
- resetButtonWidget->setValue(0.0f);
+ engineSetParam(this, PARAM_RESET, 0.0f);
return;
}
}
@@ -309,22 +309,21 @@ struct LA108 : ModuleWidget {
}
for (int i = 0; i < 8; i++) {
- addInput(Port::create(Vec(4, 20 + 35 * i), Port::INPUT, module, LA_108::INPUT_1 + i));
+ addInput(Port::create(Vec(4, 20 + 35 * i), Port::INPUT, module, LA_108::INPUT_1 + i));
addChild(ModuleLightWidget::create>(Vec(30, 22 + 35 * i), module, LA_108::LIGHT_1 + i));
}
- addInput(Port::create(Vec(4, 310), Port::INPUT, module, LA_108::INPUT_EXT));
+ addInput(Port::create(Vec(4, 310), Port::INPUT, module, LA_108::INPUT_EXT));
addChild(ModuleLightWidget::create>(Vec(30, 312), module, LA_108::LIGHT_EXT));
- addParam(ParamWidget::create(Vec(39, 301), module, LA_108::PARAM_TRIGGER, 0.0f, 8.0f, 0.0f));
+ addParam(ParamWidget::create>>(Vec(39, 301), module, LA_108::PARAM_TRIGGER, 0.0f, 8.0f, 0.0f));
addParam(ParamWidget::create(Vec(82, 308), module, LA_108::PARAM_EDGE, 0.0f, 1.0f, 0.0f));
addParam(ParamWidget::create(Vec(108, 308), module, LA_108::PARAM_RUN, 0.0f, 1.0f, 0.0f));
- module->resetButtonWidget = ParamWidget::create(Vec(151, 312), module, LA_108::PARAM_RESET, 0.0f, 1.0f, 0.0f);
- addParam(module->resetButtonWidget);
- addParam(ParamWidget::create(Vec(171, 301), module, LA_108::PARAM_TIME, -6.0f, -16.0f, -14.0f));
- addParam(ParamWidget::create(Vec(214, 315), module, LA_108::PARAM_INDEX_1, 0.0f, 1.0f, 0.0f));
- addParam(ParamWidget::create(Vec(242, 315), module, LA_108::PARAM_INDEX_2, 0.0f, 1.0f, 1.0f));
- addParam(ParamWidget::create(Vec(271, 315), module, LA_108::PARAM_PRE, 0.0f, 32.0f, 0.0f));
+ addParam(ParamWidget::create(Vec(151, 312), module, LA_108::PARAM_RESET, 0.0f, 1.0f, 0.0f));
+ addParam(ParamWidget::create>(Vec(171, 301), module, LA_108::PARAM_TIME, -6.0f, -16.0f, -14.0f));
+ addParam(ParamWidget::create>(Vec(214, 315), module, LA_108::PARAM_INDEX_1, 0.0f, 1.0f, 0.0f));
+ addParam(ParamWidget::create>(Vec(242, 315), module, LA_108::PARAM_INDEX_2, 0.0f, 1.0f, 1.0f));
+ addParam(ParamWidget::create>>(Vec(271, 315), module, LA_108::PARAM_PRE, 0.0f, 32.0f, 0.0f));
}
void appendContextMenu(Menu *menu) override {
((DS_Module *)module)->appendContextMenu(menu);
@@ -336,6 +335,6 @@ struct LA108 : ModuleWidget {
using namespace rack_plugin_SubmarineFree;
RACK_PLUGIN_MODEL_INIT(SubmarineFree, LA108) {
- Model *modelLA108 = Model::create("SubmarineFree", "LA-108", "LA-108 Logic Analyser", LOGIC_TAG, VISUAL_TAG);
+ Model *modelLA108 = Model::create("Submarine (Free)", "LA-108", "LA-108 Logic Analyser", LOGIC_TAG, VISUAL_TAG);
return modelLA108;
}
diff --git a/plugins/community/repos/SubmarineFree/src/LD-106.cpp b/plugins/community/repos/SubmarineFree/src/LD-106.cpp
deleted file mode 100644
index 48f228ce..00000000
--- a/plugins/community/repos/SubmarineFree/src/LD-106.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct LD_106 : DS_Module {
- static const int deviceCount = 6;
- enum ParamIds {
- PARAM_CUTOFF_1,
- PARAM_CUTOFF_2,
- PARAM_CUTOFF_3,
- PARAM_CUTOFF_4,
- PARAM_CUTOFF_5,
- PARAM_CUTOFF_6,
- PARAM_WIDTH_1,
- PARAM_WIDTH_2,
- PARAM_WIDTH_3,
- PARAM_WIDTH_4,
- PARAM_WIDTH_5,
- PARAM_WIDTH_6,
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_1,
- INPUT_2,
- INPUT_3,
- INPUT_4,
- INPUT_5,
- INPUT_6,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- DS_Schmitt schmittState[deviceCount];
-
- LD_106() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void LD_106::step() {
- for (int i = 0; i < deviceCount; i++) {
- outputs[OUTPUT_1 + i].value = output(schmittState[i].state(params[PARAM_CUTOFF_1 + i].value - params[PARAM_WIDTH_1 + i].value, params[PARAM_CUTOFF_1 + i].value + params[PARAM_WIDTH_1 + i].value, inputs[INPUT_1 + i].value));
- }
-}
-
-struct LD106 : ModuleWidget {
- ParamWidget *cutoffWidgets[LD_106::deviceCount];
- ParamWidget *widthWidgets[LD_106::deviceCount];
- LD106(LD_106 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/LD-106.svg")));
-
- for (int i = 0; i < LD_106::deviceCount; i++) {
- int offset = 58 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, LD_106::INPUT_1 + i));
-
- addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, LD_106::OUTPUT_1 + i));
-
- cutoffWidgets[i] = ParamWidget::create(Vec(4, 47 + offset), module, LD_106::PARAM_CUTOFF_1 + i, -10.0f, 10.0f, 5.0f);
- addParam(cutoffWidgets[i]);
- widthWidgets[i] = ParamWidget::create(Vec(62, 47 + offset), module, LD_106::PARAM_WIDTH_1 + i, 0.0f, 5.0f, 1.0f);
- addParam(widthWidgets[i]);
- }
- }
- void appendContextMenu(Menu *menu) override;
-};
-
-struct LDMenuItem: MenuItem {
- LD106 *ld106;
- float cutoff;
- float width;
- void onAction(EventAction &e) override {
- for (int i = 0; i < LD_106::deviceCount; i++) {
- ld106->cutoffWidgets[i]->setValue(cutoff);
- ld106->widthWidgets[i]->setValue(width);
- }
- }
-};
-
-void LD106::appendContextMenu(Menu *menu) {
- menu->addChild(MenuEntry::create());
- LD106 *ld106 = dynamic_cast(this);
- assert(ld106);
- LDMenuItem *menuItem = MenuItem::create("Cutoff 5V");
- menuItem->ld106 = ld106;
- menuItem->cutoff = 5.0f;
- menuItem->width = 1.0f;
- menu->addChild(menuItem);
- menuItem = MenuItem::create("Cutoff 0V");
- menuItem->ld106 = ld106;
- menuItem->cutoff = 0.0f;
- menuItem->width = 0.0f;
- menu->addChild(menuItem);
- menuItem = MenuItem::create("Cutoff 2.5V");
- menuItem->ld106 = ld106;
- menuItem->cutoff = 2.5f;
- menuItem->width = 0.5f;
- menu->addChild(menuItem);
- menuItem = MenuItem::create("TTL Levels");
- menuItem->ld106 = ld106;
- menuItem->cutoff = 1.4f;
- menuItem->width = 0.6f;
- menu->addChild(menuItem);
- ((LD_106 *)module)->appendContextMenu(menu);
-}
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, LD106) {
- Model *modelLD106 = Model::create("SubmarineFree", "LD-106", "LD-106 Schmitt Trigger Line Drivers", LOGIC_TAG, MULTIPLE_TAG);
- return modelLD106;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/LD1.cpp b/plugins/community/repos/SubmarineFree/src/LD1.cpp
new file mode 100644
index 00000000..86fe46e5
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/LD1.cpp
@@ -0,0 +1,168 @@
+#include "DS.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct LD_1 : DS_Module {
+ enum ParamIds {
+ PARAM_CUTOFF_1,
+ PARAM_WIDTH_1 = x,
+ NUM_PARAMS = x + x
+ };
+ enum InputIds {
+ INPUT_1,
+ NUM_INPUTS = x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ DS_Schmitt schmittState[x];
+
+ LD_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ for (int i = 0; i < x; i++) {
+ outputs[OUTPUT_1 + i].value = output(schmittState[i].state(params[PARAM_CUTOFF_1 + i].value - params[PARAM_WIDTH_1 + i].value, params[PARAM_CUTOFF_1 + i].value + params[PARAM_WIDTH_1 + i].value, inputs[INPUT_1 + i].value));
+ }
+ }
+};
+
+struct LD103 : ModuleWidget {
+ static const int deviceCount = 3;
+ ParamWidget *cutoffWidgets[deviceCount];
+ ParamWidget *widthWidgets[deviceCount];
+ LD103(LD_1 *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/LD-103.svg")));
+
+ for (int i = 0; i < deviceCount; i++) {
+ int offset = 116 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, LD_1<3>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(2.5,103 + offset), Port::OUTPUT, module, LD_1<3>::OUTPUT_1 + i));
+
+ cutoffWidgets[i] = ParamWidget::create>(Vec(6, 48.5 + offset), module, LD_1<3>::PARAM_CUTOFF_1 + i, -10.0f, 10.0f, 5.0f);
+ addParam(cutoffWidgets[i]);
+ widthWidgets[i] = ParamWidget::create>(Vec(6, 80.5 + offset), module, LD_1<3>::PARAM_WIDTH_1 + i, 0.0f, 5.0f, 1.0f);
+ addParam(widthWidgets[i]);
+ }
+ }
+ void appendContextMenu(Menu *menu) override;
+};
+
+struct LDMenuItem3: MenuItem {
+ LD103 *ld103;
+ float cutoff;
+ float width;
+ void onAction(EventAction &e) override {
+ for (int i = 0; i < LD103::deviceCount; i++) {
+ ld103->cutoffWidgets[i]->setValue(cutoff);
+ ld103->widthWidgets[i]->setValue(width);
+ }
+ }
+};
+
+void LD103::appendContextMenu(Menu *menu) {
+ menu->addChild(MenuEntry::create());
+ LD103 *ld103 = dynamic_cast(this);
+ assert(ld103);
+ LDMenuItem3 *menuItem = MenuItem::create("Cutoff 5V");
+ menuItem->ld103 = ld103;
+ menuItem->cutoff = 5.0f;
+ menuItem->width = 1.0f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("Cutoff 0V");
+ menuItem->ld103 = ld103;
+ menuItem->cutoff = 0.0f;
+ menuItem->width = 0.0f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("Cutoff 2.5V");
+ menuItem->ld103 = ld103;
+ menuItem->cutoff = 2.5f;
+ menuItem->width = 0.5f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("TTL Levels");
+ menuItem->ld103 = ld103;
+ menuItem->cutoff = 1.4f;
+ menuItem->width = 0.6f;
+ menu->addChild(menuItem);
+ ((DS_Module *)module)->appendContextMenu(menu);
+}
+
+struct LD106 : ModuleWidget {
+ static const int deviceCount = 6;
+ ParamWidget *cutoffWidgets[deviceCount];
+ ParamWidget *widthWidgets[deviceCount];
+ LD106(LD_1 *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/LD-106.svg")));
+
+ for (int i = 0; i < deviceCount; i++) {
+ int offset = 58 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, LD_1<6>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, LD_1<6>::OUTPUT_1 + i));
+
+ cutoffWidgets[i] = ParamWidget::create>(Vec(4, 47 + offset), module, LD_1<6>::PARAM_CUTOFF_1 + i, -10.0f, 10.0f, 5.0f);
+ addParam(cutoffWidgets[i]);
+ widthWidgets[i] = ParamWidget::create>(Vec(62, 47 + offset), module, LD_1<6>::PARAM_WIDTH_1 + i, 0.0f, 5.0f, 1.0f);
+ addParam(widthWidgets[i]);
+ }
+ }
+ void appendContextMenu(Menu *menu) override;
+};
+
+struct LDMenuItem: MenuItem {
+ LD106 *ld106;
+ float cutoff;
+ float width;
+ void onAction(EventAction &e) override {
+ for (int i = 0; i < LD106::deviceCount; i++) {
+ ld106->cutoffWidgets[i]->setValue(cutoff);
+ ld106->widthWidgets[i]->setValue(width);
+ }
+ }
+};
+
+void LD106::appendContextMenu(Menu *menu) {
+ menu->addChild(MenuEntry::create());
+ LD106 *ld106 = dynamic_cast(this);
+ assert(ld106);
+ LDMenuItem *menuItem = MenuItem::create("Cutoff 5V");
+ menuItem->ld106 = ld106;
+ menuItem->cutoff = 5.0f;
+ menuItem->width = 1.0f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("Cutoff 0V");
+ menuItem->ld106 = ld106;
+ menuItem->cutoff = 0.0f;
+ menuItem->width = 0.0f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("Cutoff 2.5V");
+ menuItem->ld106 = ld106;
+ menuItem->cutoff = 2.5f;
+ menuItem->width = 0.5f;
+ menu->addChild(menuItem);
+ menuItem = MenuItem::create("TTL Levels");
+ menuItem->ld106 = ld106;
+ menuItem->cutoff = 1.4f;
+ menuItem->width = 0.6f;
+ menu->addChild(menuItem);
+ ((DS_Module *)module)->appendContextMenu(menu);
+}
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, LD103) {
+ Model *modelLD103 = Model::create, LD103>("Submarine (Free)", "LD-103", "LD-103 Schmitt Trigger Line Drivers", LOGIC_TAG, MULTIPLE_TAG);
+ return modelLD103;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, LD106) {
+ Model *modelLD106 = Model::create, LD106>("Submarine (Free)", "LD-106", "LD-106 Schmitt Trigger Line Drivers", LOGIC_TAG, MULTIPLE_TAG);
+ return modelLD106;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/NG-112.cpp b/plugins/community/repos/SubmarineFree/src/NG-112.cpp
deleted file mode 100644
index 9e094236..00000000
--- a/plugins/community/repos/SubmarineFree/src/NG-112.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct NG_112 : DS_Module {
- static const int deviceCount = 12;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_1,
- INPUT_2,
- INPUT_3,
- INPUT_4,
- INPUT_5,
- INPUT_6,
- INPUT_7,
- INPUT_8,
- INPUT_9,
- INPUT_10,
- INPUT_11,
- INPUT_12,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- OUTPUT_11,
- OUTPUT_12,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- NG_112() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void NG_112::step() {
- for (int i = 0; i < deviceCount; i++) {
- outputs[OUTPUT_1 + i].value = (inputs[INPUT_1 + i].value < midpoint())?voltage1:voltage0;
- }
-}
-
-struct NG112 : ModuleWidget {
- NG112(NG_112 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/NG-112.svg")));
-
- for (int i = 0; i < NG_112::deviceCount; i++) {
- int offset = 29 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, NG_112::INPUT_1 + i));
-
- addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, NG_112::OUTPUT_1 + i));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, NG112) {
- Model *modelNG112 = Model::create("SubmarineFree", "NG-112", "NG-112 NOT Gates", LOGIC_TAG, MULTIPLE_TAG);
- return modelNG112;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/NG1.cpp b/plugins/community/repos/SubmarineFree/src/NG1.cpp
new file mode 100644
index 00000000..eb3b25d9
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/NG1.cpp
@@ -0,0 +1,74 @@
+#include "DS.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct NG_1 : DS_Module {
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_1,
+ NUM_INPUTS = x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ NG_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ for (int i = 0; i < x; i++) {
+ outputs[OUTPUT_1 + i].value = (inputs[INPUT_1 + i].value < midpoint())?voltage1:voltage0;
+ }
+ }
+};
+
+struct NG106 : ModuleWidget {
+ NG106(NG_1<6> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/NG-106.svg")));
+
+ for (int i = 0; i < 6; i++) {
+ int offset = 58 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, NG_1<6>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(2.5,47 + offset), Port::OUTPUT, module, NG_1<6>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct NG112 : ModuleWidget {
+ NG112(NG_1<12> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/NG-112.svg")));
+
+ for (int i = 0; i < 12; i++) {
+ int offset = 29 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, NG_1<12>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(62,19 + offset), Port::OUTPUT, module, NG_1<12>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, NG106) {
+ Model *modelNG106 = Model::create, NG106>("Submarine (Free)", "NG-106", "NG-106 NOT Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelNG106;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, NG112) {
+ Model *modelNG112 = Model::create, NG112>("Submarine (Free)", "NG-112", "NG-112 NOT Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelNG112;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/OG-106.cpp b/plugins/community/repos/SubmarineFree/src/OG-106.cpp
deleted file mode 100644
index 22327537..00000000
--- a/plugins/community/repos/SubmarineFree/src/OG-106.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "DS.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct OG_106 : DS_Module {
- static const int deviceCount = 6;
- enum ParamIds {
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_A_1,
- INPUT_A_2,
- INPUT_A_3,
- INPUT_A_4,
- INPUT_A_5,
- INPUT_A_6,
- INPUT_B_1,
- INPUT_B_2,
- INPUT_B_3,
- INPUT_B_4,
- INPUT_B_5,
- INPUT_B_6,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
-
- OG_106() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void OG_106::step() {
- int setCount = 0;
- for (int i = 0; i < deviceCount; i++) {
- if (inputs[INPUT_A_1 + i].active)
- if (inputs[INPUT_A_1 + i].value > midpoint())
- setCount++;
- if (inputs[INPUT_B_1 + i].active)
- if (inputs[INPUT_B_1 + i].value > midpoint())
- setCount++;
- if (outputs[OUTPUT_1 + i].active) {
- outputs[OUTPUT_1 + i].value = (setCount > 0)?voltage1:voltage0;
- setCount = 0;
- }
- }
-}
-
-struct OG106 : ModuleWidget {
- OG106(OG_106 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/OG-106.svg")));
-
- for (int i = 0; i < OG_106::deviceCount; i++) {
- int offset = 58 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, OG_106::INPUT_A_1 + i));
- addInput(Port::create(Vec(4,47 + offset), Port::INPUT, module, OG_106::INPUT_B_1 + i));
-
- addOutput(Port::create(Vec(62,33 + offset), Port::OUTPUT, module, OG_106::OUTPUT_1 + i));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, OG106) {
- Model *modelOG106 = Model::create("SubmarineFree", "OG-106", "OG-106 OR Gates", LOGIC_TAG, MULTIPLE_TAG);
- return modelOG106;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/OG1.cpp b/plugins/community/repos/SubmarineFree/src/OG1.cpp
new file mode 100644
index 00000000..8a53b359
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/OG1.cpp
@@ -0,0 +1,87 @@
+#include "DS.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct OG_1 : DS_Module {
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_A_1,
+ INPUT_B_1 = x,
+ NUM_INPUTS = x + x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ OG_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ int setCount = 0;
+ for (int i = 0; i < x; i++) {
+ if (inputs[INPUT_A_1 + i].active)
+ if (inputs[INPUT_A_1 + i].value > midpoint())
+ setCount++;
+ if (inputs[INPUT_B_1 + i].active)
+ if (inputs[INPUT_B_1 + i].value > midpoint())
+ setCount++;
+ if (outputs[OUTPUT_1 + i].active) {
+ outputs[OUTPUT_1 + i].value = (setCount > 0)?voltage1:voltage0;
+ setCount = 0;
+ }
+ }
+ }
+};
+
+struct OG104 : ModuleWidget {
+ OG104(OG_1<4> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/OG-104.svg")));
+
+ for (int i = 0; i < 4; i++) {
+ int offset = 87 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, OG_1<4>::INPUT_A_1 + i));
+ addInput(Port::create(Vec(2.5,47 + offset), Port::INPUT, module, OG_1<4>::INPUT_B_1 + i));
+
+ addOutput(Port::create(Vec(2.5,75 + offset), Port::OUTPUT, module, OG_1<4>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct OG106 : ModuleWidget {
+ OG106(OG_1<6> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/OG-106.svg")));
+
+ for (int i = 0; i < 6; i++) {
+ int offset = 58 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, OG_1<6>::INPUT_A_1 + i));
+ addInput(Port::create(Vec(4,47 + offset), Port::INPUT, module, OG_1<6>::INPUT_B_1 + i));
+
+ addOutput(Port::create(Vec(62,33 + offset), Port::OUTPUT, module, OG_1<6>::OUTPUT_1 + i));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, OG104) {
+ Model *modelOG104 = Model::create, OG104>("Submarine (Free)", "OG-104", "OG-104 OR Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelOG104;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, OG106) {
+ Model *modelOG106 = Model::create, OG106>("Submarine (Free)", "OG-106", "OG-106 OR Gates", LOGIC_TAG, MULTIPLE_TAG);
+ return modelOG106;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/PG-112.cpp b/plugins/community/repos/SubmarineFree/src/PG-112.cpp
deleted file mode 100644
index 7a33c7ac..00000000
--- a/plugins/community/repos/SubmarineFree/src/PG-112.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#include "DS.hpp"
-#include "dsp/digital.hpp"
-
-namespace rack_plugin_SubmarineFree {
-
-struct PG_112 : DS_Module {
- static const int deviceCount = 12;
- enum ParamIds {
- PARAM_1,
- PARAM_2,
- PARAM_3,
- PARAM_4,
- PARAM_5,
- PARAM_6,
- PARAM_7,
- PARAM_8,
- PARAM_9,
- PARAM_10,
- PARAM_11,
- PARAM_12,
- NUM_PARAMS
- };
- enum InputIds {
- INPUT_1,
- INPUT_2,
- INPUT_3,
- INPUT_4,
- INPUT_5,
- INPUT_6,
- INPUT_7,
- INPUT_8,
- INPUT_9,
- INPUT_10,
- INPUT_11,
- INPUT_12,
- NUM_INPUTS
- };
- enum OutputIds {
- OUTPUT_1,
- OUTPUT_2,
- OUTPUT_3,
- OUTPUT_4,
- OUTPUT_5,
- OUTPUT_6,
- OUTPUT_7,
- OUTPUT_8,
- OUTPUT_9,
- OUTPUT_10,
- OUTPUT_11,
- OUTPUT_12,
- NUM_OUTPUTS
- };
- enum LightIds {
- NUM_LIGHTS
- };
- DS_Schmitt schmitt[deviceCount];
- PulseGenerator pulse[deviceCount];
-
- PG_112() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
- void step() override;
-};
-
-void PG_112::step() {
- float deltaTime = 1.0f / engineGetSampleRate();
- for (int i = 0; i < deviceCount; i++) {
- if (schmitt[i].redge(this, inputs[INPUT_1 + i].value)) {
- pulse[i].process(deltaTime);
- pulse[i].trigger(powf(10.0f, params[PARAM_1 + i].value));
- outputs[OUTPUT_1 + i].value = voltage1;
- }
- else {
- outputs[OUTPUT_1 + i].value = pulse[i].process(deltaTime)?voltage1:voltage0;
- }
- }
-}
-
-struct PG112 : ModuleWidget {
- PG112(PG_112 *module) : ModuleWidget(module) {
- setPanel(SVG::load(assetPlugin(plugin, "res/PG-112.svg")));
-
- for (int i = 0; i < PG_112::deviceCount; i++) {
- int offset = 29 * i;
- addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, PG_112::INPUT_1 + i));
-
- addOutput(Port::create(Vec(92,19 + offset), Port::OUTPUT, module, PG_112::OUTPUT_1 + i));
-
- addParam(ParamWidget::create(Vec(33,19.5 + offset), module, PG_112::PARAM_1 + i, -5.0f, 2.0f, -2.0f));
- }
- }
- void appendContextMenu(Menu *menu) override {
- ((DS_Module *)module)->appendContextMenu(menu);
- }
-};
-
-} // namespace rack_plugin_SubmarineFree
-
-using namespace rack_plugin_SubmarineFree;
-
-RACK_PLUGIN_MODEL_INIT(SubmarineFree, PG112) {
- Model *modelPG112 = Model::create("SubmarineFree", "PG-112", "PG-112 Pulse Generators", LOGIC_TAG, MULTIPLE_TAG);
- return modelPG112;
-}
diff --git a/plugins/community/repos/SubmarineFree/src/PG1.cpp b/plugins/community/repos/SubmarineFree/src/PG1.cpp
new file mode 100644
index 00000000..17f52af1
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/PG1.cpp
@@ -0,0 +1,90 @@
+#include "DS.hpp"
+#include "dsp/digital.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+template
+struct PG_1 : DS_Module {
+ enum ParamIds {
+ PARAM_1,
+ NUM_PARAMS = x
+ };
+ enum InputIds {
+ INPUT_1,
+ NUM_INPUTS = x
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ NUM_OUTPUTS = x
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+ DS_Schmitt schmitt[x];
+ PulseGenerator pulse[x];
+
+ PG_1() : DS_Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override {
+ float deltaTime = 1.0f / engineGetSampleRate();
+ for (int i = 0; i < x; i++) {
+ if (schmitt[i].redge(this, inputs[INPUT_1 + i].value)) {
+ pulse[i].process(deltaTime);
+ pulse[i].trigger(powf(10.0f, params[PARAM_1 + i].value));
+ outputs[OUTPUT_1 + i].value = voltage1;
+ }
+ else {
+ outputs[OUTPUT_1 + i].value = pulse[i].process(deltaTime)?voltage1:voltage0;
+ }
+ }
+ }
+};
+
+struct PG104 : ModuleWidget {
+ PG104(PG_1<4> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/PG-104.svg")));
+
+ for (int i = 0; i < 4; i++) {
+ int offset = 87 * i;
+ addInput(Port::create(Vec(2.5,19 + offset), Port::INPUT, module, PG_1<4>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(2.5,75 + offset), Port::OUTPUT, module, PG_1<4>::OUTPUT_1 + i));
+
+ addParam(ParamWidget::create>(Vec(3,47.5 + offset), module, PG_1<4>::PARAM_1 + i, -5.0f, 2.0f, -2.0f));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+struct PG112 : ModuleWidget {
+ PG112(PG_1<12> *module) : ModuleWidget(module) {
+ setPanel(SVG::load(assetPlugin(plugin, "res/PG-112.svg")));
+
+ for (int i = 0; i < 12; i++) {
+ int offset = 29 * i;
+ addInput(Port::create(Vec(4,19 + offset), Port::INPUT, module, PG_1<12>::INPUT_1 + i));
+
+ addOutput(Port::create(Vec(92,19 + offset), Port::OUTPUT, module, PG_1<12>::OUTPUT_1 + i));
+
+ addParam(ParamWidget::create>(Vec(33,19.5 + offset), module, PG_1<12>::PARAM_1 + i, -5.0f, 2.0f, -2.0f));
+ }
+ }
+ void appendContextMenu(Menu *menu) override {
+ ((DS_Module *)module)->appendContextMenu(menu);
+ }
+};
+
+} // namespace rack_plugin_SubmarineFree
+
+using namespace rack_plugin_SubmarineFree;
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, PG104) {
+ Model *modelPG104 = Model::create, PG104>("Submarine (Free)", "PG-104", "PG-104 Pulse Generators", LOGIC_TAG, MULTIPLE_TAG);
+ return modelPG104;
+}
+
+RACK_PLUGIN_MODEL_INIT(SubmarineFree, PG112) {
+ Model *modelPG112 = Model::create, PG112>("Submarine (Free)", "PG-112", "PG-112 Pulse Generators", LOGIC_TAG, MULTIPLE_TAG);
+ return modelPG112;
+}
diff --git a/plugins/community/repos/SubmarineFree/src/PO12.cpp b/plugins/community/repos/SubmarineFree/src/PO12.cpp
new file mode 100644
index 00000000..7a58244b
--- /dev/null
+++ b/plugins/community/repos/SubmarineFree/src/PO12.cpp
@@ -0,0 +1,509 @@
+#include "SubmarineFree.hpp"
+#include "dsp/functions.hpp"
+
+namespace rack_plugin_SubmarineFree {
+
+struct PO_Util {
+ static constexpr float deg0 = 0.0f;
+ static constexpr float deg30 = M_PI / 6.0f;
+ static constexpr float deg45 = M_PI / 4.0f;
+ static constexpr float deg60 = M_PI / 3.0f;
+ static constexpr float deg90 = M_PI / 2.0f;
+ static constexpr float deg120 = 2.0f * M_PI / 3.0f;
+ static constexpr float deg135 = 3.0f * M_PI / 4.0f;
+ static constexpr float deg150 = 5.0f * M_PI / 6.0f;
+ static constexpr float ph0 = 0.0f;
+ static constexpr float ph30 = 1.0f / 12.0f;
+ static constexpr float ph45 = 0.125f;
+ static constexpr float ph60 = 1.0f / 6.0f;
+ static constexpr float ph90 = 0.25f;
+ static constexpr float ph120 = 1.0f / 3.0f;
+ static constexpr float ph135 = 0.375f;
+ static constexpr float ph150 = 5.0f / 12.0f;
+ static constexpr float ph180 = 0.5f;
+ static constexpr float ph210 = 7.0f / 12.0f;
+ static constexpr float ph225 = 0.625;
+ static constexpr float ph240 = 2.0f / 3.0f;
+ static constexpr float ph270 = 0.75f;
+ static constexpr float ph300 = 5.0f / 6.0f;
+ static constexpr float ph315 = 0.875f;
+ static constexpr float ph330 = 11.0f / 12.0f;
+
+ float sin(float phase);
+ float tri(float phase);
+ float saw(float phase);
+ float sqr(float phase);
+ float rsn(float phase);
+};
+
+float PO_Util::sin(float phase) {
+ return 5.0f * sinf(phase);
+}
+
+float PO_Util::tri(float phase) {
+ phase -= floor(phase);
+ if (phase < 0.25f)
+ return 20.0f * phase;
+ if (phase < 0.75f)
+ return 20.0f * (0.5f - phase);
+ return 20.0f * (phase - 1.0f);
+}
+
+float PO_Util::saw(float phase) {
+ phase -= floor(phase);
+ if (phase < 0.5f)
+ return 10.0f * phase;
+ return 10.0f * (phase - 1.0f);
+}
+
+float PO_Util::sqr(float phase) {
+ phase -= floor(phase);
+ return (phase < 0.5f)?5.0f:-5.0f;
+}
+
+float PO_Util::rsn(float phase) {
+ return 10.0f * fabs(sinf(phase)) - 5.0f;
+}
+
+struct PO_101 : Module, PO_Util {
+
+ enum ParamIds {
+ PARAM_TUNE,
+ PARAM_FINE,
+ PARAM_WAVE,
+ PARAM_PHASE_1,
+ PARAM_PHASE_2,
+ PARAM_PHASE_3,
+ PARAM_PHASE_4,
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_NOTE_CV,
+ INPUT_PHASE_1,
+ INPUT_PHASE_2,
+ INPUT_PHASE_3,
+ INPUT_PHASE_4,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ OUTPUT_2,
+ OUTPUT_3,
+ OUTPUT_4,
+ OUTPUT_5,
+ OUTPUT_6,
+ OUTPUT_7,
+ OUTPUT_8,
+ OUTPUT_9,
+ OUTPUT_10,
+ OUTPUT_11,
+ OUTPUT_12,
+ OUTPUT_13,
+ OUTPUT_14,
+ OUTPUT_15,
+ OUTPUT_16,
+ OUTPUT_17,
+ OUTPUT_18,
+ OUTPUT_19,
+ OUTPUT_20,
+ NUM_OUTPUTS
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ PO_101() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override;
+ void sin(float phase);
+ void tri(float phase);
+ void saw(float phase);
+ void sqr(float phase);
+ void rsn(float phase);
+ float phase = 0.0f;
+ float baseFreq = 261.626f;
+};
+
+void PO_101::sin(float phase) {
+ phase *= (2 * M_PI);
+ if (outputs[OUTPUT_1].active || outputs[OUTPUT_9].active)
+ outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::sin(phase + deg0));
+ if (outputs[OUTPUT_2].active || outputs[OUTPUT_10].active)
+ outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::sin(phase + deg30));
+ if (outputs[OUTPUT_3].active || outputs[OUTPUT_11].active)
+ outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::sin(phase + deg45));
+ if (outputs[OUTPUT_4].active || outputs[OUTPUT_12].active)
+ outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::sin(phase + deg60));
+ if (outputs[OUTPUT_5].active || outputs[OUTPUT_13].active)
+ outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::sin(phase + deg90));
+ if (outputs[OUTPUT_6].active || outputs[OUTPUT_14].active)
+ outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::sin(phase + deg120));
+ if (outputs[OUTPUT_7].active || outputs[OUTPUT_15].active)
+ outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::sin(phase + deg135));
+ if (outputs[OUTPUT_8].active || outputs[OUTPUT_16].active)
+ outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::sin(phase + deg150));
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_17 + i].active) {
+ float offset = params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ offset *= 2 * M_PI;
+ outputs[OUTPUT_17 + i].value = PO_Util::sin(phase + offset);
+ }
+ }
+}
+
+void PO_101::tri(float phase) {
+ if (outputs[OUTPUT_1].active || outputs[OUTPUT_9].active)
+ outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::tri(phase + ph0));
+ if (outputs[OUTPUT_2].active || outputs[OUTPUT_10].active)
+ outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::tri(phase + ph30));
+ if (outputs[OUTPUT_3].active || outputs[OUTPUT_11].active)
+ outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::tri(phase + ph45));
+ if (outputs[OUTPUT_4].active || outputs[OUTPUT_12].active)
+ outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::tri(phase + ph60));
+ if (outputs[OUTPUT_5].active || outputs[OUTPUT_13].active)
+ outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::tri(phase + ph90));
+ if (outputs[OUTPUT_6].active || outputs[OUTPUT_14].active)
+ outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::tri(phase + ph120));
+ if (outputs[OUTPUT_7].active || outputs[OUTPUT_15].active)
+ outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::tri(phase + ph135));
+ if (outputs[OUTPUT_8].active || outputs[OUTPUT_16].active)
+ outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::tri(phase + ph150));
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_17 + i].active) {
+ float offset = params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ outputs[OUTPUT_17 + i].value = PO_Util::tri(phase + offset);
+ }
+ }
+}
+
+void PO_101::saw(float phase) {
+ if (outputs[OUTPUT_1].active)
+ outputs[OUTPUT_1].value = PO_Util::saw(phase + ph0);
+ if (outputs[OUTPUT_2].active)
+ outputs[OUTPUT_2].value = PO_Util::saw(phase + ph30);
+ if (outputs[OUTPUT_3].active)
+ outputs[OUTPUT_3].value = PO_Util::saw(phase + ph45);
+ if (outputs[OUTPUT_4].active)
+ outputs[OUTPUT_4].value = PO_Util::saw(phase + ph60);
+ if (outputs[OUTPUT_5].active)
+ outputs[OUTPUT_5].value = PO_Util::saw(phase + ph90);
+ if (outputs[OUTPUT_6].active)
+ outputs[OUTPUT_6].value = PO_Util::saw(phase + ph120);
+ if (outputs[OUTPUT_7].active)
+ outputs[OUTPUT_7].value = PO_Util::saw(phase + ph135);
+ if (outputs[OUTPUT_8].active)
+ outputs[OUTPUT_8].value = PO_Util::saw(phase + ph150);
+ if (outputs[OUTPUT_9].active)
+ outputs[OUTPUT_9].value = PO_Util::saw(phase + ph180);
+ if (outputs[OUTPUT_10].active)
+ outputs[OUTPUT_10].value = PO_Util::saw(phase + ph210);
+ if (outputs[OUTPUT_11].active)
+ outputs[OUTPUT_11].value = PO_Util::saw(phase + ph225);
+ if (outputs[OUTPUT_12].active)
+ outputs[OUTPUT_12].value = PO_Util::saw(phase + ph240);
+ if (outputs[OUTPUT_13].active)
+ outputs[OUTPUT_13].value = PO_Util::saw(phase + ph270);
+ if (outputs[OUTPUT_14].active)
+ outputs[OUTPUT_14].value = PO_Util::saw(phase + ph300);
+ if (outputs[OUTPUT_15].active)
+ outputs[OUTPUT_15].value = PO_Util::saw(phase + ph315);
+ if (outputs[OUTPUT_16].active)
+ outputs[OUTPUT_16].value = PO_Util::saw(phase + ph330);
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_17 + i].active) {
+ float offset = params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ outputs[OUTPUT_17 + i].value = PO_Util::saw(phase + offset);
+ }
+ }
+}
+
+void PO_101::sqr(float phase) {
+ if (outputs[OUTPUT_1].active || outputs[OUTPUT_9].active)
+ outputs[OUTPUT_9].value = -(outputs[OUTPUT_1].value = PO_Util::sqr(phase + ph0));
+ if (outputs[OUTPUT_2].active || outputs[OUTPUT_10].active)
+ outputs[OUTPUT_10].value = -(outputs[OUTPUT_2].value = PO_Util::sqr(phase + ph30));
+ if (outputs[OUTPUT_3].active || outputs[OUTPUT_11].active)
+ outputs[OUTPUT_11].value = -(outputs[OUTPUT_3].value = PO_Util::sqr(phase + ph45));
+ if (outputs[OUTPUT_4].active || outputs[OUTPUT_12].active)
+ outputs[OUTPUT_12].value = -(outputs[OUTPUT_4].value = PO_Util::sqr(phase + ph60));
+ if (outputs[OUTPUT_5].active || outputs[OUTPUT_13].active)
+ outputs[OUTPUT_13].value = -(outputs[OUTPUT_5].value = PO_Util::sqr(phase + ph90));
+ if (outputs[OUTPUT_6].active || outputs[OUTPUT_14].active)
+ outputs[OUTPUT_14].value = -(outputs[OUTPUT_6].value = PO_Util::sqr(phase + ph120));
+ if (outputs[OUTPUT_7].active || outputs[OUTPUT_15].active)
+ outputs[OUTPUT_15].value = -(outputs[OUTPUT_7].value = PO_Util::sqr(phase + ph135));
+ if (outputs[OUTPUT_8].active || outputs[OUTPUT_16].active)
+ outputs[OUTPUT_16].value = -(outputs[OUTPUT_8].value = PO_Util::sqr(phase + ph150));
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_17 + i].active) {
+ float offset = params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ outputs[OUTPUT_17 + i].value = PO_Util::sqr(phase + offset);
+ }
+ }
+}
+
+void PO_101::rsn(float phase) {
+ phase *= (2 * M_PI);
+ if (outputs[OUTPUT_1].active || outputs[OUTPUT_9].active)
+ outputs[OUTPUT_9].value = (outputs[OUTPUT_1].value = PO_Util::rsn(phase + deg0));
+ if (outputs[OUTPUT_2].active || outputs[OUTPUT_10].active)
+ outputs[OUTPUT_10].value = (outputs[OUTPUT_2].value = PO_Util::rsn(phase + deg30));
+ if (outputs[OUTPUT_3].active || outputs[OUTPUT_11].active)
+ outputs[OUTPUT_11].value = (outputs[OUTPUT_3].value = PO_Util::rsn(phase + deg45));
+ if (outputs[OUTPUT_4].active || outputs[OUTPUT_12].active)
+ outputs[OUTPUT_12].value = (outputs[OUTPUT_4].value = PO_Util::rsn(phase + deg60));
+ if (outputs[OUTPUT_5].active || outputs[OUTPUT_13].active)
+ outputs[OUTPUT_13].value = (outputs[OUTPUT_5].value = PO_Util::rsn(phase + deg90));
+ if (outputs[OUTPUT_6].active || outputs[OUTPUT_14].active)
+ outputs[OUTPUT_14].value = (outputs[OUTPUT_6].value = PO_Util::rsn(phase + deg120));
+ if (outputs[OUTPUT_7].active || outputs[OUTPUT_15].active)
+ outputs[OUTPUT_15].value = (outputs[OUTPUT_7].value = PO_Util::rsn(phase + deg135));
+ if (outputs[OUTPUT_8].active || outputs[OUTPUT_16].active)
+ outputs[OUTPUT_16].value = (outputs[OUTPUT_8].value = PO_Util::rsn(phase + deg150));
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_17 + i].active) {
+ float offset = params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ offset *= 2 * M_PI;
+ outputs[OUTPUT_17 + i].value = PO_Util::rsn(phase + offset);
+ }
+ }
+}
+
+void PO_101::step() {
+ float freq = baseFreq * powf(2.0f, (params[PARAM_TUNE].value + 3.0f * quadraticBipolar(params[PARAM_FINE].value)) / 12.0f + (inputs[INPUT_NOTE_CV].active?inputs[INPUT_NOTE_CV].value:0.0f));
+ float deltaTime = freq / engineGetSampleRate();
+ phase += deltaTime;
+ double intPart;
+ phase = modf(phase, &intPart);
+
+ {
+ float waveShape = clamp(params[PARAM_WAVE].value, 0.0f, 4.0f);
+ if (waveShape < 0.5f)
+ sin(phase);
+ else if (waveShape < 1.5f)
+ tri(phase);
+ else if (waveShape < 2.5f)
+ saw(phase);
+ else if (waveShape < 3.5f)
+ sqr(phase);
+ else
+ rsn(phase);
+ }
+
+}
+
+struct PO_204 : Module, PO_Util {
+
+ enum ParamIds {
+ PARAM_TUNE,
+ PARAM_FINE,
+ PARAM_WAVE_1,
+ PARAM_WAVE_2,
+ PARAM_WAVE_3,
+ PARAM_WAVE_4,
+ PARAM_PHASE_1,
+ PARAM_PHASE_2,
+ PARAM_PHASE_3,
+ PARAM_PHASE_4,
+ PARAM_MULT_1,
+ PARAM_MULT_2,
+ PARAM_MULT_3,
+ PARAM_MULT_4,
+ NUM_PARAMS
+ };
+ enum InputIds {
+ INPUT_TUNE,
+ INPUT_WAVE_1,
+ INPUT_WAVE_2,
+ INPUT_WAVE_3,
+ INPUT_WAVE_4,
+ INPUT_PHASE_1,
+ INPUT_PHASE_2,
+ INPUT_PHASE_3,
+ INPUT_PHASE_4,
+ INPUT_MULT_1,
+ INPUT_MULT_2,
+ INPUT_MULT_3,
+ INPUT_MULT_4,
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ OUTPUT_1,
+ OUTPUT_2,
+ OUTPUT_3,
+ OUTPUT_4,
+ NUM_OUTPUTS
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ PO_204() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
+ void step() override;
+ float phase = 0.0f;
+ float baseFreq = 261.626f;
+};
+
+void PO_204::step() {
+ float freq = baseFreq * powf(2.0f, (params[PARAM_TUNE].value + 3.0f * quadraticBipolar(params[PARAM_FINE].value)) / 12.0f + (inputs[INPUT_TUNE].active?inputs[INPUT_TUNE].value:0.0f));
+ float deltaTime = freq / engineGetSampleRate();
+ phase += deltaTime;
+ double intPart;
+ phase = modf(phase, &intPart);
+ for (int i = 0; i < 4; i++) {
+ if (outputs[OUTPUT_1 + i].active) {
+ float offset = phase + params[PARAM_PHASE_1 + i].value;
+ if (inputs[INPUT_PHASE_1 + i].active)
+ offset += inputs[INPUT_PHASE_1 + i].value * 0.4f;
+ offset *= floor(clamp(params[PARAM_MULT_1 + i].value + (inputs[INPUT_MULT_1 + i].active?inputs[INPUT_MULT_1 + i].value:0.0f) * 16.0f / 10.0f, 1.0f, 16.5f));
+ float wave = params[PARAM_WAVE_1 + i].value + (inputs[INPUT_WAVE_1 + i].active?inputs[INPUT_WAVE_1 + i].value:0.0f);
+ double waveSection;
+ wave = modf(clamp(wave, 0.0f, 10.0f), &waveSection);
+ float w1 = 0.0f;
+ float w2 = 0.0f;
+ switch ((int)waveSection) {
+ case 0:
+ w1 = PO_Util::sin(offset * 2 * M_PI);
+ w2 = PO_Util::saw(offset);
+ break;
+ case 1:
+ w1 = PO_Util::saw(offset);
+ w2 = PO_Util::rsn(offset * 2 * M_PI);
+ break;
+ case 2:
+ w1 = PO_Util::rsn(offset * 2 * M_PI);
+ w2 = PO_Util::tri(offset);
+ break;
+ case 3:
+ w1 = PO_Util::tri(offset);
+ w2 = PO_Util::sqr(offset);
+ break;
+ case 4:
+ w1 = PO_Util::sqr(offset);
+ w2 = PO_Util::sin(offset * 2 * M_PI);
+ break;
+ case 5:
+ w1 = PO_Util::sin(offset * 2 * M_PI);
+ w2 = PO_Util::tri(offset);
+ break;
+ case 6:
+ w1 = PO_Util::tri(offset);
+ w2 = PO_Util::saw(offset);
+ break;
+ case 7:
+ w1 = PO_Util::saw(offset);
+ w2 = PO_Util::sqr(offset);
+ break;
+ case 8:
+ w1 = PO_Util::sqr(offset);
+ w2 = PO_Util::rsn(offset * 2 * M_PI);
+ break;
+ case 9:
+ w1 = PO_Util::rsn(offset * 2 * M_PI);
+ w2 = PO_Util::sin(offset * 2 * M_PI);
+ break;
+ default:
+ w2 = w1 = PO_Util::sin(offset * 2 * M_PI);
+ break;
+ }
+ outputs[OUTPUT_1 + i].value = w1 * (1.0f - wave) + w2 * wave;
+ }
+ }
+}
+
+struct PO_Layout : ModuleWidget {
+ PO_Layout(PO_101 *module) : ModuleWidget(module) {}
+ void Layout() {
+ addParam(ParamWidget::create>(Vec(66, 39), module, PO_101::PARAM_FINE, -1.0f, +1.0f, 0.0f));
+ addParam(ParamWidget::create>>>(Vec(121, 39), module, PO_101::PARAM_WAVE, 0.0f, +4.0f, 0.0f));
+
+ addInput(Port::create(Vec(45,19), Port::INPUT, module, PO_101::INPUT_NOTE_CV));
+
+ addOutput(Port::create(Vec(77.5,100), Port::OUTPUT, module, PO_101::OUTPUT_1));
+ addOutput(Port::create(Vec(110,109), Port::OUTPUT, module, PO_101::OUTPUT_2));
+ addOutput(Port::create(Vec(142.5,100), Port::OUTPUT, module, PO_101::OUTPUT_3));
+ addOutput(Port::create(Vec(133.5,132.5), Port::OUTPUT, module, PO_101::OUTPUT_4));
+ addOutput(Port::create(Vec(142.5,165), Port::OUTPUT, module, PO_101::OUTPUT_5));
+ addOutput(Port::create(Vec(133.5,197.5), Port::OUTPUT, module, PO_101::OUTPUT_6));
+ addOutput(Port::create(Vec(142.5,230), Port::OUTPUT, module, PO_101::OUTPUT_7));
+ addOutput(Port::create(Vec(110,221), Port::OUTPUT, module, PO_101::OUTPUT_8));
+ addOutput(Port::create(Vec(77.5,230), Port::OUTPUT, module, PO_101::OUTPUT_9));
+ addOutput(Port::create(Vec(45,221), Port::OUTPUT, module, PO_101::OUTPUT_10));
+ addOutput(Port::create