├── .gitignore
├── .gitmodules
├── LICENSE-GPLv3.txt
├── LICENSE.md
├── Makefile
├── README.md
├── generate_quantizer_presets.py
├── plugin.json
├── presets
├── CVMix
│ └── Unity mix.vcvm
├── Mutes
│ └── Mute all.vcvm
└── Quantizer
│ ├── 00_Ionian (Major).vcvm
│ ├── 01_Dorian.vcvm
│ ├── 02_Phrygian.vcvm
│ ├── 03_Lydian.vcvm
│ ├── 04_Mixolydian.vcvm
│ ├── 05_Aeolian (Minor).vcvm
│ ├── 06_Locrian.vcvm
│ ├── 07_Aeolian 7 (Harmonic Minor).vcvm
│ ├── 08_Locrian 6.vcvm
│ ├── 09_Ionian #5.vcvm
│ ├── 10_Dorian #4.vcvm
│ ├── 11_Phrygian 3.vcvm
│ ├── 12_Lydian #2.vcvm
│ ├── 13_Locrian b4 bb7.vcvm
│ ├── 14_Aeolian 6 7 (Melodic Minor).vcvm
│ ├── 15_Phrygian 6.vcvm
│ ├── 16_Lydian #5.vcvm
│ ├── 17_Lydian b7.vcvm
│ ├── 18_Aeolian 3.vcvm
│ ├── 19_Locrian 2.vcvm
│ ├── 20_Locrian b4.vcvm
│ ├── 21_Bebop Dominant.vcvm
│ ├── 22_Bebop Major.vcvm
│ ├── 23_Bebop Minor.vcvm
│ ├── 24_Bebop Melodic Minor.vcvm
│ ├── 25_Blues Major.vcvm
│ ├── 26_Blues Minor.vcvm
│ ├── 27_Blues Diminished.vcvm
│ ├── 28_Blues Pentatonic.vcvm
│ ├── 29_Blues Rock'n'Roll.vcvm
│ ├── 30_Byzantine.vcvm
│ ├── 31_Hungarian Minor.vcvm
│ ├── 32_Hungarian Gypsy.vcvm
│ ├── 33_Spanish Gypsy.vcvm
│ ├── 34_Major Pentatonic.vcvm
│ ├── 35_Neutral Pentatonic.vcvm
│ ├── 36_Rock Pentatonic.vcvm
│ ├── 37_Scottish Pentatonic.vcvm
│ ├── 38_Minor Pentatonic.vcvm
│ ├── 39_Whole.vcvm
│ ├── 40_Whole-Half.vcvm
│ ├── 41_Half-Whole.vcvm
│ ├── 42_Augmented.vcvm
│ ├── 43_Byzantine.vcvm
│ ├── 44_Chromatic.vcvm
│ ├── 45_Enigmatic (Ascending).vcvm
│ ├── 46_Enigmatic (Descending).vcvm
│ ├── 47_Hungarian Major.vcvm
│ ├── 48_Hungarian Minor.vcvm
│ ├── 49_Neapolitan Major.vcvm
│ ├── 50_Neapolitan Minor.vcvm
│ ├── 51_Overtone.vcvm
│ ├── 52_Prometheus.vcvm
│ ├── 53_Prometheus Neapolitan.vcvm
│ └── 54_Spanish 8 Tone.vcvm
├── res
├── 8vert-dark.svg
├── 8vert.svg
├── ADSR-dark.svg
├── ADSR.svg
├── CVMix-dark.svg
├── CVMix.svg
├── Compare-dark.svg
├── Compare.svg
├── Delay-dark.svg
├── Delay.svg
├── Fade-dark.svg
├── Fade.svg
├── Gates-dark.svg
├── Gates.svg
├── LFO-dark.svg
├── LFO.svg
├── Logic-dark.svg
├── Logic.svg
├── Merge-dark.svg
├── Merge.svg
├── MidSide-dark.svg
├── MidSide.svg
├── Mixer-dark.svg
├── Mixer.svg
├── Mult-dark.svg
├── Mult.svg
├── Mutes-dark.svg
├── Mutes.svg
├── Noise-dark.svg
├── Noise.svg
├── Octave-dark.svg
├── Octave.svg
├── Process-dark.svg
├── Process.svg
├── Pulses-dark.svg
├── Pulses.svg
├── Push-dark.svg
├── Push.svg
├── Quantizer-dark.svg
├── Quantizer.svg
├── Random-dark.svg
├── Random.svg
├── RandomValues-dark.svg
├── RandomValues.svg
├── Rescale-dark.svg
├── Rescale.svg
├── SEQ3-dark.svg
├── SEQ3.svg
├── SHASR-dark.svg
├── SHASR.svg
├── Scope-dark.svg
├── Scope.svg
├── SequentialSwitch1-dark.svg
├── SequentialSwitch1.svg
├── SequentialSwitch2-dark.svg
├── SequentialSwitch2.svg
├── Split-dark.svg
├── Split.svg
├── Sum-dark.svg
├── Sum.svg
├── Unity.svg
├── VCA-1-dark.svg
├── VCA-1.svg
├── VCA.svg
├── VCF-dark.svg
├── VCF.svg
├── VCMixer-dark.svg
├── VCMixer.svg
├── VCO-dark.svg
├── VCO.svg
├── VCVBezelBig.svg
├── Viz-dark.svg
├── Viz.svg
├── WTLFO-dark.svg
├── WTLFO.svg
├── WTVCO-dark.svg
└── WTVCO.svg
└── src
├── 8vert.cpp
├── ADSR.cpp
├── CVMix.cpp
├── Compare.cpp
├── Delay.cpp
├── Fade.cpp
├── Gates.cpp
├── LFO.cpp
├── Logic.cpp
├── Merge.cpp
├── MidSide.cpp
├── Mixer.cpp
├── Mult.cpp
├── Mutes.cpp
├── Noise.cpp
├── Octave.cpp
├── Process.cpp
├── Pulses.cpp
├── Push.cpp
├── Quantizer.cpp
├── Random.cpp
├── RandomValues.cpp
├── Rescale.cpp
├── SEQ3.cpp
├── SHASR.cpp
├── Scope.cpp
├── SequentialSwitch.cpp
├── Split.cpp
├── Sum.cpp
├── Unity.cpp
├── VCA-1.cpp
├── VCA.cpp
├── VCF.cpp
├── VCMixer.cpp
├── VCO.cpp
├── Viz.cpp
├── WTLFO.cpp
├── WTVCO.cpp
├── Wavetable.hpp
├── dr_wav.c
├── dr_wav.h
├── plugin.cpp
└── plugin.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore all dotfiles except git dotfiles
2 | .*
3 | !.git*
4 |
5 | # Binaries and build targets
6 | *.a
7 | *.so
8 | *.dylib
9 | *.dll
10 | /build
11 | /dep
12 | /dist
13 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VCVRack/Fundamental/d12b22a170f2b4e7940572ac13ad24869f8ea806/.gitmodules
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | All **source code** is copyright © 2016-2023 VCV.
2 |
3 | This program is free software: you can redistribute it and/or modify it under the terms of the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.en.html) as published by the [Free Software Foundation](https://www.fsf.org/), either version 3 of the License, or (at your option) any later version.
4 |
5 | The **VCV logo and icon** are copyright © 2017 VCV and may not be used in derivative works.
6 |
7 | The **"VCV" name** is trademarked and may not be used for unofficial products.
8 |
9 | The **visual design of the Fundamental modules** is copyright © 2019-2023 [VCV](https://vcvrack.com/) and licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/).
10 | Commercial use and derivative works are not allowed.
11 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | RACK_DIR ?= ../..
2 |
3 | FLAGS += -Idep/include
4 | SOURCES += $(wildcard src/*.cpp)
5 | SOURCES += $(wildcard src/*.c)
6 | DISTRIBUTABLES += res
7 | DISTRIBUTABLES += $(wildcard LICENSE*)
8 | DISTRIBUTABLES += $(wildcard presets)
9 |
10 | include $(RACK_DIR)/plugin.mk
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VCV Free modules
2 |
3 | *Essential synthesizer modules included with VCV Rack*
4 |
5 | Contact [VCV Support](https://vcvrack.com/support) to request a feature or report a bug.
6 |
7 | We cannot accept code contributions for this plugin.
8 | See [Contributing to Rack](https://github.com/VCVRack/Rack/blob/v2/.github/CONTRIBUTING.md).
9 |
10 | ## Building
11 |
12 | Follow [Building Rack plugins](https://vcvrack.com/manual/Building#Building-Rack-plugins) in the VCV Rack manual.
13 |
--------------------------------------------------------------------------------
/generate_quantizer_presets.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 |
4 | scales = """
5 | Ionian (Major) 1-2-3-4-5-6-7
6 | Dorian 1-2-b3-4-5-6-b7
7 | Phrygian 1-b2-b3-4-5-b6-b7
8 | Lydian 1-2-3-#4-5-6-7
9 | Mixolydian 1-2-3-4-5-6-b7
10 | Aeolian (Minor) 1-2-b3-4-5-b6-b7
11 | Locrian 1-b2-b3-4-b5-b6-b7
12 | Aeolian 7 (Harmonic Minor) 1-2-b3-4-5-b6-7
13 | Locrian 6 1-b2-b3-4-b5-6-b7
14 | Ionian #5 1-2-3-4-#5-6-7
15 | Dorian #4 1-2-b3-#4-5-6-b7
16 | Phrygian 3 1-b2-3-4-5-b6-b7
17 | Lydian #2 1-#2-3-#4-5-6-7
18 | Locrian b4 bb7 1-b2-b3-b4-b5-b6-bb7
19 | Aeolian 6 7 (Melodic Minor) 1-2-b3-4-5-6-7
20 | Phrygian 6 1-b2-b3-4-5-6-b7
21 | Lydian #5 1-2-3-#4-#5-6-7
22 | Lydian b7 1-2-3-#4-5-6-b7
23 | Aeolian 3 1-2-3-4-5-b6-b7
24 | Locrian 2 1-2-b3-4-b5-b6-b7
25 | Locrian b4 1-b2-b3-b4-b5-b6-b7
26 | Bebop Dominant 1-2-3-4-5-6-#6-7
27 | Bebop Major 1-2-3-4-5-b6-6-7
28 | Bebop Minor 1-2-b3-3-4-5-6-b7
29 | Bebop Melodic Minor 1-2-b3-4-5-b6-6-7
30 | Blues Major 1-2-b3-3-5-6
31 | Blues Minor 1-b3-4-b5-5-b7
32 | Blues Diminished 1-b2-b3-3-b5-5-6-b7
33 | Blues Pentatonic 1-b3-4-5-b7
34 | Blues Rock'n'Roll 1-2-b3-3-4-b5-5-6-b7
35 | Byzantine 1-b2-3-4-5-b6-7
36 | Hungarian Minor 1-2-b3-b5-5-b6-7
37 | Hungarian Gypsy 1-2-b3-#4-5-b6-b7
38 | Spanish Gypsy 1-b2-3-4-5-b6-b7
39 | Major Pentatonic 1-2-3-5-6
40 | Neutral Pentatonic 1-2-4-5-b7
41 | Rock Pentatonic 1-b3-4-#5-b7
42 | Scottish Pentatonic 1-2-4-5-6
43 | Minor Pentatonic 1-b3-4-5-b7
44 | Whole 1-2-3-#4-#5-#6
45 | Whole-Half 1-2-b3-4-#4-#5-6-7
46 | Half-Whole 1-b2-b3-3-b5-5-6-b7
47 | Augmented 1-#2-3-5-#5-7
48 | Byzantine 1-b2-3-4-5-b6-7
49 | Chromatic 1-#1-2-#2-3-4-#4-5-#5-6-#6-7
50 | Enigmatic (Ascending) 1-b2-3-#4-#5-#6-7
51 | Enigmatic (Descending) 1-b2-3-4-b6-b7-7
52 | Hungarian Major 1-b3-3-b5-5-6-b7
53 | Hungarian Minor 1-2-b3-b5-5-b6-7
54 | Neapolitan Major 1-b2-b3-4-5-6-7
55 | Neapolitan Minor 1-b2-b3-4-5-b6-7
56 | Overtone 1-2-3-#4-5-6-b7
57 | Prometheus 1-2-3-b5-6-b7
58 | Prometheus Neapolitan 1-b2-3-b5-6-b7
59 | Spanish 8 Tone 1-b2-b3-3-4-b5-b6-b7
60 | """
61 |
62 | note_indexes = {
63 | "1": 0,
64 | "#1": 1,
65 | "b2": 1,
66 | "2": 2,
67 | "#2": 3,
68 | "b3": 3,
69 | "3": 4,
70 | "b4": 4,
71 | "4": 5,
72 | "#4": 6,
73 | "b5": 6,
74 | "5": 7,
75 | "#5": 8,
76 | "b6": 8,
77 | "6": 9,
78 | "bb7": 9,
79 | "#6": 10,
80 | "b7": 10,
81 | "7": 11,
82 | }
83 |
84 | dir = "presets/Quantizer"
85 | os.makedirs(dir, exist_ok=True)
86 | count = 0
87 |
88 | for line in scales.splitlines():
89 | if not line:
90 | continue
91 |
92 | data = {
93 | "plugin": "Fundamental",
94 | "model": "Quantizer",
95 | "version": "2.0.0",
96 | "params": [],
97 | "data": {
98 | "enabledNotes": [
99 | False,
100 | False,
101 | False,
102 | False,
103 | False,
104 | False,
105 | False,
106 | False,
107 | False,
108 | False,
109 | False,
110 | False,
111 | ]
112 | }
113 | }
114 |
115 | name, notes = line.split("\t")
116 | notes = notes.split("-")
117 |
118 | for note in notes:
119 | note_index = note_indexes[note]
120 | data["data"]["enabledNotes"][note_index] = True
121 |
122 | path = os.path.join(dir, f"{count:02d}_{name}.vcvm")
123 | print(path)
124 | print(data)
125 | with open(path, "w", newline='\n') as f:
126 | json.dump(data, f, indent=2)
127 |
128 | count += 1
129 |
--------------------------------------------------------------------------------
/presets/CVMix/Unity mix.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "CVMix",
4 | "version": "2.2.0",
5 | "params": [
6 | {
7 | "value": 1.0,
8 | "id": 0
9 | },
10 | {
11 | "value": 1.0,
12 | "id": 1
13 | },
14 | {
15 | "value": 1.0,
16 | "id": 2
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/presets/Mutes/Mute all.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Mutes",
4 | "version": "2.2.0",
5 | "params": [
6 | {
7 | "value": 1.0,
8 | "id": 0
9 | },
10 | {
11 | "value": 1.0,
12 | "id": 1
13 | },
14 | {
15 | "value": 1.0,
16 | "id": 2
17 | },
18 | {
19 | "value": 1.0,
20 | "id": 3
21 | },
22 | {
23 | "value": 1.0,
24 | "id": 4
25 | },
26 | {
27 | "value": 1.0,
28 | "id": 5
29 | },
30 | {
31 | "value": 1.0,
32 | "id": 6
33 | },
34 | {
35 | "value": 1.0,
36 | "id": 7
37 | },
38 | {
39 | "value": 1.0,
40 | "id": 8
41 | },
42 | {
43 | "value": 1.0,
44 | "id": 9
45 | }
46 | ]
47 | }
--------------------------------------------------------------------------------
/presets/Quantizer/00_Ionian (Major).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/01_Dorian.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/02_Phrygian.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/03_Lydian.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/04_Mixolydian.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/05_Aeolian (Minor).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/06_Locrian.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/07_Aeolian 7 (Harmonic Minor).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/08_Locrian 6.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | true,
15 | false,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/09_Ionian #5.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | false,
16 | true,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/10_Dorian #4.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/11_Phrygian 3.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/12_Lydian #2.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/13_Locrian b4 bb7.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | false,
16 | true,
17 | true,
18 | false,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/14_Aeolian 6 7 (Melodic Minor).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/15_Phrygian 6.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/16_Lydian #5.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | false,
16 | true,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/17_Lydian b7.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/18_Aeolian 3.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/19_Locrian 2.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/20_Locrian b4.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/21_Bebop Dominant.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | true,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/22_Bebop Major.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/23_Bebop Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | true,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/24_Bebop Melodic Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | true,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/25_Blues Major.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | true,
13 | false,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/26_Blues Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | false,
13 | true,
14 | true,
15 | true,
16 | false,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/27_Blues Diminished.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/28_Blues Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/29_Blues Rock'n'Roll.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | true,
13 | true,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/30_Byzantine.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/31_Hungarian Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | false,
14 | true,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/32_Hungarian Gypsy.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | false,
14 | true,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/33_Spanish Gypsy.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/34_Major Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/35_Neutral Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/36_Rock Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/37_Scottish Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/38_Minor Pentatonic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/39_Whole.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/40_Whole-Half.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | true,
14 | true,
15 | false,
16 | true,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/41_Half-Whole.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/42_Augmented.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | true,
13 | false,
14 | false,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/43_Byzantine.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/44_Chromatic.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | true,
11 | true,
12 | true,
13 | true,
14 | true,
15 | true,
16 | true,
17 | true,
18 | true,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/45_Enigmatic (Ascending).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | false,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/46_Enigmatic (Descending).vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | true,
14 | false,
15 | false,
16 | true,
17 | false,
18 | true,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/47_Hungarian Major.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | false,
11 | true,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/48_Hungarian Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | true,
12 | false,
13 | false,
14 | true,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/49_Neapolitan Major.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | false,
17 | true,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/50_Neapolitan Minor.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | false,
13 | true,
14 | false,
15 | true,
16 | true,
17 | false,
18 | false,
19 | true
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/51_Overtone.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | true,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/52_Prometheus.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | false,
10 | true,
11 | false,
12 | true,
13 | false,
14 | true,
15 | false,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/53_Prometheus Neapolitan.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | false,
12 | true,
13 | false,
14 | true,
15 | false,
16 | false,
17 | true,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/presets/Quantizer/54_Spanish 8 Tone.vcvm:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": "Fundamental",
3 | "model": "Quantizer",
4 | "version": "2.0.0",
5 | "params": [],
6 | "data": {
7 | "enabledNotes": [
8 | true,
9 | true,
10 | false,
11 | true,
12 | true,
13 | true,
14 | true,
15 | false,
16 | true,
17 | false,
18 | true,
19 | false
20 | ]
21 | }
22 | }
--------------------------------------------------------------------------------
/res/Mixer-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/Mult-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/Octave-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/RandomValues-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/VCA-1-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/VCVBezelBig.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
51 |
--------------------------------------------------------------------------------
/res/Viz-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/8vert.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct _8vert : Module {
5 | enum ParamIds {
6 | ENUMS(GAIN_PARAMS, 8),
7 | NUM_PARAMS
8 | };
9 | enum InputIds {
10 | ENUMS(IN_INPUTS, 8),
11 | NUM_INPUTS
12 | };
13 | enum OutputIds {
14 | ENUMS(OUT_OUTPUTS, 8),
15 | NUM_OUTPUTS
16 | };
17 | enum LightIds {
18 | NUM_LIGHTS
19 | };
20 |
21 | dsp::ClockDivider paramDivider;
22 |
23 | _8vert() {
24 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
25 | for (int i = 0; i < 8; i++) {
26 | configParam(GAIN_PARAMS + i, -1.f, 1.f, 0.f, string::f("Row %d gain", i + 1), "%", 0, 100);
27 | configInput(IN_INPUTS + i, string::f("Row %d", i + 1));
28 | configOutput(OUT_OUTPUTS + i, string::f("Row %d", i + 1));
29 | }
30 |
31 | paramDivider.setDivision(2048);
32 | }
33 |
34 | void process(const ProcessArgs& args) override {
35 | float in[16] = {10.f};
36 | int channels = 1;
37 |
38 | for (int i = 0; i < 8; i++) {
39 | // Get input
40 | if (inputs[IN_INPUTS + i].isConnected()) {
41 | channels = inputs[IN_INPUTS + i].getChannels();
42 | inputs[IN_INPUTS + i].readVoltages(in);
43 | }
44 |
45 | if (outputs[OUT_OUTPUTS + i].isConnected()) {
46 | // Apply gain
47 | float out[16];
48 | float gain = params[GAIN_PARAMS + i].getValue();
49 | for (int c = 0; c < channels; c++) {
50 | out[c] = gain * in[c];
51 | }
52 |
53 | // Set output
54 | outputs[OUT_OUTPUTS + i].setChannels(channels);
55 | outputs[OUT_OUTPUTS + i].writeVoltages(out);
56 | }
57 | }
58 |
59 | if (paramDivider.process()) {
60 | refreshParamQuantities();
61 | }
62 | }
63 |
64 | /** Set the gain param units to either V or %, depending on whether a cable is connected. */
65 | void refreshParamQuantities() {
66 | bool normalized = true;
67 |
68 | for (int i = 0; i < 8; i++) {
69 | ParamQuantity* pq = paramQuantities[GAIN_PARAMS + i];
70 | if (!pq)
71 | continue;
72 |
73 | if (inputs[IN_INPUTS + i].isConnected())
74 | normalized = false;
75 | if (normalized) {
76 | pq->unit = "V";
77 | pq->displayMultiplier = 10.f;
78 | }
79 | else {
80 | pq->unit = "%";
81 | pq->displayMultiplier = 100.f;
82 | }
83 | }
84 | }
85 | };
86 |
87 |
88 | struct _8vertWidget : ModuleWidget {
89 | _8vertWidget(_8vert* module) {
90 | setModule(module);
91 | setPanel(createPanel(asset::plugin(pluginInstance, "res/8vert.svg"), asset::plugin(pluginInstance, "res/8vert-dark.svg")));
92 |
93 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
94 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
95 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
96 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
97 |
98 | addParam(createParamCentered(mm2px(Vec(20.351, 21.968)), module, _8vert::GAIN_PARAMS + 0));
99 | addParam(createParamCentered(mm2px(Vec(20.351, 34.982)), module, _8vert::GAIN_PARAMS + 1));
100 | addParam(createParamCentered(mm2px(Vec(20.351, 48.004)), module, _8vert::GAIN_PARAMS + 2));
101 | addParam(createParamCentered(mm2px(Vec(20.351, 61.026)), module, _8vert::GAIN_PARAMS + 3));
102 | addParam(createParamCentered(mm2px(Vec(20.351, 74.048)), module, _8vert::GAIN_PARAMS + 4));
103 | addParam(createParamCentered(mm2px(Vec(20.351, 87.07)), module, _8vert::GAIN_PARAMS + 5));
104 | addParam(createParamCentered(mm2px(Vec(20.351, 100.093)), module, _8vert::GAIN_PARAMS + 6));
105 | addParam(createParamCentered(mm2px(Vec(20.351, 113.115)), module, _8vert::GAIN_PARAMS + 7));
106 |
107 | addInput(createInputCentered(mm2px(Vec(7.331, 21.968)), module, _8vert::IN_INPUTS + 0));
108 | addInput(createInputCentered(mm2px(Vec(7.331, 34.982)), module, _8vert::IN_INPUTS + 1));
109 | addInput(createInputCentered(mm2px(Vec(7.331, 48.004)), module, _8vert::IN_INPUTS + 2));
110 | addInput(createInputCentered(mm2px(Vec(7.331, 61.026)), module, _8vert::IN_INPUTS + 3));
111 | addInput(createInputCentered(mm2px(Vec(7.331, 74.048)), module, _8vert::IN_INPUTS + 4));
112 | addInput(createInputCentered(mm2px(Vec(7.331, 87.07)), module, _8vert::IN_INPUTS + 5));
113 | addInput(createInputCentered(mm2px(Vec(7.331, 100.093)), module, _8vert::IN_INPUTS + 6));
114 | addInput(createInputCentered(mm2px(Vec(7.331, 113.115)), module, _8vert::IN_INPUTS + 7));
115 |
116 | addOutput(createOutputCentered(mm2px(Vec(33.371, 21.968)), module, _8vert::OUT_OUTPUTS + 0));
117 | addOutput(createOutputCentered(mm2px(Vec(33.371, 34.982)), module, _8vert::OUT_OUTPUTS + 1));
118 | addOutput(createOutputCentered(mm2px(Vec(33.371, 48.004)), module, _8vert::OUT_OUTPUTS + 2));
119 | addOutput(createOutputCentered(mm2px(Vec(33.371, 61.026)), module, _8vert::OUT_OUTPUTS + 3));
120 | addOutput(createOutputCentered(mm2px(Vec(33.371, 74.048)), module, _8vert::OUT_OUTPUTS + 4));
121 | addOutput(createOutputCentered(mm2px(Vec(33.371, 87.07)), module, _8vert::OUT_OUTPUTS + 5));
122 | addOutput(createOutputCentered(mm2px(Vec(33.371, 100.093)), module, _8vert::OUT_OUTPUTS + 6));
123 | addOutput(createOutputCentered(mm2px(Vec(33.371, 113.115)), module, _8vert::OUT_OUTPUTS + 7));
124 | }
125 | };
126 |
127 |
128 | Model* model_8vert = createModel<_8vert, _8vertWidget>("8vert");
129 |
--------------------------------------------------------------------------------
/src/CVMix.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | using simd::float_4;
5 |
6 |
7 | struct CVMix : Module {
8 | enum ParamId {
9 | ENUMS(LEVEL_PARAMS, 3),
10 | PARAMS_LEN
11 | };
12 | enum InputId {
13 | ENUMS(CV_INPUTS, 3),
14 | INPUTS_LEN
15 | };
16 | enum OutputId {
17 | MIX_OUTPUT,
18 | OUTPUTS_LEN
19 | };
20 | enum LightId {
21 | LIGHTS_LEN
22 | };
23 |
24 | CVMix() {
25 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
26 | for (int i = 0; i < 3; i++)
27 | configParam(LEVEL_PARAMS + i, -1.f, 1.f, 0.f, string::f("Level %d", i + 1), "%", 0, 100);
28 |
29 | for (int i = 0; i < 3; i++) {
30 | configInput(CV_INPUTS + i, string::f("CV %d", i + 1));
31 | getInputInfo(CV_INPUTS + i)->description = "Normalled to 10 V";
32 | }
33 |
34 | configOutput(MIX_OUTPUT, "Mix");
35 | }
36 |
37 | void process(const ProcessArgs& args) override {
38 | if (!outputs[MIX_OUTPUT].isConnected())
39 | return;
40 |
41 | // Get number of channels
42 | int channels = 1;
43 | for (int i = 0; i < 3; i++)
44 | channels = std::max(channels, inputs[CV_INPUTS + i].getChannels());
45 |
46 | for (int c = 0; c < channels; c += 4) {
47 | // Sum CV inputs
48 | float_4 mix = 0.f;
49 | for (int i = 0; i < 3; i++) {
50 | // Normalize inputs to 10V
51 | float_4 cv = inputs[CV_INPUTS + i].getNormalPolyVoltageSimd(10.f, c);
52 |
53 | // Apply gain
54 | cv *= params[LEVEL_PARAMS + i].getValue();
55 | mix += cv;
56 | }
57 |
58 | // Set mix output
59 | outputs[MIX_OUTPUT].setVoltageSimd(mix, c);
60 | }
61 | outputs[MIX_OUTPUT].setChannels(channels);
62 | }
63 | };
64 |
65 |
66 | struct CVMixWidget : ModuleWidget {
67 | CVMixWidget(CVMix* module) {
68 | setModule(module);
69 | setPanel(createPanel(asset::plugin(pluginInstance, "res/CVMix.svg"), asset::plugin(pluginInstance, "res/CVMix-dark.svg")));
70 |
71 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
72 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
73 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
74 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
75 |
76 | addParam(createParamCentered(mm2px(Vec(7.62, 24.723)), module, CVMix::LEVEL_PARAMS + 0));
77 | addParam(createParamCentered(mm2px(Vec(7.62, 41.327)), module, CVMix::LEVEL_PARAMS + 1));
78 | addParam(createParamCentered(mm2px(Vec(7.62, 57.922)), module, CVMix::LEVEL_PARAMS + 2));
79 |
80 | addInput(createInputCentered(mm2px(Vec(7.62, 76.539)), module, CVMix::CV_INPUTS + 0));
81 | addInput(createInputCentered(mm2px(Vec(7.62, 86.699)), module, CVMix::CV_INPUTS + 1));
82 | addInput(createInputCentered(mm2px(Vec(7.62, 96.859)), module, CVMix::CV_INPUTS + 2));
83 |
84 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, CVMix::MIX_OUTPUT));
85 | }
86 | };
87 |
88 |
89 | Model* modelCVMix = createModel("CVMix");
--------------------------------------------------------------------------------
/src/Compare.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Compare : Module {
5 | enum ParamId {
6 | B_PARAM,
7 | PARAMS_LEN
8 | };
9 | enum InputId {
10 | A_INPUT,
11 | B_INPUT,
12 | INPUTS_LEN
13 | };
14 | enum OutputId {
15 | MAX_OUTPUT,
16 | MIN_OUTPUT,
17 | CLIP_OUTPUT,
18 | LIM_OUTPUT,
19 | CLIPGATE_OUTPUT,
20 | LIMGATE_OUTPUT,
21 | GREATER_OUTPUT,
22 | LESS_OUTPUT,
23 | OUTPUTS_LEN
24 | };
25 | enum LightId {
26 | ENUMS(CLIP_LIGHT, 2),
27 | ENUMS(LIM_LIGHT, 2),
28 | ENUMS(GREATER_LIGHT, 2),
29 | ENUMS(LESS_LIGHT, 2),
30 | LIGHTS_LEN
31 | };
32 |
33 | dsp::ClockDivider lightDivider;
34 |
35 | Compare() {
36 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
37 | configParam(B_PARAM, -10.f, 10.f, 0.f, "B offset", " V");
38 | configInput(A_INPUT, "A");
39 | configInput(B_INPUT, "B");
40 | configOutput(MAX_OUTPUT, "Maximum");
41 | configOutput(MIN_OUTPUT, "Minimum");
42 | configOutput(CLIP_OUTPUT, "Clip");
43 | configOutput(LIM_OUTPUT, "Limit");
44 | configOutput(CLIPGATE_OUTPUT, "Clip gate");
45 | configOutput(LIMGATE_OUTPUT, "Limit gate");
46 | configOutput(GREATER_OUTPUT, "A>B");
47 | configOutput(LESS_OUTPUT, "A b ? 10.f : 0.f, c);
88 | anyGreater = anyGreater || (a > b);
89 | outputs[LESS_OUTPUT].setVoltage(a < b ? 10.f : 0.f, c);
90 | anyLess = anyLess || (a < b);
91 | }
92 |
93 | outputs[MAX_OUTPUT].setChannels(channels);
94 | outputs[MIN_OUTPUT].setChannels(channels);
95 | outputs[CLIP_OUTPUT].setChannels(channels);
96 | outputs[LIM_OUTPUT].setChannels(channels);
97 | outputs[CLIPGATE_OUTPUT].setChannels(channels);
98 | outputs[LIMGATE_OUTPUT].setChannels(channels);
99 | outputs[GREATER_OUTPUT].setChannels(channels);
100 | outputs[LESS_OUTPUT].setChannels(channels);
101 |
102 | if (lightDivider.process()) {
103 | float lightTime = args.sampleTime * lightDivider.getDivision();
104 | lights[CLIP_LIGHT + 0].setBrightnessSmooth(anyClipped && channels <= 1, lightTime);
105 | lights[CLIP_LIGHT + 1].setBrightnessSmooth(anyClipped && channels > 1, lightTime);
106 | lights[LIM_LIGHT + 0].setBrightnessSmooth(anyLimmed && channels <= 1, lightTime);
107 | lights[LIM_LIGHT + 1].setBrightnessSmooth(anyLimmed && channels > 1, lightTime);
108 | lights[GREATER_LIGHT + 0].setBrightnessSmooth(anyGreater && channels <= 1, lightTime);
109 | lights[GREATER_LIGHT + 1].setBrightnessSmooth(anyGreater && channels > 1, lightTime);
110 | lights[LESS_LIGHT + 0].setBrightnessSmooth(anyLess && channels <= 1, lightTime);
111 | lights[LESS_LIGHT + 1].setBrightnessSmooth(anyLess && channels > 1, lightTime);
112 | }
113 | }
114 | };
115 |
116 |
117 | struct CompareWidget : ModuleWidget {
118 | CompareWidget(Compare* module) {
119 | setModule(module);
120 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Compare.svg"), asset::plugin(pluginInstance, "res/Compare-dark.svg")));
121 |
122 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
123 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
124 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
125 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
126 |
127 | addParam(createParamCentered(mm2px(Vec(12.646, 26.755)), module, Compare::B_PARAM));
128 |
129 | addInput(createInputCentered(mm2px(Vec(7.299, 52.31)), module, Compare::A_INPUT));
130 | addInput(createInputCentered(mm2px(Vec(18.136, 52.31)), module, Compare::B_INPUT));
131 |
132 | addOutput(createOutputCentered(mm2px(Vec(7.297, 67.53)), module, Compare::MAX_OUTPUT));
133 | addOutput(createOutputCentered(mm2px(Vec(18.134, 67.53)), module, Compare::MIN_OUTPUT));
134 | addOutput(createOutputCentered(mm2px(Vec(7.297, 82.732)), module, Compare::CLIP_OUTPUT));
135 | addOutput(createOutputCentered(mm2px(Vec(18.134, 82.732)), module, Compare::LIM_OUTPUT));
136 | addOutput(createOutputCentered(mm2px(Vec(7.297, 97.958)), module, Compare::CLIPGATE_OUTPUT));
137 | addOutput(createOutputCentered(mm2px(Vec(18.134, 97.958)), module, Compare::LIMGATE_OUTPUT));
138 | addOutput(createOutputCentered(mm2px(Vec(7.297, 113.115)), module, Compare::GREATER_OUTPUT));
139 | addOutput(createOutputCentered(mm2px(Vec(18.134, 113.115)), module, Compare::LESS_OUTPUT));
140 |
141 | addChild(createLightCentered>>(mm2px(Vec(11.027, 94.233)), module, Compare::CLIP_LIGHT));
142 | addChild(createLightCentered>>(mm2px(Vec(21.864, 94.233)), module, Compare::LIM_LIGHT));
143 | addChild(createLightCentered>>(mm2px(Vec(11.027, 109.393)), module, Compare::GREATER_LIGHT));
144 | addChild(createLightCentered>>(mm2px(Vec(21.864, 109.393)), module, Compare::LESS_LIGHT));
145 | }
146 | };
147 |
148 |
149 | Model* modelCompare = createModel("Compare");
--------------------------------------------------------------------------------
/src/Fade.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | using simd::float_4;
5 |
6 |
7 | struct Fade : Module {
8 | enum ParamId {
9 | CROSSFADE_PARAM,
10 | CROSSFADE_CV_PARAM,
11 | PARAMS_LEN
12 | };
13 | enum InputId {
14 | CROSSFADE_INPUT,
15 | IN1_INPUT,
16 | IN2_INPUT,
17 | INPUTS_LEN
18 | };
19 | enum OutputId {
20 | OUT1_OUTPUT,
21 | OUT2_OUTPUT,
22 | OUTPUTS_LEN
23 | };
24 | enum LightId {
25 | LIGHTS_LEN
26 | };
27 |
28 | /**
29 | 0 linear
30 | 1 -3dB
31 | */
32 | int panLaw = 0;
33 |
34 | Fade() {
35 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
36 | configParam(CROSSFADE_PARAM, 0.f, 1.f, 0.5f, "Crossfade", "%", 0, 100);
37 | configParam(CROSSFADE_CV_PARAM, -1.f, 1.f, 0.f, "Crossfade CV", "%", 0, 100);
38 | configInput(CROSSFADE_INPUT, "Crossfade");
39 | configInput(IN1_INPUT, "Ch 1");
40 | configInput(IN2_INPUT, "Ch 2");
41 | configOutput(OUT1_OUTPUT, "Ch 1");
42 | configOutput(OUT2_OUTPUT, "Ch 2");
43 |
44 | configBypass(IN1_INPUT, OUT1_OUTPUT);
45 | configBypass(IN2_INPUT, OUT2_OUTPUT);
46 | }
47 |
48 | void onReset(const ResetEvent& e) override {
49 | Module::onReset(e);
50 | panLaw = 0;
51 | }
52 |
53 | void process(const ProcessArgs& args) override {
54 | if (!outputs[OUT1_OUTPUT].isConnected() && !outputs[OUT2_OUTPUT].isConnected())
55 | return;
56 |
57 | int channels = std::max({1, inputs[IN1_INPUT].getChannels(), inputs[IN2_INPUT].getChannels()});
58 |
59 | for (int c = 0; c < channels; c += 4) {
60 | // Get crossfade amount
61 | float_4 crossfade = params[CROSSFADE_PARAM].getValue();
62 | crossfade += inputs[CROSSFADE_INPUT].getPolyVoltageSimd(c) / 10.f * params[CROSSFADE_CV_PARAM].getValue();
63 | crossfade = clamp(crossfade, 0.f, 1.f);
64 | float_4 crossfade1 = 1.f - crossfade;
65 |
66 | // Apply sqrt pan law
67 | if (panLaw == 1) {
68 | crossfade = (simd::sqrt(crossfade));
69 | crossfade1 = simd::sqrt(crossfade1);
70 | }
71 |
72 | // Get inputs
73 | float_4 in1 = inputs[IN1_INPUT].getPolyVoltageSimd(c);
74 | float_4 in2 = inputs[IN2_INPUT].getPolyVoltageSimd(c);
75 |
76 | // Calculate outputs
77 | float_4 out1 = crossfade1 * in1 + crossfade * in2;
78 | float_4 out2 = crossfade * in1 + crossfade1 * in2;
79 | outputs[OUT1_OUTPUT].setVoltageSimd(out1, c);
80 | outputs[OUT2_OUTPUT].setVoltageSimd(out2, c);
81 | }
82 |
83 | outputs[OUT1_OUTPUT].setChannels(channels);
84 | outputs[OUT2_OUTPUT].setChannels(channels);
85 | }
86 |
87 | json_t* dataToJson() override {
88 | json_t* rootJ = json_object();
89 | json_object_set_new(rootJ, "panLaw", json_integer(panLaw));
90 | return rootJ;
91 | }
92 |
93 | void dataFromJson(json_t* rootJ) override {
94 | json_t* panLawJ = json_object_get(rootJ, "panLaw");
95 | if (panLawJ)
96 | panLaw = json_integer_value(panLawJ);
97 | }
98 | };
99 |
100 |
101 | struct FadeWidget : ModuleWidget {
102 | FadeWidget(Fade* module) {
103 | setModule(module);
104 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Fade.svg"), asset::plugin(pluginInstance, "res/Fade-dark.svg")));
105 |
106 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
107 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
108 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
109 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
110 |
111 | addParam(createParamCentered(mm2px(Vec(7.62, 24.723)), module, Fade::CROSSFADE_PARAM));
112 | addParam(createParamCentered(mm2px(Vec(7.62, 37.064)), module, Fade::CROSSFADE_CV_PARAM));
113 |
114 | addInput(createInputCentered(mm2px(Vec(7.62, 52.987)), module, Fade::CROSSFADE_INPUT));
115 | addInput(createInputCentered(mm2px(Vec(7.62, 67.53)), module, Fade::IN1_INPUT));
116 | addInput(createInputCentered(mm2px(Vec(7.62, 82.732)), module, Fade::IN2_INPUT));
117 |
118 | addOutput(createOutputCentered(mm2px(Vec(7.62, 97.923)), module, Fade::OUT1_OUTPUT));
119 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Fade::OUT2_OUTPUT));
120 | }
121 |
122 | void appendContextMenu(Menu* menu) override {
123 | Fade* module = getModule();
124 |
125 | menu->addChild(new MenuSeparator);
126 |
127 | menu->addChild(createIndexPtrSubmenuItem("Pan law", {"-6 dB (linear)", "-3 dB"}, &module->panLaw));
128 | }
129 | };
130 |
131 |
132 | Model* modelFade = createModel("Fade");
--------------------------------------------------------------------------------
/src/Logic.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Logic : Module {
5 | enum ParamId {
6 | B_PARAM,
7 | PARAMS_LEN
8 | };
9 | enum InputId {
10 | A_INPUT,
11 | B_INPUT,
12 | INPUTS_LEN
13 | };
14 | enum OutputId {
15 | NOTA_OUTPUT,
16 | NOTB_OUTPUT,
17 | OR_OUTPUT,
18 | NOR_OUTPUT,
19 | AND_OUTPUT,
20 | NAND_OUTPUT,
21 | XOR_OUTPUT,
22 | XNOR_OUTPUT,
23 | OUTPUTS_LEN
24 | };
25 | enum LightId {
26 | B_BUTTON_LIGHT,
27 | ENUMS(NOTA_LIGHT, 2),
28 | ENUMS(NOTB_LIGHT, 2),
29 | ENUMS(OR_LIGHT, 2),
30 | ENUMS(NOR_LIGHT, 2),
31 | ENUMS(AND_LIGHT, 2),
32 | ENUMS(NAND_LIGHT, 2),
33 | ENUMS(XOR_LIGHT, 2),
34 | ENUMS(XNOR_LIGHT, 2),
35 | LIGHTS_LEN
36 | };
37 |
38 | dsp::ClockDivider lightDivider;
39 |
40 | Logic() {
41 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
42 | configButton(B_PARAM, "B");
43 | configInput(A_INPUT, "A");
44 | configInput(B_INPUT, "B");
45 | configOutput(NOTA_OUTPUT, "NOT A");
46 | configOutput(NOTB_OUTPUT, "NOT B");
47 | configOutput(OR_OUTPUT, "OR");
48 | configOutput(NOR_OUTPUT, "NOR");
49 | configOutput(AND_OUTPUT, "AND");
50 | configOutput(NAND_OUTPUT, "NAND");
51 | configOutput(XOR_OUTPUT, "XOR");
52 | configOutput(XNOR_OUTPUT, "XNOR");
53 |
54 | lightDivider.setDivision(32);
55 | }
56 |
57 | void process(const ProcessArgs& args) override {
58 | int channels = std::max({1, inputs[A_INPUT].getChannels(), inputs[B_INPUT].getChannels()});
59 |
60 | bool bPush = params[B_PARAM].getValue() > 0.f;
61 | bool anyState[8] = {};
62 |
63 | for (int c = 0; c < channels; c++) {
64 | bool a = inputs[A_INPUT].getPolyVoltage(c) >= 1.f;
65 | bool b = bPush || inputs[B_INPUT].getPolyVoltage(c) >= 1.f;
66 |
67 | bool states[8] = {
68 | !a, // NOTA
69 | !b, // NOTB
70 | a || b, // OR
71 | !(a || b), // NOR
72 | a && b, // AND
73 | !(a && b), // NAND
74 | a != b, // XOR
75 | a == b, // XNOR
76 | };
77 |
78 | for (int i = 0; i < 8; i++) {
79 | outputs[NOTA_OUTPUT + i].setVoltage(states[i] ? 10.f : 0.f, c);
80 | if (states[i])
81 | anyState[i] = true;
82 | }
83 | }
84 |
85 | for (int i = 0; i < 8; i++) {
86 | outputs[NOTA_OUTPUT + i].setChannels(channels);
87 | }
88 |
89 | // Set lights
90 | if (lightDivider.process()) {
91 | float lightTime = args.sampleTime * lightDivider.getDivision();
92 | lights[B_BUTTON_LIGHT].setBrightness(bPush);
93 | for (int i = 0; i < 8; i++) {
94 | lights[NOTA_LIGHT + 2 * i + 0].setBrightnessSmooth(anyState[i] && channels == 1, lightTime);
95 | lights[NOTA_LIGHT + 2 * i + 1].setBrightnessSmooth(anyState[i] && channels > 1, lightTime);
96 | }
97 | }
98 | }
99 | };
100 |
101 |
102 | struct LogicWidget : ModuleWidget {
103 | LogicWidget(Logic* module) {
104 | setModule(module);
105 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Logic.svg"), asset::plugin(pluginInstance, "res/Logic-dark.svg")));
106 |
107 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
108 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
109 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
110 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
111 |
112 | addParam(createLightParamCentered>>(mm2px(Vec(12.7, 26.755)), module, Logic::B_PARAM, Logic::B_BUTTON_LIGHT));
113 |
114 | addInput(createInputCentered(mm2px(Vec(7.299, 52.31)), module, Logic::A_INPUT));
115 | addInput(createInputCentered(mm2px(Vec(18.136, 52.31)), module, Logic::B_INPUT));
116 |
117 | addOutput(createOutputCentered(mm2px(Vec(7.297, 67.53)), module, Logic::NOTA_OUTPUT));
118 | addOutput(createOutputCentered(mm2px(Vec(18.134, 67.53)), module, Logic::NOTB_OUTPUT));
119 | addOutput(createOutputCentered(mm2px(Vec(7.297, 82.732)), module, Logic::OR_OUTPUT));
120 | addOutput(createOutputCentered(mm2px(Vec(18.134, 82.732)), module, Logic::NOR_OUTPUT));
121 | addOutput(createOutputCentered(mm2px(Vec(7.297, 97.958)), module, Logic::AND_OUTPUT));
122 | addOutput(createOutputCentered(mm2px(Vec(18.134, 97.958)), module, Logic::NAND_OUTPUT));
123 | addOutput(createOutputCentered(mm2px(Vec(7.297, 113.115)), module, Logic::XOR_OUTPUT));
124 | addOutput(createOutputCentered(mm2px(Vec(18.134, 113.115)), module, Logic::XNOR_OUTPUT));
125 |
126 | addChild(createLightCentered>>(mm2px(Vec(11.027, 63.805)), module, Logic::NOTA_LIGHT));
127 | addChild(createLightCentered>>(mm2px(Vec(21.864, 63.805)), module, Logic::NOTB_LIGHT));
128 | addChild(createLightCentered>>(mm2px(Vec(11.027, 79.007)), module, Logic::OR_LIGHT));
129 | addChild(createLightCentered>>(mm2px(Vec(21.864, 79.007)), module, Logic::NOR_LIGHT));
130 | addChild(createLightCentered>>(mm2px(Vec(11.027, 94.233)), module, Logic::AND_LIGHT));
131 | addChild(createLightCentered>>(mm2px(Vec(21.864, 94.233)), module, Logic::NAND_LIGHT));
132 | addChild(createLightCentered>>(mm2px(Vec(11.027, 109.393)), module, Logic::XOR_LIGHT));
133 | addChild(createLightCentered>>(mm2px(Vec(21.864, 109.393)), module, Logic::XNOR_LIGHT));
134 | }
135 | };
136 |
137 |
138 | Model* modelLogic = createModel("Logic");
--------------------------------------------------------------------------------
/src/Merge.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Merge : Module {
5 | enum ParamIds {
6 | NUM_PARAMS
7 | };
8 | enum InputIds {
9 | ENUMS(MONO_INPUTS, 16),
10 | NUM_INPUTS
11 | };
12 | enum OutputIds {
13 | POLY_OUTPUT,
14 | NUM_OUTPUTS
15 | };
16 | enum LightIds {
17 | ENUMS(CHANNEL_LIGHTS, 16),
18 | NUM_LIGHTS
19 | };
20 |
21 | int channels = -1;
22 | int automaticChannels = 0;
23 |
24 | Merge() {
25 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
26 | for (int i = 0; i < 16; i++)
27 | configInput(MONO_INPUTS + i, string::f("Channel %d", i + 1));
28 | configOutput(POLY_OUTPUT, "Polyphonic");
29 |
30 | onReset();
31 | }
32 |
33 | void onReset() override {
34 | channels = -1;
35 | }
36 |
37 | void process(const ProcessArgs& args) override {
38 | int lastChannel = -1;
39 | for (int c = 0; c < 16; c++) {
40 | float v = 0.f;
41 | if (inputs[MONO_INPUTS + c].isConnected()) {
42 | lastChannel = c;
43 | v = inputs[MONO_INPUTS + c].getVoltage();
44 | }
45 | outputs[POLY_OUTPUT].setVoltage(v, c);
46 | }
47 | automaticChannels = lastChannel + 1;
48 |
49 | // In order to allow 0 channels, modify `channels` directly instead of using `setChannels()`
50 | outputs[POLY_OUTPUT].channels = (channels >= 0) ? channels : automaticChannels;
51 | }
52 |
53 | json_t* dataToJson() override {
54 | json_t* rootJ = json_object();
55 | json_object_set_new(rootJ, "channels", json_integer(channels));
56 | return rootJ;
57 | }
58 |
59 | void dataFromJson(json_t* rootJ) override {
60 | json_t* channelsJ = json_object_get(rootJ, "channels");
61 | if (channelsJ)
62 | channels = json_integer_value(channelsJ);
63 | }
64 | };
65 |
66 |
67 | struct MergeChannelDisplay : ChannelDisplay {
68 | Merge* module;
69 | void step() override {
70 | int channels = 16;
71 | if (module) {
72 | channels = module->channels;
73 | if (channels < 0)
74 | channels = module->automaticChannels;
75 | }
76 |
77 | text = string::f("%d", channels);
78 | }
79 | };
80 |
81 |
82 | struct MergeWidget : ModuleWidget {
83 | MergeWidget(Merge* module) {
84 | setModule(module);
85 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Merge.svg"), asset::plugin(pluginInstance, "res/Merge-dark.svg")));
86 |
87 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
88 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
89 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
90 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
91 |
92 | addInput(createInputCentered(mm2px(Vec(7.281, 41.995)), module, Merge::MONO_INPUTS + 0));
93 | addInput(createInputCentered(mm2px(Vec(7.281, 52.155)), module, Merge::MONO_INPUTS + 1));
94 | addInput(createInputCentered(mm2px(Vec(7.281, 62.315)), module, Merge::MONO_INPUTS + 2));
95 | addInput(createInputCentered(mm2px(Vec(7.281, 72.475)), module, Merge::MONO_INPUTS + 3));
96 | addInput(createInputCentered(mm2px(Vec(7.281, 82.635)), module, Merge::MONO_INPUTS + 4));
97 | addInput(createInputCentered(mm2px(Vec(7.281, 92.795)), module, Merge::MONO_INPUTS + 5));
98 | addInput(createInputCentered(mm2px(Vec(7.281, 102.955)), module, Merge::MONO_INPUTS + 6));
99 | addInput(createInputCentered(mm2px(Vec(7.281, 113.115)), module, Merge::MONO_INPUTS + 7));
100 | addInput(createInputCentered(mm2px(Vec(18.119, 41.995)), module, Merge::MONO_INPUTS + 8));
101 | addInput(createInputCentered(mm2px(Vec(18.119, 52.155)), module, Merge::MONO_INPUTS + 9));
102 | addInput(createInputCentered(mm2px(Vec(18.119, 62.315)), module, Merge::MONO_INPUTS + 10));
103 | addInput(createInputCentered(mm2px(Vec(18.119, 72.475)), module, Merge::MONO_INPUTS + 11));
104 | addInput(createInputCentered(mm2px(Vec(18.119, 82.635)), module, Merge::MONO_INPUTS + 12));
105 | addInput(createInputCentered(mm2px(Vec(18.119, 92.795)), module, Merge::MONO_INPUTS + 13));
106 | addInput(createInputCentered(mm2px(Vec(18.119, 102.955)), module, Merge::MONO_INPUTS + 14));
107 | addInput(createInputCentered(mm2px(Vec(18.119, 113.115)), module, Merge::MONO_INPUTS + 15));
108 |
109 | addOutput(createOutputCentered(mm2px(Vec(7.281, 21.967)), module, Merge::POLY_OUTPUT));
110 |
111 | MergeChannelDisplay* display = createWidget(mm2px(Vec(14.02, 18.611)));
112 | display->box.size = mm2px(Vec(8.197, 8.197));
113 | display->module = module;
114 | addChild(display);
115 | }
116 |
117 | void appendContextMenu(Menu* menu) override {
118 | Merge* module = dynamic_cast(this->module);
119 |
120 | menu->addChild(new MenuSeparator);
121 |
122 | std::vector channelLabels;
123 | channelLabels.push_back(string::f("Automatic (%d)", module->automaticChannels));
124 | for (int i = 0; i <= 16; i++) {
125 | channelLabels.push_back(string::f("%d", i));
126 | }
127 | menu->addChild(createIndexSubmenuItem("Channels", channelLabels,
128 | [=]() {return module->channels + 1;},
129 | [=](int i) {module->channels = i - 1;}
130 | ));
131 | }
132 | };
133 |
134 |
135 | Model* modelMerge = createModel("Merge");
136 |
--------------------------------------------------------------------------------
/src/MidSide.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct MidSide : Module {
5 | enum ParamIds {
6 | ENC_WIDTH_PARAM,
7 | DEC_WIDTH_PARAM,
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | ENC_WIDTH_INPUT,
12 | ENC_LEFT_INPUT,
13 | ENC_RIGHT_INPUT,
14 | DEC_WIDTH_INPUT,
15 | DEC_MID_INPUT,
16 | DEC_SIDE_INPUT,
17 | NUM_INPUTS
18 | };
19 | enum OutputIds {
20 | ENC_MID_OUTPUT,
21 | ENC_SIDE_OUTPUT,
22 | DEC_LEFT_OUTPUT,
23 | DEC_RIGHT_OUTPUT,
24 | NUM_OUTPUTS
25 | };
26 | enum LightIds {
27 | NUM_LIGHTS
28 | };
29 |
30 | MidSide() {
31 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
32 | configParam(ENC_WIDTH_PARAM, 0.f, 2.f, 1.f, "Encoder width", "%", 0, 100);
33 | configParam(DEC_WIDTH_PARAM, 0.f, 2.f, 1.f, "Decoder width", "%", 0, 100);
34 | configInput(ENC_WIDTH_INPUT, "Encoder width");
35 | configInput(ENC_LEFT_INPUT, "Encoder left");
36 | configInput(ENC_RIGHT_INPUT, "Encoder right");
37 | configInput(DEC_WIDTH_INPUT, "Decoder width");
38 | configInput(DEC_MID_INPUT, "Decoder mid");
39 | configInput(DEC_SIDE_INPUT, "Decoder side");
40 | configOutput(ENC_MID_OUTPUT, "Encoder mid");
41 | configOutput(ENC_SIDE_OUTPUT, "Encoder side");
42 | configOutput(DEC_LEFT_OUTPUT, "Decoder left");
43 | configOutput(DEC_RIGHT_OUTPUT, "Decoder right");
44 | }
45 |
46 | void process(const ProcessArgs& args) override {
47 | using simd::float_4;
48 |
49 | // Encoder
50 | int channels = std::max(inputs[ENC_LEFT_INPUT].getChannels(), inputs[ENC_RIGHT_INPUT].getChannels());
51 |
52 | outputs[ENC_MID_OUTPUT].setChannels(channels);
53 | outputs[ENC_SIDE_OUTPUT].setChannels(channels);
54 |
55 | for (int c = 0; c < channels; c += 4) {
56 | float_4 width = params[ENC_WIDTH_PARAM].getValue();
57 | width += inputs[ENC_WIDTH_INPUT].getPolyVoltageSimd(c) / 10 * 2;
58 | width = simd::fmax(width, 0.f);
59 | float_4 left = inputs[ENC_LEFT_INPUT].getVoltageSimd(c);
60 | float_4 right = inputs[ENC_RIGHT_INPUT].getVoltageSimd(c);
61 | float_4 mid = (left + right) / 2;
62 | float_4 side = (left - right) / 2 * width;
63 | outputs[ENC_MID_OUTPUT].setVoltageSimd(mid, c);
64 | outputs[ENC_SIDE_OUTPUT].setVoltageSimd(side, c);
65 | }
66 |
67 | // Decoder
68 | if (inputs[DEC_MID_INPUT].isConnected() || inputs[DEC_SIDE_INPUT].isConnected())
69 | channels = std::max(inputs[DEC_MID_INPUT].getChannels(), inputs[DEC_SIDE_INPUT].getChannels());
70 |
71 | outputs[DEC_LEFT_OUTPUT].setChannels(channels);
72 | outputs[DEC_RIGHT_OUTPUT].setChannels(channels);
73 |
74 | for (int c = 0; c < channels; c += 4) {
75 | float_4 width = params[DEC_WIDTH_PARAM].getValue();
76 | width += inputs[DEC_WIDTH_INPUT].getPolyVoltageSimd(c) / 10 * 2;
77 | width = simd::fmax(width, 0.f);
78 | float_4 mid = outputs[ENC_MID_OUTPUT].getVoltageSimd(c);
79 | float_4 side = outputs[ENC_SIDE_OUTPUT].getVoltageSimd(c);
80 | mid = inputs[DEC_MID_INPUT].getNormalVoltageSimd(mid, c);
81 | side = inputs[DEC_SIDE_INPUT].getNormalVoltageSimd(side, c);
82 | float_4 left = mid + side * width;
83 | float_4 right = mid - side * width;
84 | outputs[DEC_LEFT_OUTPUT].setVoltageSimd(left, c);
85 | outputs[DEC_RIGHT_OUTPUT].setVoltageSimd(right, c);
86 | }
87 | }
88 | };
89 |
90 |
91 | struct MidSideWidget : ModuleWidget {
92 | MidSideWidget(MidSide* module) {
93 | setModule(module);
94 | setPanel(createPanel(asset::plugin(pluginInstance, "res/MidSide.svg"), asset::plugin(pluginInstance, "res/MidSide-dark.svg")));
95 |
96 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
97 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
98 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
99 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
100 |
101 | addParam(createParamCentered(mm2px(Vec(7.285, 25.203)), module, MidSide::ENC_WIDTH_PARAM));
102 | addParam(createParamCentered(mm2px(Vec(7.285, 80.583)), module, MidSide::DEC_WIDTH_PARAM));
103 |
104 | addInput(createInputCentered(mm2px(Vec(18.122, 25.142)), module, MidSide::ENC_WIDTH_INPUT));
105 | addInput(createInputCentered(mm2px(Vec(7.285, 41.373)), module, MidSide::ENC_LEFT_INPUT));
106 | addInput(createInputCentered(mm2px(Vec(18.122, 41.373)), module, MidSide::ENC_RIGHT_INPUT));
107 | addInput(createInputCentered(mm2px(Vec(18.122, 80.603)), module, MidSide::DEC_WIDTH_INPUT));
108 | addInput(createInputCentered(mm2px(Vec(7.285, 96.859)), module, MidSide::DEC_MID_INPUT));
109 | addInput(createInputCentered(mm2px(Vec(18.122, 96.859)), module, MidSide::DEC_SIDE_INPUT));
110 |
111 | addOutput(createOutputCentered(mm2px(Vec(7.285, 57.679)), module, MidSide::ENC_MID_OUTPUT));
112 | addOutput(createOutputCentered(mm2px(Vec(18.122, 57.679)), module, MidSide::ENC_SIDE_OUTPUT));
113 | addOutput(createOutputCentered(mm2px(Vec(7.285, 113.115)), module, MidSide::DEC_LEFT_OUTPUT));
114 | addOutput(createOutputCentered(mm2px(Vec(18.122, 113.115)), module, MidSide::DEC_RIGHT_OUTPUT));
115 | }
116 | };
117 |
118 |
119 | Model* modelMidSide = createModel("MidSide");
--------------------------------------------------------------------------------
/src/Mixer.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | using simd::float_4;
5 |
6 |
7 | struct Mixer : Module {
8 | enum ParamId {
9 | LEVEL_PARAM,
10 | PARAMS_LEN
11 | };
12 | enum InputId {
13 | ENUMS(IN_INPUTS, 6),
14 | INPUTS_LEN
15 | };
16 | enum OutputId {
17 | OUT_OUTPUT,
18 | OUTPUTS_LEN
19 | };
20 | enum LightId {
21 | LIGHTS_LEN
22 | };
23 |
24 | bool invert = false;
25 | bool average = false;
26 |
27 | Mixer() {
28 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
29 | configParam(LEVEL_PARAM, 0.f, 1.f, 1.f, "Level", "%", 0, 100);
30 | for (int i = 0; i < 6; i++)
31 | configInput(IN_INPUTS + i, string::f("Channel %d", i + 1));
32 | configOutput(OUT_OUTPUT, "Mix");
33 | }
34 |
35 | void process(const ProcessArgs& args) override {
36 | // Get number of channels and number of connected inputs
37 | int channels = 1;
38 | int connected = 0;
39 | for (int i = 0; i < 6; i++) {
40 | channels = std::max(channels, inputs[IN_INPUTS + i].getChannels());
41 | if (inputs[IN_INPUTS + i].isConnected())
42 | connected++;
43 | }
44 |
45 | float gain = params[LEVEL_PARAM].getValue();
46 | // Invert
47 | if (invert) {
48 | gain *= -1.f;
49 | }
50 | // Average
51 | if (average) {
52 | gain /= std::max(1, connected);
53 | }
54 |
55 | // Iterate polyphonic channels
56 | for (int c = 0; c < channels; c += 4) {
57 | float_4 out = 0.f;
58 | // Mix input
59 | for (int i = 0; i < 6; i++) {
60 | out += inputs[IN_INPUTS + i].getVoltageSimd(c);
61 | }
62 |
63 | // Apply gain
64 | out *= gain;
65 |
66 | // Set output
67 | outputs[OUT_OUTPUT].setVoltageSimd(out, c);
68 | }
69 |
70 | outputs[OUT_OUTPUT].setChannels(channels);
71 | }
72 |
73 | json_t* dataToJson() override {
74 | json_t* rootJ = json_object();
75 | // average
76 | json_object_set_new(rootJ, "average", json_boolean(average));
77 | // invert
78 | json_object_set_new(rootJ, "invert", json_boolean(invert));
79 | return rootJ;
80 | }
81 |
82 | void dataFromJson(json_t* rootJ) override {
83 | // average
84 | json_t* averageJ = json_object_get(rootJ, "average");
85 | if (averageJ)
86 | average = json_boolean_value(averageJ);
87 | // invert
88 | json_t* invertJ = json_object_get(rootJ, "invert");
89 | if (invertJ)
90 | invert = json_boolean_value(invertJ);
91 | }
92 | };
93 |
94 |
95 | struct MixerWidget : ModuleWidget {
96 | MixerWidget(Mixer* module) {
97 | setModule(module);
98 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Mixer.svg"), asset::plugin(pluginInstance, "res/Mixer-dark.svg")));
99 |
100 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
101 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
102 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
103 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
104 |
105 | addParam(createParamCentered(mm2px(Vec(7.62, 24.723)), module, Mixer::LEVEL_PARAM));
106 |
107 | addInput(createInputCentered(mm2px(Vec(7.62, 46.059)), module, Mixer::IN_INPUTS + 0));
108 | addInput(createInputCentered(mm2px(Vec(7.62, 56.219)), module, Mixer::IN_INPUTS + 1));
109 | addInput(createInputCentered(mm2px(Vec(7.62, 66.379)), module, Mixer::IN_INPUTS + 2));
110 | addInput(createInputCentered(mm2px(Vec(7.62, 76.539)), module, Mixer::IN_INPUTS + 3));
111 | addInput(createInputCentered(mm2px(Vec(7.62, 86.699)), module, Mixer::IN_INPUTS + 4));
112 | addInput(createInputCentered(mm2px(Vec(7.62, 96.859)), module, Mixer::IN_INPUTS + 5));
113 |
114 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Mixer::OUT_OUTPUT));
115 | }
116 |
117 | void appendContextMenu(Menu* menu) override {
118 | Mixer* module = dynamic_cast(this->module);
119 | assert(module);
120 |
121 | menu->addChild(new MenuSeparator);
122 |
123 | menu->addChild(createBoolPtrMenuItem("Invert output", "", &module->invert));
124 | menu->addChild(createBoolPtrMenuItem("Average voltages", "", &module->average));
125 | }
126 | };
127 |
128 |
129 | Model* modelMixer = createModel("Mixer");
--------------------------------------------------------------------------------
/src/Mult.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Mult : Module {
5 | enum ParamId {
6 | PARAMS_LEN
7 | };
8 | enum InputId {
9 | MULT_INPUT,
10 | INPUTS_LEN
11 | };
12 | enum OutputId {
13 | ENUMS(MULT_OUTPUTS, 8),
14 | OUTPUTS_LEN
15 | };
16 | enum LightId {
17 | LIGHTS_LEN
18 | };
19 |
20 | Mult() {
21 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
22 | configInput(MULT_INPUT, "Mult");
23 | for (int i = 0; i < 8; i++)
24 | configOutput(MULT_OUTPUTS + i, string::f("Mult %d", i + 1));
25 | }
26 |
27 | void process(const ProcessArgs& args) override {
28 | int channels = std::max(1, inputs[MULT_INPUT].getChannels());
29 |
30 | // Copy input to outputs
31 | for (int i = 0; i < 8; i++) {
32 | outputs[MULT_OUTPUTS + i].setChannels(channels);
33 | outputs[MULT_OUTPUTS + i].writeVoltages(inputs[MULT_INPUT].getVoltages());
34 | }
35 | }
36 | };
37 |
38 |
39 | struct MultWidget : ModuleWidget {
40 | MultWidget(Mult* module) {
41 | setModule(module);
42 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Mult.svg"), asset::plugin(pluginInstance, "res/Mult-dark.svg")));
43 |
44 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
45 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
46 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
47 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
48 |
49 | addInput(createInputCentered(mm2px(Vec(7.62, 22.001)), module, Mult::MULT_INPUT));
50 |
51 | addOutput(createOutputCentered(mm2px(Vec(7.62, 42.017)), module, Mult::MULT_OUTPUTS + 0));
52 | addOutput(createOutputCentered(mm2px(Vec(7.62, 52.155)), module, Mult::MULT_OUTPUTS + 1));
53 | addOutput(createOutputCentered(mm2px(Vec(7.62, 62.315)), module, Mult::MULT_OUTPUTS + 2));
54 | addOutput(createOutputCentered(mm2px(Vec(7.62, 72.475)), module, Mult::MULT_OUTPUTS + 3));
55 | addOutput(createOutputCentered(mm2px(Vec(7.62, 82.635)), module, Mult::MULT_OUTPUTS + 4));
56 | addOutput(createOutputCentered(mm2px(Vec(7.62, 92.795)), module, Mult::MULT_OUTPUTS + 5));
57 | addOutput(createOutputCentered(mm2px(Vec(7.62, 102.955)), module, Mult::MULT_OUTPUTS + 6));
58 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Mult::MULT_OUTPUTS + 7));
59 | }
60 | };
61 |
62 |
63 | Model* modelMult = createModel("Mult");
--------------------------------------------------------------------------------
/src/Mutes.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Mutes : Module {
5 | enum ParamIds {
6 | ENUMS(MUTE_PARAMS, 10),
7 | NUM_PARAMS
8 | };
9 | enum InputIds {
10 | ENUMS(IN_INPUTS, 10),
11 | NUM_INPUTS
12 | };
13 | enum OutputIds {
14 | ENUMS(OUT_OUTPUTS, 10),
15 | NUM_OUTPUTS
16 | };
17 | enum LightIds {
18 | ENUMS(MUTE_LIGHTS, 10),
19 | NUM_LIGHTS
20 | };
21 |
22 | Mutes() {
23 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
24 | for (int i = 0; i < 10; i++) {
25 | configSwitch(MUTE_PARAMS + i, 0.f, 1.f, 0.f, string::f("Row %d mute", i + 1));
26 | configInput(IN_INPUTS + i, string::f("Row %d", i + 1));
27 | configOutput(OUT_OUTPUTS + i, string::f("Row %d", i + 1));
28 | configBypass(IN_INPUTS + i, OUT_OUTPUTS + i);
29 | }
30 | }
31 |
32 | void process(const ProcessArgs& args) override {
33 | const float zero[16] = {};
34 | float out[16] = {};
35 |
36 | // Iterate rows
37 | for (int i = 0; i < 10; i++) {
38 | int channels = 1;
39 | bool mute = params[MUTE_PARAMS + i].getValue() > 0.f;
40 |
41 | // Get input
42 | // Inputs are normalized to the input above it, so only set if connected
43 | if (inputs[IN_INPUTS + i].isConnected()) {
44 | channels = inputs[IN_INPUTS + i].getChannels();
45 | inputs[IN_INPUTS + i].readVoltages(out);
46 | }
47 |
48 | // Set output
49 | if (outputs[OUT_OUTPUTS + i].isConnected()) {
50 | outputs[OUT_OUTPUTS + i].setChannels(channels);
51 | outputs[OUT_OUTPUTS + i].writeVoltages(mute ? zero : out);
52 | }
53 |
54 | // Set light
55 | lights[MUTE_LIGHTS + i].setBrightness(mute);
56 | }
57 | }
58 |
59 | void dataFromJson(json_t* rootJ) override {
60 | // In <2.0, states were stored in data
61 | json_t* statesJ = json_object_get(rootJ, "states");
62 | if (statesJ) {
63 | for (int i = 0; i < 10; i++) {
64 | json_t* stateJ = json_array_get(statesJ, i);
65 | if (stateJ)
66 | params[MUTE_PARAMS + i].setValue(!json_boolean_value(stateJ));
67 | }
68 | }
69 | }
70 |
71 | void invert() {
72 | for (int i = 0; i < 10; i++) {
73 | params[MUTE_PARAMS + i].setValue(!params[MUTE_PARAMS + i].getValue());
74 | }
75 | }
76 | };
77 |
78 |
79 | struct MutesWidget : ModuleWidget {
80 | MutesWidget(Mutes* module) {
81 | setModule(module);
82 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Mutes.svg"), asset::plugin(pluginInstance, "res/Mutes-dark.svg")));
83 |
84 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
85 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
86 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
87 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
88 |
89 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 21.968)), module, Mutes::MUTE_PARAMS + 0, Mutes::MUTE_LIGHTS + 0));
90 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 32.095)), module, Mutes::MUTE_PARAMS + 1, Mutes::MUTE_LIGHTS + 1));
91 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 42.222)), module, Mutes::MUTE_PARAMS + 2, Mutes::MUTE_LIGHTS + 2));
92 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 52.35)), module, Mutes::MUTE_PARAMS + 3, Mutes::MUTE_LIGHTS + 3));
93 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 62.477)), module, Mutes::MUTE_PARAMS + 4, Mutes::MUTE_LIGHTS + 4));
94 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 72.605)), module, Mutes::MUTE_PARAMS + 5, Mutes::MUTE_LIGHTS + 5));
95 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 82.732)), module, Mutes::MUTE_PARAMS + 6, Mutes::MUTE_LIGHTS + 6));
96 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 92.86)), module, Mutes::MUTE_PARAMS + 7, Mutes::MUTE_LIGHTS + 7));
97 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 102.987)), module, Mutes::MUTE_PARAMS + 8, Mutes::MUTE_LIGHTS + 8));
98 | addParam(createLightParamCentered>>(mm2px(Vec(20.312, 113.115)), module, Mutes::MUTE_PARAMS + 9, Mutes::MUTE_LIGHTS + 9));
99 |
100 | addInput(createInputCentered(mm2px(Vec(7.291, 21.968)), module, Mutes::IN_INPUTS + 0));
101 | addInput(createInputCentered(mm2px(Vec(7.291, 32.095)), module, Mutes::IN_INPUTS + 1));
102 | addInput(createInputCentered(mm2px(Vec(7.291, 42.222)), module, Mutes::IN_INPUTS + 2));
103 | addInput(createInputCentered(mm2px(Vec(7.291, 52.35)), module, Mutes::IN_INPUTS + 3));
104 | addInput(createInputCentered(mm2px(Vec(7.291, 62.477)), module, Mutes::IN_INPUTS + 4));
105 | addInput(createInputCentered(mm2px(Vec(7.291, 72.605)), module, Mutes::IN_INPUTS + 5));
106 | addInput(createInputCentered(mm2px(Vec(7.291, 82.732)), module, Mutes::IN_INPUTS + 6));
107 | addInput(createInputCentered(mm2px(Vec(7.291, 92.86)), module, Mutes::IN_INPUTS + 7));
108 | addInput(createInputCentered(mm2px(Vec(7.291, 102.987)), module, Mutes::IN_INPUTS + 8));
109 | addInput(createInputCentered(mm2px(Vec(7.291, 113.115)), module, Mutes::IN_INPUTS + 9));
110 |
111 | addOutput(createOutputCentered(mm2px(Vec(33.332, 21.968)), module, Mutes::OUT_OUTPUTS + 0));
112 | addOutput(createOutputCentered(mm2px(Vec(33.332, 32.095)), module, Mutes::OUT_OUTPUTS + 1));
113 | addOutput(createOutputCentered(mm2px(Vec(33.332, 42.222)), module, Mutes::OUT_OUTPUTS + 2));
114 | addOutput(createOutputCentered(mm2px(Vec(33.332, 52.35)), module, Mutes::OUT_OUTPUTS + 3));
115 | addOutput(createOutputCentered(mm2px(Vec(33.332, 62.477)), module, Mutes::OUT_OUTPUTS + 4));
116 | addOutput(createOutputCentered(mm2px(Vec(33.332, 72.605)), module, Mutes::OUT_OUTPUTS + 5));
117 | addOutput(createOutputCentered(mm2px(Vec(33.332, 82.732)), module, Mutes::OUT_OUTPUTS + 6));
118 | addOutput(createOutputCentered(mm2px(Vec(33.332, 92.86)), module, Mutes::OUT_OUTPUTS + 7));
119 | addOutput(createOutputCentered(mm2px(Vec(33.332, 102.987)), module, Mutes::OUT_OUTPUTS + 8));
120 | addOutput(createOutputCentered(mm2px(Vec(33.332, 113.115)), module, Mutes::OUT_OUTPUTS + 9));
121 | }
122 |
123 | void appendContextMenu(Menu* menu) override {
124 | Mutes* module = dynamic_cast(this->module);
125 | assert(module);
126 |
127 | menu->addChild(new MenuSeparator);
128 |
129 | menu->addChild(createMenuItem("Invert mutes", "",
130 | [=]() {module->invert();}
131 | ));
132 | }
133 | };
134 |
135 |
136 | Model* modelMutes = createModel("Mutes");
137 |
--------------------------------------------------------------------------------
/src/Noise.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | /** Based on "The Voss algorithm"
5 | http://www.firstpr.com.au/dsp/pink-noise/
6 | */
7 | template
8 | struct PinkNoiseGenerator {
9 | int frame = -1;
10 | float values[QUALITY] = {};
11 |
12 | float process() {
13 | int lastFrame = frame;
14 | frame++;
15 | if (frame >= (1 << QUALITY))
16 | frame = 0;
17 | int diff = lastFrame ^ frame;
18 |
19 | float sum = 0.f;
20 | for (int i = 0; i < QUALITY; i++) {
21 | if (diff & (1 << i)) {
22 | values[i] = random::uniform() - 0.5f;
23 | }
24 | sum += values[i];
25 | }
26 | return sum;
27 | }
28 | };
29 |
30 |
31 | struct InverseAWeightingFFTFilter {
32 | static constexpr int BUFFER_LEN = 1024;
33 |
34 | alignas(16) float inputBuffer[BUFFER_LEN] = {};
35 | alignas(16) float outputBuffer[BUFFER_LEN] = {};
36 | int frame = 0;
37 | dsp::RealFFT fft;
38 |
39 | InverseAWeightingFFTFilter() : fft(BUFFER_LEN) {}
40 |
41 | float process(float deltaTime, float x) {
42 | inputBuffer[frame] = x;
43 | if (++frame >= BUFFER_LEN) {
44 | frame = 0;
45 | alignas(16) float freqBuffer[BUFFER_LEN * 2];
46 | fft.rfft(inputBuffer, freqBuffer);
47 |
48 | for (int i = 0; i < BUFFER_LEN; i++) {
49 | float f = 1 / deltaTime / 2 / BUFFER_LEN * i;
50 | float amp = 0.f;
51 | if (80.f <= f && f <= 20000.f) {
52 | float f2 = f * f;
53 | // Inverse A-weighted curve
54 | amp = ((424.36f + f2) * std::sqrt((11599.3f + f2) * (544496.f + f2)) * (148693636.f + f2)) / (148693636.f * f2 * f2);
55 | }
56 | freqBuffer[2 * i + 0] *= amp / BUFFER_LEN;
57 | freqBuffer[2 * i + 1] *= amp / BUFFER_LEN;
58 | }
59 |
60 | fft.irfft(freqBuffer, outputBuffer);
61 | }
62 | return outputBuffer[frame];
63 | }
64 | };
65 |
66 |
67 |
68 |
69 | struct Noise : Module {
70 | enum ParamIds {
71 | NUM_PARAMS
72 | };
73 | enum InputIds {
74 | NUM_INPUTS
75 | };
76 | enum OutputIds {
77 | WHITE_OUTPUT,
78 | PINK_OUTPUT,
79 | RED_OUTPUT,
80 | VIOLET_OUTPUT,
81 | BLUE_OUTPUT,
82 | GRAY_OUTPUT,
83 | BLACK_OUTPUT,
84 | NUM_OUTPUTS
85 | };
86 | enum LightIds {
87 | NUM_LIGHTS
88 | };
89 |
90 | dsp::ClockDivider blackDivider;
91 | PinkNoiseGenerator<8> pinkNoiseGenerator;
92 | dsp::IIRFilter<2, 2> redFilter;
93 | float lastWhite = 0.f;
94 | float lastPink = 0.f;
95 | InverseAWeightingFFTFilter grayFilter;
96 |
97 | // For calibrating levels
98 | // float meanSqr = 0.f;
99 |
100 | Noise() {
101 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
102 | configOutput(WHITE_OUTPUT, "White noise");
103 | outputInfos[WHITE_OUTPUT]->description = "0 dB/octave power density";
104 | configOutput(PINK_OUTPUT, "Pink noise");
105 | outputInfos[PINK_OUTPUT]->description = "-3 dB/octave power density";
106 | configOutput(RED_OUTPUT, "Red noise");
107 | outputInfos[RED_OUTPUT]->description = "-6 dB/octave power density";
108 | configOutput(VIOLET_OUTPUT, "Violet noise");
109 | outputInfos[VIOLET_OUTPUT]->description = "+6 dB/octave power density";
110 | configOutput(BLUE_OUTPUT, "Blue noise");
111 | outputInfos[BLUE_OUTPUT]->description = "+3 dB/octave power density";
112 | configOutput(GRAY_OUTPUT, "Gray noise");
113 | outputInfos[GRAY_OUTPUT]->description = "Psychoacoustic equal loudness";
114 | configOutput(BLACK_OUTPUT, "Black noise");
115 | outputInfos[BLACK_OUTPUT]->description = "Uniform random numbers";
116 |
117 | // Hard-code coefficients for Butterworth lowpass with cutoff 20 Hz @ 44.1kHz.
118 | const float b[] = {0.00425611, 0.00425611};
119 | const float a[] = {-0.99148778};
120 | redFilter.setCoefficients(b, a);
121 | }
122 |
123 | void process(const ProcessArgs& args) override {
124 | // All noise is calibrated to 1 RMS.
125 | // Then they should be scaled to match the RMS of a sine wave with 5V amplitude.
126 | const float gain = 5.f / std::sqrt(2.f);
127 |
128 | if (outputs[WHITE_OUTPUT].isConnected() || outputs[RED_OUTPUT].isConnected() || outputs[VIOLET_OUTPUT].isConnected() || outputs[GRAY_OUTPUT].isConnected()) {
129 | // White noise: equal power density
130 | float white = random::normal();
131 | outputs[WHITE_OUTPUT].setVoltage(white * gain);
132 |
133 | // Red/Brownian noise: -6dB/oct
134 | if (outputs[RED_OUTPUT].isConnected()) {
135 | float red = redFilter.process(white) / 0.0645f;
136 | outputs[RED_OUTPUT].setVoltage(red * gain);
137 | }
138 |
139 | // Violet/purple noise: 6dB/oct
140 | if (outputs[VIOLET_OUTPUT].isConnected()) {
141 | float violet = (white - lastWhite) / 1.41f;
142 | lastWhite = white;
143 | outputs[VIOLET_OUTPUT].setVoltage(violet * gain);
144 | }
145 |
146 | // Gray noise: psychoacoustic equal loudness curve, specifically inverted A-weighted
147 | if (outputs[GRAY_OUTPUT].isConnected()) {
148 | float gray = grayFilter.process(args.sampleTime, white) / 1.67f;
149 | outputs[GRAY_OUTPUT].setVoltage(gray * gain);
150 | }
151 | }
152 |
153 | if (outputs[PINK_OUTPUT].isConnected() || outputs[BLUE_OUTPUT].isConnected()) {
154 | // Pink noise: -3dB/oct
155 | float pink = pinkNoiseGenerator.process() / 0.816f;
156 | outputs[PINK_OUTPUT].setVoltage(pink * gain);
157 |
158 | // Blue noise: 3dB/oct
159 | if (outputs[BLUE_OUTPUT].isConnected()) {
160 | float blue = (pink - lastPink) / 0.705f;
161 | lastPink = pink;
162 | outputs[BLUE_OUTPUT].setVoltage(blue * gain);
163 | }
164 | }
165 |
166 | // Black noise: uniform noise
167 | // Note: I made this definition up.
168 | if (outputs[BLACK_OUTPUT].isConnected()) {
169 | float u = random::uniform();
170 | outputs[BLACK_OUTPUT].setVoltage(u * 10.f - 5.f);
171 | }
172 |
173 | // meanSqr += (std::pow(gray, 2.f) - meanSqr) * args.sampleTime / 10.f;
174 | // DEBUG("%f", std::sqrt(meanSqr));
175 | }
176 | };
177 |
178 |
179 | struct NoiseWidget : ModuleWidget {
180 | NoiseWidget(Noise* module) {
181 | setModule(module);
182 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Noise.svg"), asset::plugin(pluginInstance, "res/Noise-dark.svg")));
183 |
184 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
185 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
186 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
187 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
188 |
189 | addOutput(createOutputCentered(mm2px(Vec(7.62, 21.897)), module, Noise::WHITE_OUTPUT));
190 | addOutput(createOutputCentered(mm2px(Vec(7.62, 37.102)), module, Noise::PINK_OUTPUT));
191 | addOutput(createOutputCentered(mm2px(Vec(7.62, 52.31)), module, Noise::RED_OUTPUT));
192 | addOutput(createOutputCentered(mm2px(Vec(7.62, 67.53)), module, Noise::VIOLET_OUTPUT));
193 | addOutput(createOutputCentered(mm2px(Vec(7.62, 82.732)), module, Noise::BLUE_OUTPUT));
194 | addOutput(createOutputCentered(mm2px(Vec(7.62, 97.923)), module, Noise::GRAY_OUTPUT));
195 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Noise::BLACK_OUTPUT));
196 | }
197 | };
198 |
199 |
200 | Model* modelNoise = createModel("Noise");
--------------------------------------------------------------------------------
/src/Octave.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct Octave : Module {
5 | enum ParamIds {
6 | OCTAVE_PARAM,
7 | NUM_PARAMS
8 | };
9 | enum InputIds {
10 | PITCH_INPUT,
11 | OCTAVE_INPUT,
12 | NUM_INPUTS
13 | };
14 | enum OutputIds {
15 | PITCH_OUTPUT,
16 | NUM_OUTPUTS
17 | };
18 | enum LightIds {
19 | NUM_LIGHTS
20 | };
21 |
22 | int lastOctave = 0;
23 |
24 | Octave() {
25 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
26 | configParam(OCTAVE_PARAM, -4.f, 4.f, 0.f, "Shift", " oct");
27 | getParamQuantity(OCTAVE_PARAM)->snapEnabled = true;
28 | configInput(PITCH_INPUT, "1V/octave pitch");
29 | configInput(OCTAVE_INPUT, "Octave shift CV");
30 | configOutput(PITCH_OUTPUT, "Pitch");
31 | configBypass(PITCH_INPUT, PITCH_OUTPUT);
32 | }
33 |
34 | void process(const ProcessArgs& args) override {
35 | int channels = std::max(inputs[PITCH_INPUT].getChannels(), 1);
36 | int octaveParam = std::round(params[OCTAVE_PARAM].getValue());
37 |
38 | for (int c = 0; c < channels; c++) {
39 | int octave = octaveParam + std::round(inputs[OCTAVE_INPUT].getPolyVoltage(c));
40 | float pitch = inputs[PITCH_INPUT].getVoltage(c);
41 | pitch += octave;
42 | outputs[PITCH_OUTPUT].setVoltage(pitch, c);
43 | if (c == 0)
44 | lastOctave = octave;
45 | }
46 | outputs[PITCH_OUTPUT].setChannels(channels);
47 | }
48 |
49 | void dataFromJson(json_t* rootJ) override {
50 | // In Fundamental 1.1.1 and earlier, the octave param was internal data.
51 | json_t* octaveJ = json_object_get(rootJ, "octave");
52 | if (octaveJ) {
53 | params[OCTAVE_PARAM].setValue(json_integer_value(octaveJ));
54 | }
55 | }
56 | };
57 |
58 |
59 | struct OctaveButton : Widget {
60 | int octave;
61 |
62 | void drawLayer(const DrawArgs& args, int layer) override {
63 | if (layer != 1)
64 | return;
65 |
66 | Vec c = box.size.div(2);
67 |
68 | int activeOctave = 0;
69 | int lastOctave = 0;
70 | ParamWidget* paramWidget = getAncestorOfType();
71 | assert(paramWidget);
72 | engine::ParamQuantity* pq = paramWidget->getParamQuantity();
73 | if (pq) {
74 | activeOctave = std::round(pq->getValue());
75 | Octave* module = dynamic_cast(pq->module);
76 | if (module)
77 | lastOctave = module->lastOctave;
78 | }
79 |
80 | if (activeOctave == octave) {
81 | // Enabled
82 | nvgBeginPath(args.vg);
83 | nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
84 | if (octave == 0)
85 | nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
86 | else
87 | nvgFillColor(args.vg, SCHEME_YELLOW);
88 | nvgFill(args.vg);
89 | }
90 | else if (lastOctave == octave) {
91 | // Disabled but enabled by CV
92 | nvgBeginPath(args.vg);
93 | nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
94 | if (octave == 0)
95 | nvgFillColor(args.vg, color::alpha(color::WHITE, 0.5 * 0.33));
96 | else
97 | nvgFillColor(args.vg, color::alpha(SCHEME_YELLOW, 0.5));
98 | nvgFill(args.vg);
99 | }
100 | else {
101 | // Disabled
102 | nvgBeginPath(args.vg);
103 | nvgCircle(args.vg, c.x, c.y, mm2px(4.0 / 2));
104 | nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
105 | nvgFill(args.vg);
106 |
107 | nvgBeginPath(args.vg);
108 | nvgCircle(args.vg, c.x, c.y, mm2px(3.0 / 2));
109 | nvgFillColor(args.vg, nvgRGB(0x12, 0x12, 0x12));
110 | nvgFill(args.vg);
111 |
112 | if (octave == 0) {
113 | nvgBeginPath(args.vg);
114 | nvgCircle(args.vg, c.x, c.y, mm2px(1.0 / 2));
115 | nvgFillColor(args.vg, color::alpha(color::WHITE, 0.33));
116 | nvgFill(args.vg);
117 | }
118 | }
119 | }
120 |
121 | void onDragHover(const event::DragHover& e) override {
122 | if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
123 | e.consume(this);
124 | }
125 | Widget::onDragHover(e);
126 | }
127 |
128 | void onDragEnter(const event::DragEnter& e) override;
129 | };
130 |
131 |
132 | struct OctaveParam : ParamWidget {
133 | OctaveParam() {
134 | box.size = mm2px(Vec(15.263, 55.88));
135 | const int octaves = 9;
136 | const float margin = mm2px(2.0);
137 | float height = box.size.y - 2 * margin;
138 | for (int i = 0; i < octaves; i++) {
139 | OctaveButton* octaveButton = new OctaveButton();
140 | octaveButton->box.pos = Vec(0, height / octaves * i + margin);
141 | octaveButton->box.size = Vec(box.size.x, height / octaves);
142 | octaveButton->octave = 4 - i;
143 | addChild(octaveButton);
144 | }
145 | }
146 | };
147 |
148 |
149 | inline void OctaveButton::onDragEnter(const event::DragEnter& e) {
150 | if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
151 | OctaveParam* origin = dynamic_cast(e.origin);
152 | if (origin) {
153 | ParamWidget* paramWidget = getAncestorOfType();
154 | assert(paramWidget);
155 | engine::ParamQuantity* pq = paramWidget->getParamQuantity();
156 | if (pq) {
157 | pq->setValue(octave);
158 | }
159 | }
160 | }
161 | Widget::onDragEnter(e);
162 | }
163 |
164 |
165 | struct OctaveDisplay : LedDisplay {
166 | void setModule(Octave* module) {
167 | addChild(createParam(mm2px(Vec(0.0, 0.0)), module, Octave::OCTAVE_PARAM));
168 | }
169 | };
170 |
171 |
172 | struct OctaveWidget : ModuleWidget {
173 | OctaveWidget(Octave* module) {
174 | setModule(module);
175 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Octave.svg"), asset::plugin(pluginInstance, "res/Octave-dark.svg")));
176 |
177 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
178 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
179 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
180 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
181 |
182 | addInput(createInputCentered(mm2px(Vec(7.62, 80.573)), module, Octave::OCTAVE_INPUT));
183 | addInput(createInputCentered(mm2px(Vec(7.62, 96.859)), module, Octave::PITCH_INPUT));
184 |
185 | addOutput(createOutputCentered(mm2px(Vec(7.62, 113.115)), module, Octave::PITCH_OUTPUT));
186 |
187 | OctaveDisplay* display = createWidget(mm2px(Vec(0.0, 13.039)));
188 | display->box.size = mm2px(Vec(15.263, 55.88));
189 | display->setModule(module);
190 | addChild(display);
191 | }
192 | };
193 |
194 |
195 | Model* modelOctave = createModel("Octave");
196 |
--------------------------------------------------------------------------------
/src/Process.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 |
4 | struct SlewFilter {
5 | float value = 0.f;
6 |
7 | float process(float in, float slew) {
8 | value += math::clamp(in - value, -slew, slew);
9 | return value;
10 | }
11 | float jump(float in) {
12 | value = in;
13 | return value;
14 | }
15 | float getValue() {
16 | return value;
17 | }
18 | };
19 |
20 |
21 | struct Process : Module {
22 | enum ParamId {
23 | SLEW_PARAM,
24 | GATE_PARAM,
25 | PARAMS_LEN
26 | };
27 | enum InputId {
28 | SLEW_INPUT,
29 | IN_INPUT,
30 | GATE_INPUT,
31 | INPUTS_LEN
32 | };
33 | enum OutputId {
34 | SH1_OUTPUT,
35 | SH2_OUTPUT,
36 | TH_OUTPUT,
37 | HT_OUTPUT,
38 | SLEW_OUTPUT,
39 | GLIDE_OUTPUT,
40 | OUTPUTS_LEN
41 | };
42 | enum LightId {
43 | GATE_LIGHT,
44 | LIGHTS_LEN
45 | };
46 |
47 | struct Engine {
48 | bool state = false;
49 | // For glide to turn on after 1ms
50 | float onTime = 0.f;
51 | float sample1 = 0.f;
52 | float sample2 = 0.f;
53 | SlewFilter sample1Filter;
54 | SlewFilter sample2Filter;
55 | float holdValue = 0.f;
56 | SlewFilter slewFilter;
57 | SlewFilter glideFilter;
58 | };
59 | Engine engines[16];
60 |
61 | Process() {
62 | config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
63 |
64 | struct SlewQuantity : ParamQuantity {
65 | float getDisplayValue() override {
66 | if (getValue() <= getMinValue())
67 | return 0.f;
68 | return ParamQuantity::getDisplayValue();
69 | }
70 | };
71 | configParam(SLEW_PARAM, std::log2(1e-3f), std::log2(10.f), std::log2(1e-3f), "Slew", " ms/V", 2, 1000);
72 | configButton(GATE_PARAM, "Gate");
73 | configInput(SLEW_INPUT, "Slew");
74 | configInput(IN_INPUT, "Voltage");
75 | configInput(GATE_INPUT, "Gate");
76 | configOutput(SH1_OUTPUT, "Sample & hold");
77 | configOutput(SH2_OUTPUT, "Sample & hold 2");
78 | configOutput(TH_OUTPUT, "Track & hold");
79 | configOutput(HT_OUTPUT, "Hold & track");
80 | configOutput(SLEW_OUTPUT, "Slew");
81 | configOutput(GLIDE_OUTPUT, "Glide");
82 | }
83 |
84 | void process(const ProcessArgs& args) override {
85 | int channels = inputs[IN_INPUT].getChannels();
86 | bool gateButton = params[GATE_PARAM].getValue() > 0.f;
87 | float slewParam = params[SLEW_PARAM].getValue();
88 | // Hard-left param means infinite slew
89 | if (slewParam <= std::log2(1e-3f))
90 | slewParam = -INFINITY;
91 |
92 | for (int c = 0; c < channels; c++) {
93 | Engine& e = engines[c];
94 |
95 | float in = inputs[IN_INPUT].getVoltage(c);
96 | float gateValue = inputs[GATE_INPUT].getPolyVoltage(c);
97 |
98 | // Slew rate in V/s
99 | float slew = INFINITY;
100 | if (std::isfinite(slewParam)) {
101 | float slewPitch = slewParam + inputs[SLEW_INPUT].getPolyVoltage(c);
102 | slew = dsp::exp2_taylor5(-slewPitch + 30.f) / std::exp2(30.f);
103 | }
104 | float slewDelta = slew * args.sampleTime;
105 |
106 | // Gate trigger/untrigger
107 | if (!e.state) {
108 | if (gateValue >= 2.f || gateButton) {
109 | // Triggered
110 | e.state = true;
111 | e.onTime = 0.f;
112 | // Hold and track
113 | e.holdValue = in;
114 | // Sample and hold
115 | e.sample2 = e.sample1;
116 | e.sample1 = in;
117 | }
118 | }
119 | else {
120 | if (gateValue <= 0.1f && !gateButton) {
121 | // Untriggered
122 | e.state = false;
123 | // Track and hold
124 | e.holdValue = in;
125 | }
126 | }
127 |
128 | // Track & hold
129 | float tr = e.state ? e.holdValue : in;
130 | float ht = e.state ? in : e.holdValue;
131 |
132 | // Slew
133 | if (e.state) {
134 | e.slewFilter.jump(in);
135 | e.onTime += args.sampleTime;
136 | }
137 | else {
138 | e.slewFilter.process(in, slewDelta);
139 | }
140 |
141 | // Glide
142 | // Wait 1ms before considering gate as legato
143 | if (e.state && e.onTime > 1e-3f) {
144 | e.glideFilter.process(in, slewDelta);
145 | }
146 | else {
147 | e.glideFilter.jump(in);
148 | }
149 |
150 | outputs[SH1_OUTPUT].setVoltage(e.sample1Filter.process(e.sample1, slewDelta), c);
151 | outputs[SH2_OUTPUT].setVoltage(e.sample2Filter.process(e.sample2, slewDelta), c);
152 | outputs[TH_OUTPUT].setVoltage(tr, c);
153 | outputs[HT_OUTPUT].setVoltage(ht, c);
154 | outputs[SLEW_OUTPUT].setVoltage(e.slewFilter.getValue(), c);
155 | outputs[GLIDE_OUTPUT].setVoltage(e.glideFilter.getValue(), c);
156 | }
157 |
158 | outputs[SH1_OUTPUT].setChannels(channels);
159 | outputs[SH2_OUTPUT].setChannels(channels);
160 | outputs[TH_OUTPUT].setChannels(channels);
161 | outputs[HT_OUTPUT].setChannels(channels);
162 | outputs[SLEW_OUTPUT].setChannels(channels);
163 | outputs[GLIDE_OUTPUT].setChannels(channels);
164 |
165 | lights[GATE_LIGHT].setBrightness(gateButton);
166 | }
167 | };
168 |
169 |
170 | struct ProcessWidget : ModuleWidget {
171 | ProcessWidget(Process* module) {
172 | setModule(module);
173 | setPanel(createPanel(asset::plugin(pluginInstance, "res/Process.svg"), asset::plugin(pluginInstance, "res/Process-dark.svg")));
174 |
175 | addChild(createWidget(Vec(RACK_GRID_WIDTH, 0)));
176 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
177 | addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
178 | addChild(createWidget(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
179 |
180 | addParam(createParamCentered(mm2px(Vec(12.646, 26.755)), module, Process::SLEW_PARAM));
181 | addParam(createLightParamCentered>(mm2px(Vec(18.136, 52.31)), module, Process::GATE_PARAM, Process::GATE_LIGHT));
182 |
183 | addInput(createInputCentered(mm2px(Vec(7.299, 52.31)), module, Process::SLEW_INPUT));
184 | addInput(createInputCentered(mm2px(Vec(7.297, 67.53)), module, Process::IN_INPUT));
185 | addInput(createInputCentered(mm2px(Vec(18.122, 67.53)), module, Process::GATE_INPUT));
186 |
187 | addOutput(createOutputCentered