├── CHANGELOG.md
├── LICENSE.txt
├── Makefile
├── README.md
├── RS.code-workspace
├── img
├── RSVectorVictor.png
├── boogie.png
└── fido3.png
├── plugin.json
├── res
├── RSVectorVictor.svg
├── components
│ ├── RSButton.svg
│ ├── RSButtonInvisible.svg
│ ├── RSButtonInvisibleIsh.svg
│ ├── RSButtonPress.svg
│ ├── RSJackMonoIn.svg
│ ├── RSJackMonoOut.svg
│ ├── RSJackPolyIn.svg
│ ├── RSJackPolyOut.svg
│ ├── RSJackSmallMonoIn.svg
│ ├── RSJackSmallMonoOut.svg
│ ├── RSKnobInvisible.svg
│ ├── RSKnobLrg.svg
│ ├── RSKnobMed.svg
│ ├── RSKnobSml.svg
│ ├── RSRoundButton.svg
│ ├── RSRoundButtonPress.svg
│ ├── RSSwitch_0.svg
│ ├── RSSwitch_1.svg
│ └── RSSwitch_2.svg
└── fonts
│ └── Ubuntu Condensed 400.ttf
└── src
├── Fido3-16.cpp
├── RS.hpp
├── RSBlank.cpp
├── RSBoogieBay.cpp
├── RSBoogieBayH8.cpp
├── RSCVHeat.cpp
├── RSGroundControl.cpp
├── RSHeat.cpp
├── RSLaunchControl.cpp
├── RSMFH.cpp
├── RSMajorTom.cpp
├── RSMissionControl.cpp
├── RSModule.hpp
├── RSModuleWidgetDraw.hpp
├── RSPhaseFour.cpp
├── RSPhaseOne.cpp
├── RSReheat.cpp
├── RSScratch.cpp
├── RSShades.cpp
├── RSSkeleton.cpp
├── RSVectorVictor.cpp
├── RSXYGLR.cpp
├── plugin.cpp
└── plugin.hpp
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | 27/10/19 V1.0.0 Initial version including Vector Victor, Boogie Bay & Boogie Bay H8.
4 | 27/11/19 V1.1.0 Added Heat
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # If RACK_DIR is not defined when calling the Makefile, default to two directories above
2 | RACK_DIR ?= ../..
3 |
4 | # FLAGS will be passed to both the C and C++ compiler
5 | FLAGS +=
6 | CFLAGS +=
7 | CXXFLAGS +=
8 |
9 | # Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path.
10 | # Static libraries are fine, but they should be added to this plugin's build system.
11 | LDFLAGS +=
12 |
13 | # Add .cpp files to the build
14 | SOURCES += $(wildcard src/*.cpp)
15 |
16 | # Add files to the ZIP package when running `make dist`
17 | # The compiled plugin and "plugin.json" are automatically added.
18 | DISTRIBUTABLES += res
19 | DISTRIBUTABLES += $(wildcard LICENSE*)
20 |
21 | # Include the Rack plugin Makefile framework
22 | include $(RACK_DIR)/plugin.mk
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Racket Science
2 | Modules for the VCV Rack virtual modular synthesizer.
3 |
4 | ## PHYDEAUX III
5 | 
6 |
7 | I know, yet another bloody step sequencer, in fact it nearly got called YABSS. I built this as an exercise and as a result feel much more confident developing for VCV now. It's the size of a bus so I named it after one of Frank Zappa's.
8 |
9 | You get four rows of sixteen steps with pulses and gates for each step, each row also has an attenuverter and scaled CV output. There are currently 276 parameters, 71 inputs, 148 outputs and 64 blinkenlights. In order to fit all of that into a meagre 104hp there are stealth sockets that only appear when you drag a cable, every pulse & gate has it's own output and the buttons on the left also double up as inputs. There are CV & write inputs, you can record a sequence from a midi keyboard should you wish. Outputs can be fed back into inputs so, for instance, if you want a row to be eight steps long, set a pulse on the ninth step and feed that back to the index reset input for that row. If you want to modify steps on the fly, you could take a gate output into write in. Similarly you can play back multiple patterns by feeding an EOC output back to the next pattern input. You could use pulses from one row to step another row etc etc. The possibilities are endless really.
10 |
11 | There are also phase inputs so it can be driven by modules such as ZZC and run backwards, forwards, upside down & inside out if you like.
12 |
13 |
14 | ## Boogie Bay
15 |
16 | 
17 |
18 | You've seen flying faders, with Boogie Bay you have sliding sockets for all of your wire wobbling needs.
19 | These modules also double up as voltage indicators, right click on the vertical 2 channel BB for range menu, BBH8 range can be selected for each channel by clicking on the individual voltage labels, also BBH8 has invisible scribble strips, click on an underscore to enter text to describe each channel.
20 |
21 | ## Vector Victor
22 |
23 | Shortly after I first got into VCV around May 2019 I found myself looking for a real time CV loop recorder yet couldn't find one, once the VCV Prototype module appeared I created a simple precursor of Vector Victor, also inspired by the ZZC phase based way of timing.
24 |
25 | Vector Victor is a phase driven CV recorder, if you're familiar with programming think of it as a thousand element array indexed by the phase input as that's exactly what it is. You get two for the price of one!
26 |
27 | Typically you would feed the phase input with a slow rising sawtooth from a LFO, or ZZC clock & divider modules to keep everything in sync. A simple use case is shown below where Vector Victor loops input from a MIDI keyboard. It can also be used to record knob movements or whatever really but is not designed with audio rates in mind.
28 |
29 | 
30 |
31 |
32 | ## Notices
33 |
34 | **Racket Science** is copyright © 2020 Ewen Bates, specifically:
35 |
36 | The **brand name** **Racket Science**. If you want to use this for a racket sports brand just ask, but in the context of creations that make a noise (aka racket) with synthesis, this is mine.
37 |
38 | All **source code** is copyright © 2020 Ewen Bates and is licensed under the [GNU General Public License v3.0](gpl-3.0.txt).
39 |
40 | All **graphics** in the `res` directory are copyright © 2020 Ewen Bates and licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/).
41 |
42 | Many thanks to the VCV developer community.
43 |
--------------------------------------------------------------------------------
/RS.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | },
6 | {
7 | "path": "/home/ewen/Rack-SDK"
8 | }
9 | ],
10 | "settings": {
11 | "files.associations": {
12 | "atomic": "cpp",
13 | "memory_resource": "cpp",
14 | "*.tcc": "cpp",
15 | "tuple": "cpp"
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/img/RSVectorVictor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ContemporaryInsanity/RacketScience/2978e546f55cd3ad7cdb6c03f7e003b8351bbd5f/img/RSVectorVictor.png
--------------------------------------------------------------------------------
/img/boogie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ContemporaryInsanity/RacketScience/2978e546f55cd3ad7cdb6c03f7e003b8351bbd5f/img/boogie.png
--------------------------------------------------------------------------------
/img/fido3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ContemporaryInsanity/RacketScience/2978e546f55cd3ad7cdb6c03f7e003b8351bbd5f/img/fido3.png
--------------------------------------------------------------------------------
/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "slug": "RacketScience",
3 | "name": "Racket Science",
4 | "version": "1.2.0",
5 | "license": "GPL-3.0",
6 | "brand": "Racket Science",
7 | "author": "ContemporaryInsanity",
8 | "authorEmail": "ContemporaryInsanity@gmail.com",
9 | "authorUrl": "https://github.com/ContemporaryInsanity",
10 | "pluginUrl": "https://github.com/ContemporaryInsanity/RacketScience",
11 | "manualUrl": "https://github.com/ContemporaryInsanity/RacketScience",
12 | "sourceUrl": "https://github.com/ContemporaryInsanity/RacketScience",
13 | "donateUrl": "https://www.paypal.me/EwenBates",
14 | "modules": [
15 | {
16 | "slug": "RSGroundControl",
17 | "name": "RSGroundControl",
18 | "description": "Theme settings",
19 | "tags": [
20 | "Visual",
21 | "Utility"
22 | ]
23 | },
24 | {
25 | "slug": "RSMajorTom",
26 | "name": "RSMajorTom",
27 | "description": "Colour cycling",
28 | "tags": [
29 | "Utility"
30 | ]
31 | },
32 | {
33 | "slug": "RSHeat",
34 | "name": "RSHeat",
35 | "description": "Note / Octave Heat Map",
36 | "tags": [
37 | "Visual",
38 | "Utility"
39 | ]
40 | },
41 | {
42 | "slug": "RSReheat",
43 | "name": "RSReheat",
44 | "description": "Note / Octave Heat Map",
45 | "tags": [
46 | "Visual",
47 | "Utility"
48 | ]
49 | },
50 | {
51 | "slug": "RSCVHeat",
52 | "name": "RSCVHeat",
53 | "description": "CV Heat Map",
54 | "tags": [
55 | "Visual",
56 | "Utility"
57 | ]
58 | },
59 | {
60 | "slug": "RSBoogieBay",
61 | "name": "RSBoogieBay",
62 | "description": "Animated patchbay / voltage indicator",
63 | "tags": [
64 | "Visual",
65 | "Utility"
66 | ]
67 | },
68 | {
69 | "slug": "RSBoogieBayH8",
70 | "name": "RSBoogieBayH8",
71 | "description": "Animated patchbay / voltage indicator",
72 | "tags": [
73 | "Visual",
74 | "Utility"
75 | ]
76 | },
77 | {
78 | "slug": "RSMFH",
79 | "name": "RSMFH",
80 | "description": "Utility",
81 | "tags": [
82 | "Utility"
83 | ]
84 | },
85 | {
86 | "slug": "RSBlank",
87 | "name": "RSBlank",
88 | "description": "Resizeable blank panel",
89 | "tags": [
90 | "Visual",
91 | "Utility"
92 | ]
93 | },
94 | {
95 | "slug": "RSShades",
96 | "name": "RSShades",
97 | "description": "Colour Control",
98 | "tags": [
99 | "Visual",
100 | "Utility"
101 | ]
102 | },
103 | {
104 | "slug": "RSFido316",
105 | "name": "RSFido316",
106 | "description": "16 Step Sequencer",
107 | "tags": [
108 | "Sequencer"
109 | ]
110 | },
111 | {
112 | "slug": "RSPhaseOne",
113 | "name": "RSPhaseOne",
114 | "description": "Phase Recorder / Step Sequencer",
115 | "tags": [
116 | "Recorder",
117 | "Sequencer"
118 | ]
119 | },
120 | {
121 | "slug": "RSPhaseFour",
122 | "name": "RSPhaseFour",
123 | "description": "Phase Recorder / Step Sequencer",
124 | "tags": [
125 | "Recorder",
126 | "Sequencer"
127 | ]
128 | },
129 | {
130 | "slug": "RSMissionControl",
131 | "name": "RSMissionControl",
132 | "description": "Seuqncer Launcher Sequencer",
133 | "tags": [
134 | "Utility",
135 | "Sequencer"
136 | ]
137 | },
138 | {
139 | "slug": "RSLaunchControl",
140 | "name": "RSLaunchControl",
141 | "description": "Phase Sequencer Launcher",
142 | "tags": [
143 | "Utility",
144 | "Sequencer"
145 | ]
146 | },
147 | {
148 | "slug": "RSXYGLR",
149 | "name": "RSXYGLR",
150 | "description": "XY Pad Loop Recorder",
151 | "tags": [
152 | "Recorder",
153 | "Sequencer"
154 | ]
155 | },
156 | {
157 | "slug": "RSVectorVictor",
158 | "name": "RSVectorVictor",
159 | "description": "Phase Driven CV Looper",
160 | "tags": [
161 | "Recorder",
162 | "Sequencer"
163 | ]
164 | }
165 | ]
166 | }
--------------------------------------------------------------------------------
/res/components/RSButton.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/res/components/RSButtonInvisible.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
67 |
--------------------------------------------------------------------------------
/res/components/RSButtonInvisibleIsh.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
76 |
--------------------------------------------------------------------------------
/res/components/RSButtonPress.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
78 |
--------------------------------------------------------------------------------
/res/components/RSJackMonoIn.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
113 |
--------------------------------------------------------------------------------
/res/components/RSJackMonoOut.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/res/components/RSJackPolyIn.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
125 |
--------------------------------------------------------------------------------
/res/components/RSJackPolyOut.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
206 |
--------------------------------------------------------------------------------
/res/components/RSJackSmallMonoIn.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/res/components/RSJackSmallMonoOut.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/res/components/RSKnobInvisible.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
67 |
--------------------------------------------------------------------------------
/res/components/RSKnobLrg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
91 |
--------------------------------------------------------------------------------
/res/components/RSKnobMed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
92 |
--------------------------------------------------------------------------------
/res/components/RSKnobSml.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
91 |
--------------------------------------------------------------------------------
/res/components/RSRoundButton.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
76 |
--------------------------------------------------------------------------------
/res/components/RSRoundButtonPress.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/res/components/RSSwitch_0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/res/components/RSSwitch_1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/res/components/RSSwitch_2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
82 |
--------------------------------------------------------------------------------
/res/fonts/Ubuntu Condensed 400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ContemporaryInsanity/RacketScience/2978e546f55cd3ad7cdb6c03f7e003b8351bbd5f/res/fonts/Ubuntu Condensed 400.ttf
--------------------------------------------------------------------------------
/src/RS.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "RSModule.hpp"
6 |
7 |
8 | #define quantize(v) (round(v * 12.f) / 12.f)
9 | #define octave(v) int(floor(quantize(v)))
10 | #define note(v) (int(round((v + 10) * 12)) % 12)
11 |
12 | static float noteVoltage[] = {
13 | 0.00000f, // C
14 | 0.08333f, // C#
15 | 0.16667f, // D
16 | 0.25000f, // D#
17 | 0.33333f, // E
18 | 0.41667f, // F
19 | 0.50000f, // F#
20 | 0.58333f, // G
21 | 0.66667f, // G#
22 | 0.75000f, // A
23 | 0.83333f, // A#
24 | 0.91667f, // B
25 | };
26 |
27 | static inline float RSclamp(float in, float min, float max) {
28 | return in < min ? min : (in > max ? max : in);
29 | }
30 |
31 | static inline float RSscale(float in, float inMin, float inMax, float outMin, float outMax) {
32 | return((outMax - outMin) * (in - inMin) / (inMax - inMin)) + outMin;
33 | }
34 |
35 |
36 | /*
37 | Racket Science custom components
38 | (C) 2020 Ewen Bates
39 | */
40 |
41 | // Colours
42 |
43 | #define COLOR_RS_GREY nvgRGB(0xb4, 0xb4, 0xb4)
44 | #define COLOR_RS_BRONZE nvgRGB(0x85, 0x87, 0x39)
45 | #define COLOR_BLACK nvgRGB(0x00, 0x00, 0x00)
46 | #define COLOR_RED nvgRGB(0xff, 0x00, 0x00)
47 | #define COLOR_GREEN nvgRGB(0x00, 0xff, 0x00)
48 | #define COLOR_BLUE nvgRGB(0x00, 0x00, 0xff)
49 | #define COLOR_YELLOW nvgRGB(0xff, 0xff, 0x00)
50 | #define COLOR_WHITE nvgRGB(0xff, 0xff, 0xff)
51 |
52 |
53 | // LEDs
54 |
55 | struct RSLightWidget : LightWidget {
56 |
57 |
58 |
59 |
60 |
61 | };
62 |
63 |
64 | // Labels
65 |
66 | // Uses own colour, intended for use with scale labels, green for pos, red for neg
67 | // Now we have themes this causes problems with contrast, perhaps have a black background?
68 | struct RSLabel : LedDisplay {
69 | int fontSize;
70 | std::shared_ptr font;
71 | std::string text;
72 | NVGcolor color;
73 |
74 | RSLabel(int x, int y, const char* str = "", int fontSize = 10, const NVGcolor& colour = COLOR_RS_GREY) {
75 | font = APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/Ubuntu Condensed 400.ttf"));
76 | box.pos = Vec(x, y);
77 | box.size = Vec(120, 12);
78 | text = str;
79 | color = colour;
80 | this->fontSize = fontSize;
81 | }
82 |
83 | void draw(const DrawArgs &args) override {
84 | if(font->handle >= 0) {
85 | bndSetFont(font->handle);
86 |
87 | nvgFontSize(args.vg, fontSize);
88 | nvgFontFaceId(args.vg, font->handle);
89 | nvgTextLetterSpacing(args.vg, 0);
90 |
91 | nvgBeginPath(args.vg);
92 | nvgFillColor(args.vg, color);
93 | nvgText(args.vg, 0, 0, text.c_str(), NULL);
94 | nvgStroke(args.vg);
95 |
96 | bndSetFont(APP->window->uiFont->handle);
97 | }
98 | }
99 | };
100 |
101 |
102 | // Uses RSGlobal color, intended for general labels
103 | struct RSLabelCentered : LedDisplay {
104 | int fontSize;
105 | std::shared_ptr font;
106 | std::string text;
107 | int *themeIdx = NULL;
108 |
109 | RSLabelCentered(int x, int y, const char* str = "", int fontSize = 10, RSModule *module = NULL) {
110 | font = APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/Ubuntu Condensed 400.ttf"));
111 | this->fontSize = fontSize;
112 | box.pos = Vec(x, y);
113 | text = str;
114 |
115 | if(module) themeIdx = &(module->RSTheme);
116 | }
117 |
118 | void draw(const DrawArgs &args) override {
119 | if(font->handle >= 0) {
120 | bndSetFont(font->handle);
121 |
122 | nvgFontSize(args.vg, fontSize);
123 | nvgFontFaceId(args.vg, font->handle);
124 | nvgTextLetterSpacing(args.vg, 0);
125 | nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
126 |
127 | nvgBeginPath(args.vg);
128 | if(themeIdx == NULL) nvgFillColor(args.vg, RSGlobal.themes[RSGlobal.themeIdx].lbColor);
129 | else nvgFillColor(args.vg, RSGlobal.themes[*themeIdx - 1].lbColor);
130 |
131 | nvgText(args.vg, 0, 0, text.c_str(), NULL);
132 | nvgStroke(args.vg);
133 |
134 | bndSetFont(APP->window->uiFont->handle);
135 | }
136 | }
137 | };
138 |
139 |
140 | // Scribble Strips
141 |
142 | struct RSScribbleStrip : LedDisplayTextField {
143 | int textSize = 12;
144 | int numChars = 25;
145 |
146 | RSScribbleStrip(int x, int y, int size = 150, const char* str = "_") {
147 | font = APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/Ubuntu Condensed 400.ttf"));
148 | box.pos = Vec(x, y);
149 | box.size = Vec(size, 14); // Derive size from pos & panel width? have numChars as parameter instead
150 | textOffset = Vec(0, -3);
151 | multiline = false; // Doesn't appear to have the desired effect
152 | text = str;
153 | }
154 |
155 | // We want scribbles without background
156 | void draw(const DrawArgs &args) override {
157 | if(cursor > numChars) {
158 | text.resize(numChars);
159 | cursor = numChars;
160 | selection = numChars;
161 | }
162 |
163 | //nvgScissor(args.vg, RECT_ARGS(args.clipBox));
164 | if (font->handle >= 0) {
165 | bndSetFont(font->handle);
166 |
167 | float bounds[4];
168 | nvgTextBounds(args.vg, 0.0f, 0.0f, text.c_str(), NULL, bounds);
169 | float textWidth = bounds[2];
170 |
171 | // If we subtract textWidth / 2 from parameter 2 textOffset.x we get dynamically centered strips
172 | // however the mouse doesn't position the cursor accordingly
173 | NVGcolor color = RSGlobal.themes[RSGlobal.themeIdx].ssColor;
174 | NVGcolor highlightColor = color;
175 | highlightColor.a = 0.5;
176 | int begin = std::min(cursor, selection);
177 | int end = (this == APP->event->selectedWidget) ? std::max(cursor, selection) : -1;
178 | //INFO("Racket Science: textWidth: %f box.size.x: %f box.size.y: %f", textWidth, box.size.x, box.size.y);
179 | //INFO("Racket Science: cursor: %i selection: %i begin: %i end: %i", cursor, selection, begin, end);
180 | bndIconLabelCaret(args.vg, textOffset.x, textOffset.y,
181 | box.size.x - 2*textOffset.x, box.size.y - 2*textOffset.y,
182 | -1, color, textSize, text.c_str(), highlightColor, begin, end);
183 |
184 | bndSetFont(APP->window->uiFont->handle);
185 | }
186 | //nvgResetScissor(args.vg);
187 | }
188 | };
189 |
190 |
191 | // Ports
192 |
193 | struct RSJackMonoOut : SVGPort { RSJackMonoOut() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoOut.svg"))); } };
194 | struct RSJackSmallMonoOut : SVGPort { RSJackSmallMonoOut() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackSmallMonoOut.svg"))); } };
195 | struct RSJackPolyOut : SVGPort { RSJackPolyOut() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackPolyOut.svg"))); } };
196 | struct RSJackMonoIn : SVGPort { RSJackMonoIn() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoIn.svg"))); } };
197 | struct RSJackSmallMonoIn : SVGPort { RSJackSmallMonoIn() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackSmallMonoIn.svg"))); } };
198 | struct RSJackPolyIn : SVGPort { RSJackPolyIn() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackPolyIn.svg"))); } };
199 |
200 | struct RSStealthJackIn : app::SvgPort { // With thanks to https://github.com/DominoMarama/ReTunesFree
201 | // RSStealthJackIn() {
202 | // setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoIn.svg")));
203 | // }
204 |
205 | void step() override {
206 | if(!module) return;
207 |
208 | if(module->inputs[portId].isConnected()) {
209 | Widget::show();
210 | }
211 | else {
212 | CableWidget* cw = APP->scene->rack->incompleteCable;
213 | if(cw) {
214 | if(cw->outputPort) Widget::show();
215 | else Widget::hide();
216 | }
217 | else Widget::hide();
218 | }
219 | Widget::step();
220 | }
221 | };
222 |
223 | struct RSStealthJackOut : app::SvgPort {
224 | // RSStealthJackOut() {
225 | // setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoOut.svg")));
226 | // }
227 |
228 | void step() override {
229 | if(!module) return;
230 |
231 | if(module->outputs[portId].isConnected()) {
232 | Widget::show();
233 | }
234 | else {
235 | CableWidget* cw = APP->scene->rack->incompleteCable;
236 | if(cw) {
237 | if(cw->inputPort) Widget::show();
238 | else Widget::hide();
239 | }
240 | else Widget::hide();
241 | }
242 | Widget::step();
243 | }
244 | };
245 |
246 | struct RSStealthJackMonoIn : RSStealthJackIn {
247 | RSStealthJackMonoIn() {
248 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoIn.svg")));
249 | }
250 | };
251 |
252 | struct RSStealthJackSmallMonoIn : RSStealthJackIn {
253 | RSStealthJackSmallMonoIn() {
254 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackSmallMonoIn.svg")));
255 | }
256 | };
257 |
258 | struct RSStealthJackMonoOut : RSStealthJackOut {
259 | RSStealthJackMonoOut() {
260 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackMonoOut.svg")));
261 | }
262 | };
263 |
264 | struct RSStealthJackSmallMonoOut : RSStealthJackOut {
265 | RSStealthJackSmallMonoOut() {
266 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackSmallMonoOut.svg")));
267 | }
268 | };
269 |
270 | struct RSStealthJackPolyIn : RSStealthJackIn {
271 | RSStealthJackPolyIn() {
272 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackPolyIn.svg")));
273 | }
274 | };
275 |
276 | struct RSStealthJackPolyOut : RSStealthJackOut {
277 | RSStealthJackPolyOut() {
278 | setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSJackPolyOut.svg")));
279 | }
280 | };
281 |
282 |
283 | // Knobs
284 |
285 | struct RSKnob : SVGKnob {
286 | RSKnob() {
287 | minAngle = -0.83 * M_PI;
288 | maxAngle = 0.83 * M_PI;
289 |
290 | shadow->opacity = 0.0f; // Hide shadows
291 | }
292 | };
293 |
294 | struct RSKnobDetent : RSKnob {
295 | RSKnobDetent() {
296 | snap = true;
297 | }
298 | };
299 |
300 | struct RSKnobSml : RSKnob { RSKnobSml() {setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobSml.svg"))); } };
301 | struct RSKnobMed : RSKnob { RSKnobMed() {setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobMed.svg"))); } };
302 | struct RSKnobLrg : RSKnob { RSKnobLrg() {setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobLrg.svg"))); } };
303 |
304 | struct RSKnobInvisible : RSKnob { RSKnobInvisible() {setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobInvisible.svg"))); } };
305 |
306 | struct RSKnobDetentSml : RSKnobDetent { RSKnobDetentSml() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobSml.svg"))); } };
307 | struct RSKnobDetentMed : RSKnobDetent { RSKnobDetentMed() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobMed.svg"))); } };
308 | struct RSKnobDetentLrg : RSKnobDetent { RSKnobDetentLrg() { setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobLrg.svg"))); } };
309 |
310 | struct RSKnobDetentInvisible : RSKnobDetent { RSKnobDetentInvisible() {setSvg(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSKnobInvisible.svg"))); } };
311 |
312 |
313 | // Buttons
314 |
315 | struct RSButton : SVGSwitch {
316 | RSButton() {
317 | shadow->opacity = 0.0f; // Hide shadows
318 | }
319 |
320 | void randomize() override {
321 | SVGSwitch::randomize();
322 |
323 | if(paramQuantity->getValue() > 0.5f) paramQuantity->setValue(1.0f);
324 | else paramQuantity->setValue(0.0f);
325 | }
326 | };
327 |
328 | struct RSButtonToggle : RSButton {
329 | RSButtonToggle() {
330 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSButton.svg")));
331 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSButtonPress.svg")));
332 | }
333 | };
334 |
335 | struct RSRoundButtonToggle : RSButton {
336 | RSRoundButtonToggle() {
337 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSRoundButton.svg")));
338 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSRoundButtonPress.svg")));
339 | }
340 | };
341 |
342 | struct RSButtonToggleInvisible : RSButton {
343 | RSButtonToggleInvisible() {
344 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSButtonInvisibleIsh.svg")));
345 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSButtonInvisible.svg")));
346 | }
347 | };
348 |
349 | struct RSButtonMomentary : RSButtonToggle {
350 | RSButtonMomentary() {
351 | momentary = true;
352 | }
353 | };
354 |
355 | struct RSRoundButtonMomentary : RSRoundButtonToggle {
356 | RSRoundButtonMomentary() {
357 | momentary = true;
358 | }
359 | };
360 |
361 | struct RSButtonMomentaryInvisible : RSButtonToggleInvisible {
362 | RSButtonMomentaryInvisible() {
363 | momentary = true;
364 | }
365 | };
366 |
367 | // Switches
368 |
369 | struct RSSwitch2P : SVGSwitch {
370 | RSSwitch2P() {
371 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSSwitch_0.svg")));
372 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSSwitch_2.svg")));
373 |
374 | shadow->opacity = 0.0f;
375 | }
376 |
377 | void onChange(const event::Change &e) override {
378 | SVGSwitch::onChange(e);
379 |
380 | if(paramQuantity->getValue() > 0.5f) paramQuantity->setValue(1.0f);
381 | else paramQuantity->setValue(0.0f);
382 | }
383 |
384 | void randomize() override {
385 | SVGSwitch::randomize();
386 |
387 | if(paramQuantity->getValue() > 0.5f) paramQuantity->setValue(1.0f);
388 | else paramQuantity->setValue(0.0f);
389 | }
390 | };
391 |
392 | struct RSSwitch3PV : SVGSwitch {
393 | RSSwitch3PV() {
394 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSSwitch_0.svg")));
395 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSSwitch_1.svg")));
396 | addFrame(APP->window->loadSvg(asset::plugin(pluginInstance, "res/components/RSSwitch_2.svg")));
397 |
398 | shadow->opacity = 0.0f;
399 | }
400 |
401 | void onChange(const event::Change &e) override {
402 | SVGSwitch::onChange(e);
403 |
404 | if(paramQuantity->getValue() > 1.33f) paramQuantity->setValue(2.0f);
405 | else if(paramQuantity->getValue() > 0.67f) paramQuantity->setValue(1.0f);
406 | else paramQuantity->setValue(0.0f);
407 | }
408 |
409 | void randomize() override {
410 | SVGSwitch::randomize();
411 | if(paramQuantity->getValue() > 1.33f) paramQuantity->setValue(2.0f);
412 | else if(paramQuantity->getValue() > 0.67f) paramQuantity->setValue(1.0f);
413 | else paramQuantity->setValue(0.0f);
414 | }
415 | };
416 |
417 | struct ModuleResizeHandle : OpaqueWidget {
418 | bool right = false;
419 | Vec dragPos;
420 | Rect originalBox;
421 |
422 | ModuleResizeHandle() {
423 | box.size = Vec(1 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
424 | }
425 |
426 | void onDragStart(const event::DragStart &e) override {
427 | if (e.button != GLFW_MOUSE_BUTTON_LEFT)
428 | return;
429 |
430 | dragPos = APP->scene->rack->mousePos;
431 | ModuleWidget *mw = getAncestorOfType();
432 | assert(mw);
433 | originalBox = mw->box;
434 | }
435 |
436 | void onDragMove(const event::DragMove &e) override {
437 | ModuleWidget *mw = getAncestorOfType();
438 | assert(mw);
439 |
440 | Vec newDragPos = APP->scene->rack->mousePos;
441 | float deltaX = newDragPos.x - dragPos.x;
442 |
443 | Rect newBox = originalBox;
444 | Rect oldBox = mw->box;
445 | const float minWidth = 3 * RACK_GRID_WIDTH;
446 | if (right) {
447 | newBox.size.x += deltaX;
448 | newBox.size.x = std::fmax(newBox.size.x, minWidth);
449 | newBox.size.x = std::round(newBox.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
450 | }
451 | else {
452 | newBox.size.x -= deltaX;
453 | newBox.size.x = std::fmax(newBox.size.x, minWidth);
454 | newBox.size.x = std::round(newBox.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
455 | newBox.pos.x = originalBox.pos.x + originalBox.size.x - newBox.size.x;
456 | }
457 |
458 | mw->box = newBox;
459 | if (!APP->scene->rack->requestModulePos(mw, newBox.pos)) {
460 | mw->box = oldBox;
461 | }
462 | }
463 | };
464 |
--------------------------------------------------------------------------------
/src/RSBlank.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | // struct BlankPanel : Widget {
6 | // Widget* panelBorder;
7 |
8 | // BlankPanel() {
9 | // panelBorder = new PanelBorder;
10 | // addChild(panelBorder);
11 | // }
12 |
13 | // void step() override {
14 | // panelBorder->box.size = box.size;
15 | // Widget::step();
16 | // }
17 |
18 | // void draw(const DrawArgs& args) {
19 | // nvgBeginPath(args.vg);
20 | // nvgRoundedRect(args.vg, 0.0, 0.0, box.size.x, box.size.y, 5);
21 | // nvgFillColor(args.vg, nvgRGB(0xe6, 0xe6, 0xe6));
22 | // nvgFill(args.vg);
23 | // Widget::draw(args);
24 | // }
25 | // };
26 |
27 | struct RSBlank : RSModule {
28 | enum ParamIds {
29 | THEME_KNOB,
30 | NUM_PARAMS
31 | };
32 | enum InputIds {
33 | NUM_INPUTS
34 | };
35 | enum OutputIds {
36 | NUM_OUTPUTS
37 | };
38 | enum LightIds {
39 | NUM_LIGHTS
40 | };
41 |
42 | dsp::BooleanTrigger themeTrigger;
43 |
44 | RSBlank() {
45 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
46 |
47 | configParam(THEME_KNOB, 1.f, 16.f, 1.f, "THEME");
48 | }
49 |
50 | void process(const ProcessArgs &args) override {
51 |
52 | }
53 |
54 | json_t* dataToJson() override {
55 | json_t* rootJ = json_object();
56 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
57 |
58 | return rootJ;
59 | }
60 |
61 | void dataFromJson(json_t* rootJ) override {
62 | json_t* themeJ = json_object_get(rootJ, "theme");
63 | if(themeJ) RSTheme = json_integer_value(themeJ);
64 | }
65 | };
66 |
67 | struct RSBlankWidget : ModuleWidget {
68 | RSBlank *module;
69 | Widget *rightHandle;
70 |
71 | RSBlankWidget(RSBlank *module) {
72 | INFO("Racket Science: RSBlankWidget()");
73 |
74 | setModule(module);
75 | this->module = module;
76 |
77 | box.size = Vec(RACK_GRID_WIDTH * 3, RACK_GRID_HEIGHT);
78 | int middle = box.size.x / 2 + 1;
79 |
80 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSBlank::THEME_KNOB));
81 |
82 | addChild(new RSLabelCentered(middle, box.size.y - 15, "Racket", 12, module));
83 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Science", 12, module));
84 |
85 | ModuleResizeHandle *rightHandle = new ModuleResizeHandle;
86 | rightHandle->right = true;
87 | this->rightHandle = rightHandle;
88 | addChild(rightHandle);
89 | }
90 |
91 | void step() override {
92 | rightHandle->box.pos.x = box.size.x - rightHandle->box.size.x;
93 |
94 | if(module) module->RSTheme = module->params[RSBlank::THEME_KNOB].getValue();
95 |
96 | ModuleWidget::step();
97 | }
98 |
99 | void customDraw(const DrawArgs& args) {}
100 | #include "RSModuleWidgetDraw.hpp"
101 |
102 | json_t *toJson() override {
103 | json_t* rootJ = ModuleWidget::toJson();
104 |
105 | json_object_set_new(rootJ, "width", json_real(box.size.x));
106 |
107 | return rootJ;
108 | }
109 |
110 | void fromJson(json_t* rootJ) override {
111 | ModuleWidget::fromJson(rootJ);
112 |
113 | json_t* widthJ = json_object_get(rootJ, "width");
114 | if(widthJ) box.size.x = json_number_value(widthJ);
115 | }
116 | };
117 |
118 | Model* modelRSBlank = createModel("RSBlank");
--------------------------------------------------------------------------------
/src/RSBoogieBay.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSBoogieBay : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | INA_INPUT,
12 | INB_INPUT,
13 | NUM_INPUTS
14 | };
15 | enum OutputIds {
16 | OUTA_OUTPUT,
17 | OUTB_OUTPUT,
18 | NUM_OUTPUTS
19 | };
20 | enum LightIds {
21 | NUM_LIGHTS
22 | };
23 |
24 | dsp::BooleanTrigger themeTrigger;
25 |
26 | int vrangea = 4;
27 | int vrangeb = 2;
28 |
29 | bool menuaChanged = true;
30 | bool menubChanged = true;
31 |
32 | RSBoogieBay() {
33 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
34 |
35 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
36 | }
37 |
38 | void process(const ProcessArgs &args) override {
39 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
40 | RSTheme++;
41 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
42 | }
43 |
44 | outputs[OUTA_OUTPUT].setVoltage(inputs[INA_INPUT].getVoltage());
45 | outputs[OUTB_OUTPUT].setVoltage(inputs[INB_INPUT].getVoltage());
46 | }
47 |
48 | void onReset() override {
49 | }
50 |
51 | json_t* dataToJson() override {
52 | json_t* rootJ = json_object();
53 |
54 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
55 |
56 | json_object_set_new(rootJ, "vrangea", json_integer(vrangea));
57 | json_object_set_new(rootJ, "vrangeb", json_integer(vrangeb));
58 |
59 | return rootJ;
60 | }
61 |
62 | void dataFromJson(json_t* rootJ) override {
63 | json_t* themeJ = json_object_get(rootJ, "theme");
64 |
65 | if(themeJ) RSTheme = json_integer_value(themeJ);
66 |
67 | json_t* vrangeaJ = json_object_get(rootJ, "vrangea");
68 | json_t* vrangebJ = json_object_get(rootJ, "vrangeb");
69 |
70 | if(vrangeaJ) vrangea = json_integer_value(vrangeaJ);
71 | if(vrangebJ) vrangeb = json_integer_value(vrangebJ);
72 | }
73 | };
74 |
75 |
76 | struct RSBoogieBayWidget : ModuleWidget {
77 | RSBoogieBay* module;
78 |
79 | PortWidget *ina, *inb;
80 |
81 | RSLabel* topScaleaLabel;
82 | RSLabel* midScaleaLabel;
83 | RSLabel* botScaleaLabel;
84 |
85 | RSLabel* topScalebLabel;
86 | RSLabel* midScalebLabel;
87 | RSLabel* botScalebLabel;
88 |
89 | RSBoogieBayWidget(RSBoogieBay *module) {
90 | INFO("Racket Science: RSBoogieBayWidget()");
91 |
92 | setModule(module);
93 | this->module = module;
94 |
95 | box.size.x = mm2px(5.08 * 5);
96 | int middle = box.size.x / 2 + 1;
97 |
98 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSBoogieBay::THEME_BUTTON));
99 |
100 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "BOOGIE", 14, module));
101 | addChild(new RSLabelCentered(middle, box.pos.y + 25, "BAY", 14, module));
102 |
103 | addChild(new RSLabelCentered(box.size.x / 2, box.size.y - 6, "Racket Science", 12, module));
104 |
105 | ina = createInputCentered(Vec(22, 175), module, RSBoogieBay::INA_INPUT);
106 | addInput(ina);
107 | inb = createInputCentered(Vec(52, 175), module, RSBoogieBay::INB_INPUT);
108 | addInput(inb);
109 |
110 | addOutput(createOutputCentered(Vec(22, 345), module, RSBoogieBay::OUTA_OUTPUT));
111 | addOutput(createOutputCentered(Vec(52, 345), module, RSBoogieBay::OUTB_OUTPUT));
112 |
113 | addChild(topScaleaLabel = new RSLabel(3, 58, "0", 10, COLOR_GREEN));
114 | addChild(midScaleaLabel = new RSLabel(3, 177, "0", 10, COLOR_RS_GREY));
115 | addChild(botScaleaLabel = new RSLabel(3, 296, "0", 10, COLOR_RED));
116 |
117 | addChild(topScalebLabel = new RSLabel(67, 58, "0", 10, COLOR_GREEN));
118 | addChild(midScalebLabel = new RSLabel(67, 177, "0", 10, COLOR_RS_GREY));
119 | addChild(botScalebLabel = new RSLabel(67, 296, "0", 10, COLOR_RED));
120 | }
121 |
122 | void customDraw(const DrawArgs& args) {
123 | int top = 55, bottom = 295;
124 | nvgLineCap(args.vg, NVG_ROUND);
125 |
126 | // Socket scale markers
127 | nvgStrokeColor(args.vg, COLOR_RS_GREY);
128 | nvgStrokeWidth(args.vg, 1);
129 | for(int y = top, cnt = 0; y <= bottom; y += (bottom - top) / 10, cnt++) {
130 | nvgBeginPath(args.vg);
131 | switch(cnt) {
132 | case 0: case 5: case 10:
133 | nvgMoveTo(args.vg, 16, y);
134 | nvgLineTo(args.vg, 58, y);
135 | break;
136 | default:
137 | nvgMoveTo(args.vg, 30, y);
138 | nvgLineTo(args.vg, 44, y);
139 | break;
140 | }
141 | nvgStroke(args.vg);
142 | }
143 |
144 | nvgStrokeColor(args.vg, COLOR_BLACK);
145 | nvgStrokeWidth(args.vg, 5);
146 |
147 | // Left socket slot
148 | nvgBeginPath(args.vg);
149 | nvgMoveTo(args.vg, 22, top);
150 | nvgLineTo(args.vg, 22, bottom);
151 | nvgStroke(args.vg);
152 |
153 | // Right socket slot
154 | nvgBeginPath(args.vg);
155 | nvgMoveTo(args.vg, 52, top);
156 | nvgLineTo(args.vg, 52, bottom);
157 | nvgStroke(args.vg);
158 | }
159 | #include "RSModuleWidgetDraw.hpp"
160 |
161 | void step() override {
162 | if(!module) return;
163 |
164 | if(module->menuaChanged) {
165 | switch(module->vrangea) {
166 | case 0: // 0 - 1
167 | topScaleaLabel->text = "1"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
168 | midScaleaLabel->text = ".5"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_GREEN;
169 | botScaleaLabel->text = "0"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RS_GREY;
170 | break;
171 | case 1: // 0 - 5
172 | topScaleaLabel->text = "5"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
173 | midScaleaLabel->text = "2.5"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_GREEN;
174 | botScaleaLabel->text = "0"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RS_GREY;
175 | break;
176 | case 2: // 0 - 10
177 | topScaleaLabel->text = "10"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
178 | midScaleaLabel->text = "5"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_GREEN;
179 | botScaleaLabel->text = "0"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RS_GREY;
180 | break;
181 | case 3: // -2 - 2
182 | topScaleaLabel->text = "2"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
183 | midScaleaLabel->text = "0"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_RS_GREY;
184 | botScaleaLabel->text = "2"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RED;
185 | break;
186 | case 4: // -5 - 5
187 | topScaleaLabel->text = "5"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
188 | midScaleaLabel->text = "0"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_RS_GREY;
189 | botScaleaLabel->text = "5"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RED;
190 | break;
191 | case 5: // -10 - 10
192 | topScaleaLabel->text = "10"; topScaleaLabel->box.pos.x = 3; topScaleaLabel->color = COLOR_GREEN;
193 | midScaleaLabel->text = "0"; midScaleaLabel->box.pos.x = 3; midScaleaLabel->color = COLOR_RS_GREY;
194 | botScaleaLabel->text = "10"; botScaleaLabel->box.pos.x = 3; botScaleaLabel->color = COLOR_RED;
195 | break;
196 | }
197 | module->menuaChanged = false;
198 | }
199 |
200 | if(module->menubChanged) {
201 | switch(module->vrangeb) {
202 | case 0: // 0 - 1
203 | topScalebLabel->text = "1"; topScalebLabel->box.pos.x = 67; topScalebLabel->color = COLOR_GREEN;
204 | midScalebLabel->text = ".5"; midScalebLabel->box.pos.x = 65; midScalebLabel->color = COLOR_GREEN;
205 | botScalebLabel->text = "0"; botScalebLabel->box.pos.x = 67; botScalebLabel->color = COLOR_RS_GREY;
206 | break;
207 | case 1: // 0 - 5
208 | topScalebLabel->text = "5"; topScalebLabel->box.pos.x = 67; topScalebLabel->color = COLOR_GREEN;
209 | midScalebLabel->text = "2.5"; midScalebLabel->box.pos.x = 63; midScalebLabel->color = COLOR_GREEN;
210 | botScalebLabel->text = "0"; botScalebLabel->box.pos.x = 67; botScalebLabel->color = COLOR_RS_GREY;
211 | break;
212 | case 2: // 0 - 10
213 | topScalebLabel->text = "10"; topScalebLabel->box.pos.x = 64; topScalebLabel->color = COLOR_GREEN;
214 | midScalebLabel->text = "5"; midScalebLabel->box.pos.x = 67; midScalebLabel->color = COLOR_GREEN;
215 | botScalebLabel->text = "0"; botScalebLabel->box.pos.x = 67; botScalebLabel->color = COLOR_RS_GREY;
216 | break;
217 | case 3: // -2 - 2
218 | topScalebLabel->text = "2"; topScalebLabel->box.pos.x = 67; topScalebLabel->color = COLOR_GREEN;
219 | midScalebLabel->text = "0"; midScalebLabel->box.pos.x = 67; midScalebLabel->color = COLOR_RS_GREY;
220 | botScalebLabel->text = "2"; botScalebLabel->box.pos.x = 67; botScalebLabel->color = COLOR_RED;
221 | break;
222 | case 4: // -5 - 5
223 | topScalebLabel->text = "5"; topScalebLabel->box.pos.x = 67; topScalebLabel->color = COLOR_GREEN;
224 | midScalebLabel->text = "0"; midScalebLabel->box.pos.x = 67; midScalebLabel->color = COLOR_RS_GREY;
225 | botScalebLabel->text = "5"; botScalebLabel->box.pos.x = 67; botScalebLabel->color = COLOR_RED;
226 | break;
227 | case 5: // -10 - 10
228 | topScalebLabel->text = "10"; topScalebLabel->box.pos.x = 64; topScalebLabel->color = COLOR_GREEN;
229 | midScalebLabel->text = "0"; midScalebLabel->box.pos.x = 67; midScalebLabel->color = COLOR_RS_GREY;
230 | botScalebLabel->text = "10"; botScalebLabel->box.pos.x = 64; botScalebLabel->color = COLOR_RED;
231 | break;
232 | }
233 | module->menubChanged = false;
234 | }
235 |
236 | float inav = RSclamp(module->inputs[RSBoogieBay::INA_INPUT].getVoltage(), -10.f, 10.f);
237 | float inbv = RSclamp(module->inputs[RSBoogieBay::INB_INPUT].getVoltage(), -10.f, 10.f);
238 | int yposa = 0, yposb = 0;
239 |
240 | switch(module->vrangea) {
241 | case 0: // 0 - 1
242 | inav = clamp(inav, 0.f, 1.f);
243 | yposa = mm2px(95 - inav * 80);
244 | break;
245 | case 1: // 0 - 5
246 | inav = clamp(inav, 0.f, 5.f);
247 | yposa = mm2px(95 - inav * 16);
248 | break;
249 | case 2: // 0 - 10
250 | inav = clamp(inav, 0.f, 10.f);
251 | yposa = mm2px(95 - inav * 8);
252 | break;
253 | case 3: // -2 - 2
254 | inav = clamp(inav, -2.f, 2.f);
255 | inav += 2;
256 | yposa = mm2px(95 - inav * 20);
257 | break;
258 | case 4: // -5 - 5
259 | inav = clamp(inav, -5.f, 5.f);
260 | inav += 5;
261 | yposa = mm2px(95 - inav * 8);
262 | break;
263 | case 5: // -10 - 10
264 | inav = clamp(inav, -10.f, 10.f);
265 | inav += 10;
266 | yposa = mm2px(95 - inav * 4);
267 | break;
268 | default:
269 | break;
270 | };
271 |
272 | switch(module->vrangeb) {
273 | case 0: // 0 - 1
274 | inbv = clamp(inbv, 0.f, 1.f);
275 | yposb = mm2px(95 - inbv * 80);
276 | break;
277 | case 1: // 0 - 5
278 | inbv = clamp(inbv, 0.f, 5.f);
279 | yposb = mm2px(95 - inbv * 16);
280 | break;
281 | case 2: // 0 - 10
282 | inbv = clamp(inbv, 0.f, 10.f);
283 | yposb = mm2px(95 - inbv * 8);
284 | break;
285 | case 3: // -2 - 2
286 | inbv = clamp(inbv, -2.f, 2.f);
287 | inbv += 2;
288 | yposb = mm2px(95 - inbv * 20);
289 | break;
290 | case 4: // -5 - 5
291 | inbv = clamp(inbv, -5.f, 5.f);
292 | inbv += 5;
293 | yposb = mm2px(95 - inbv * 8);
294 | break;
295 | case 5: // -10 - 10
296 | inbv = clamp(inbv, -10.f, 10.f);
297 | inbv += 10;
298 | yposb = mm2px(95 - inbv * 4);
299 | break;
300 | default:
301 | break;
302 | };
303 |
304 | ina->box.pos.y = yposa;
305 | inb->box.pos.y = yposb;
306 |
307 | ModuleWidget::step();
308 | }
309 |
310 |
311 | void appendContextMenu(Menu* menu) override {
312 | RSBoogieBay* module = dynamic_cast(this->module);
313 |
314 | std::string rangeNames[6] = {"0V - 1V", "0V - 5V", "0V - 10V", "-2V - 2V", "-5V - 5V", "-10V - 10V"};
315 |
316 | menu->addChild(new MenuEntry);
317 |
318 | menu->addChild(createMenuLabel("Voltage Range A"));
319 |
320 | struct RangeaItem : MenuItem {
321 | RSBoogieBay* module;
322 | int vrangea;
323 | void onAction(const event::Action& e) override {
324 | module->vrangea = vrangea;
325 | module->menuaChanged = true;
326 | }
327 | };
328 |
329 | for(int i = 0; i < 6; i++) {
330 | RangeaItem* rangeaItem = createMenuItem(rangeNames[i]);
331 | rangeaItem->rightText = CHECKMARK(module->vrangea == i);
332 | rangeaItem->module = module;
333 | rangeaItem->vrangea = i;
334 | menu->addChild(rangeaItem);
335 | }
336 |
337 | menu->addChild(createMenuLabel("Voltage Range B"));
338 |
339 | struct RangebItem : MenuItem {
340 | RSBoogieBay* module;
341 | int vrangeb;
342 | void onAction(const event::Action& e) override {
343 | module->vrangeb = vrangeb;
344 | module->menubChanged = true;
345 | }
346 | };
347 |
348 | for(int i = 0; i < 6; i++) {
349 | RangebItem* rangebItem = createMenuItem(rangeNames[i]);
350 | rangebItem->rightText = CHECKMARK(module->vrangeb == i);
351 | rangebItem->module = module;
352 | rangebItem->vrangeb = i;
353 | menu->addChild(rangebItem);
354 | }
355 | }
356 | };
357 |
358 |
359 | Model *modelRSBoogieBay = createModel("RSBoogieBay");
360 |
--------------------------------------------------------------------------------
/src/RSBoogieBayH8.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSBoogieBayH8 : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | ENUMS(LEFT_SCALE_BUTTONS, 8),
9 | ENUMS(RIGHT_SCALE_BUTTONS, 8),
10 | NUM_PARAMS
11 | };
12 | enum InputIds {
13 | ENUMS(INPUTS, 8),
14 | NUM_INPUTS
15 | };
16 | enum OutputIds {
17 | ENUMS(LEFT_OUTPUTS, 8),
18 | ENUMS(RIGHT_OUTPUTS, 8),
19 | POLY_LEFT_OUTPUT,
20 | POLY_RIGHT_OUTPUT,
21 | NUM_OUTPUTS
22 | };
23 | enum LightIds {
24 | NUM_LIGHTS
25 | };
26 |
27 | dsp::BooleanTrigger themeTrigger;
28 |
29 | dsp::ClockDivider scaleDivider;
30 | dsp::BooleanTrigger leftScaleTrigger[8];
31 | dsp::BooleanTrigger rightScaleTrigger[8];
32 |
33 | RSScribbleStrip *ss[8];
34 |
35 | bool scaleChanged = true;
36 | RSLabel *leftScaleLabels[8]; // Move to widget
37 | int leftScale[8] = {};
38 | RSLabel *rightScaleLabels[8]; // Move to widget
39 | int rightScale[8] = {};
40 |
41 |
42 | RSBoogieBayH8() {
43 | scaleDivider.setDivision(4096);
44 |
45 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
46 |
47 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
48 |
49 | for(int i = 0; i < 8; i++) {
50 | configParam(LEFT_SCALE_BUTTONS + i, 0.f, 1.f, 0.f, "SCALE");
51 | configParam(RIGHT_SCALE_BUTTONS + i, 0.f, 1.f, 0.f, "SCALE");
52 | leftScale[i] = 3; rightScale[i] = 2;
53 | }
54 | }
55 |
56 | void process(const ProcessArgs &args) override {
57 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
58 | RSTheme++;
59 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
60 | }
61 |
62 | outputs[POLY_LEFT_OUTPUT].setChannels(8);
63 | outputs[POLY_RIGHT_OUTPUT].setChannels(8);
64 |
65 | if(scaleDivider.process()) {
66 | for(int i = 0; i < 8; i++) {
67 | if(leftScaleTrigger[i].process(params[LEFT_SCALE_BUTTONS + i].getValue())) {
68 | ++leftScale[i]; if(leftScale[i] > 4) leftScale[i] = 0;
69 | scaleChanged = true;
70 | }
71 | if(rightScaleTrigger[i].process(params[RIGHT_SCALE_BUTTONS + i].getValue())) {
72 | ++rightScale[i]; if(rightScale[i] > 3) rightScale[i] = 0;
73 | scaleChanged = true;
74 | }
75 | }
76 | }
77 |
78 | for(int i = 0; i < 8; i++) {
79 | float inv = inputs[INPUTS + i].getVoltage();
80 | outputs[LEFT_OUTPUTS + i].setVoltage(inv);
81 | outputs[RIGHT_OUTPUTS + i].setVoltage(inv);
82 | outputs[POLY_LEFT_OUTPUT].setVoltage(inv, i);
83 | outputs[POLY_RIGHT_OUTPUT].setVoltage(inv, i);
84 | }
85 | }
86 |
87 | void onReset() override {
88 | for(int i = 0; i < 8; i++) {
89 | leftScale[i] = 3; rightScale[i] = 2;
90 | ss[i]->text = "_";
91 | }
92 | }
93 |
94 | json_t* dataToJson() override {
95 | json_t* rootJ = json_object();
96 |
97 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
98 |
99 | char ssn[4], lsn[4], rsn[4];
100 |
101 | for(int i = 0; i < 8; i++) {
102 | // Scribble strips
103 | json_t* ssj = json_string(ss[i]->text.c_str());
104 | sprintf(ssn, "SS%i", i);
105 | json_object_set_new(rootJ, ssn, ssj);
106 |
107 | // Left scales
108 | json_t* lsj = json_string(leftScaleLabels[i]->text.c_str());
109 | sprintf(lsn, "LS%i", i);
110 | json_object_set_new(rootJ, lsn, lsj);
111 |
112 | // Right scales
113 | json_t* rsj = json_string(rightScaleLabels[i]->text.c_str());
114 | sprintf(rsn, "RS%i", i);
115 | json_object_set_new(rootJ, rsn, rsj);
116 | }
117 |
118 | return rootJ;
119 | }
120 |
121 | void dataFromJson(json_t* rootJ) override {
122 | json_t* themeJ = json_object_get(rootJ, "theme");
123 |
124 | if(themeJ) RSTheme = json_integer_value(themeJ);
125 |
126 | char ssn[4], lsn[4], rsn[4];
127 |
128 | for(int i = 0; i < 8; i++) {
129 | // Scribble strips
130 | sprintf(ssn, "SS%i", i);
131 | json_t* ssj = json_object_get(rootJ, ssn);
132 | if(ssj) ss[i]->text = json_string_value(ssj);
133 |
134 | // Left scales
135 | sprintf(lsn, "LS%i", i);
136 | json_t* lsj = json_object_get(rootJ, lsn);
137 | if(lsj) {
138 | int ls = std::stoi(json_string_value(lsj));
139 | //INFO("Racket Science: leftScale %i %i", i, ls);
140 |
141 | // SIMPLIFY!!! leftScale / rightScale will be -10 to + 10, scaleLabels can be created at run time
142 |
143 | switch(ls) {
144 | case 0: leftScale[i] = 0; leftScaleLabels[i]->text = "0"; break;
145 | case 1: leftScale[i] = 1; leftScaleLabels[i]->text = "1"; break;
146 | case 2: leftScale[i] = 2; leftScaleLabels[i]->text = "2"; break;
147 | case 5: leftScale[i] = 3; leftScaleLabels[i]->text = "5"; break;
148 | case 10: leftScale[i] = 4; leftScaleLabels[i]->text = "10"; break;
149 | }
150 | }
151 |
152 | // Right scales
153 | sprintf(rsn, "RS%i", i);
154 | json_t* rsj = json_object_get(rootJ, rsn);
155 | if(rsj) {
156 | int rs = std::stoi(json_string_value(rsj));
157 | //INFO("Racket Science: rightScale %i %i", i, rs);
158 | switch(rs) {
159 | case 1: rightScale[i] = 0; rightScaleLabels[i]->text = "1"; break;
160 | case 2: rightScale[i] = 1; rightScaleLabels[i]->text = "2"; break;
161 | case 5: rightScale[i] = 2; rightScaleLabels[i]->text = "5"; break;
162 | case 10: rightScale[i] = 3; rightScaleLabels[i]->text = "10"; break;
163 | }
164 | }
165 | }
166 | }
167 | };
168 |
169 |
170 | struct RSBoogieBayH8Widget : ModuleWidget {
171 | RSBoogieBayH8* module;
172 |
173 | PortWidget *in[8];
174 | int middle, left, right;
175 |
176 | RSBoogieBayH8Widget(RSBoogieBayH8 *module) {
177 | INFO("Racket Science: RSBoogieBayH8Widget()");
178 |
179 | setModule(module);
180 | this->module = module;
181 |
182 | box.size.x = mm2px(5.08 * 25);
183 | middle = box.size.x / 2;
184 | left = 40;
185 | right = box.size.y - 40;
186 |
187 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSBoogieBayH8::THEME_BUTTON));
188 |
189 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "BOOGIE BAY H8", 14, module));
190 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
191 |
192 | for(int i = 0; i < 8; i++) {
193 | in[i] = createInputCentered(Vec(middle, 40 + (i * 40)), module, RSBoogieBayH8::INPUTS + i); addInput(in[i]);
194 | addOutput(createOutputCentered(Vec(left, 40 + (i * 40)), module, RSBoogieBayH8::LEFT_OUTPUTS + i));
195 | addOutput(createOutputCentered(Vec(right, 40 + (i * 40)), module, RSBoogieBayH8::RIGHT_OUTPUTS + i));
196 | addParam(createParamCentered(Vec(left + 20, 40 + (i * 40)), module, RSBoogieBayH8::LEFT_SCALE_BUTTONS + i));
197 | addParam(createParamCentered(Vec(right - 20, 40 + (i * 40)), module, RSBoogieBayH8::RIGHT_SCALE_BUTTONS + i));
198 | if(module) {
199 | addChild(module->ss[i] = new RSScribbleStrip(left + 25, 25 + (i * 40)));
200 | addChild(module->leftScaleLabels[i] = new RSLabel(left + 16, 43 + (i * 40), "10", 10, COLOR_RED));
201 | addChild(module->rightScaleLabels[i] = new RSLabel(right - 24, 43 + (i * 40), "10", 10, COLOR_GREEN));
202 | }
203 | }
204 |
205 | addOutput(createOutputCentered(Vec(left, 360), module, RSBoogieBayH8::POLY_LEFT_OUTPUT));
206 | addOutput(createOutputCentered(Vec(right, 360), module, RSBoogieBayH8::POLY_RIGHT_OUTPUT));
207 | }
208 |
209 | void customDraw(const DrawArgs& args) {
210 | // Socket slots
211 | nvgLineCap(args.vg, NVG_ROUND);
212 | nvgStrokeColor(args.vg, COLOR_BLACK);
213 | nvgStrokeWidth(args.vg, 5);
214 |
215 | for(int i = 0; i < 8; i++) {
216 | nvgBeginPath(args.vg);
217 | nvgMoveTo(args.vg, left + 30, 40 + (i * 40));
218 | nvgLineTo(args.vg, right - 30, 40 + (i * 40));
219 | nvgStroke(args.vg);
220 | }
221 |
222 | }
223 | #include "RSModuleWidgetDraw.hpp"
224 |
225 | void step() override {
226 | if(!module) return;
227 |
228 | static float ls = 0, rs = 0;
229 |
230 | for(int i = 0; i < 8; i++) {
231 | module->leftScaleLabels[i]->color = COLOR_RED;
232 | switch(module->leftScale[i]) {
233 | case 0: module->leftScaleLabels[i]->text = "0"; ls = 0; module->leftScaleLabels[i]->color = COLOR_RS_GREY; break;
234 | case 1: module->leftScaleLabels[i]->text = "1"; ls = -1; break;
235 | case 2: module->leftScaleLabels[i]->text = "2"; ls = -2; break;
236 | case 3: module->leftScaleLabels[i]->text = "5"; ls = -5; break;
237 | case 4: module->leftScaleLabels[i]->text = "10"; ls = -10; break;
238 | }
239 | switch(module->rightScale[i]) {
240 | case 0: module->rightScaleLabels[i]->text = "1"; rs = 1; break;
241 | case 1: module->rightScaleLabels[i]->text = "2"; rs = 2; break;
242 | case 2: module->rightScaleLabels[i]->text = "5"; rs = 5; break;
243 | case 3: module->rightScaleLabels[i]->text = "10"; rs = 10; break;
244 | }
245 | float inv = RSclamp(module->inputs[RSBoogieBayH8::INPUTS + i].getVoltage(), ls, rs);
246 | in[i]->box.pos.x = RSscale(inv, ls, rs, 65, 290);
247 | }
248 |
249 | ModuleWidget::step();
250 | }
251 | };
252 |
253 | Model *modelRSBoogieBayH8 = createModel("RSBoogieBayH8");
--------------------------------------------------------------------------------
/src/RSCVHeat.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSCVHeat : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | GAIN_KNOB,
9 | LOSS_KNOB,
10 | RESET_BUTTON,
11 | NUM_PARAMS
12 | };
13 | enum InputIds {
14 | CV_INPUT,
15 | NUM_INPUTS
16 | };
17 | enum OutputIds {
18 | NUM_OUTPUTS
19 | };
20 | enum LightIds {
21 | NUM_LIGHTS
22 | };
23 |
24 | RSCVHeat() {
25 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
26 |
27 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
28 |
29 | configParam(GAIN_KNOB, 0.00001f, 0.001f, 0.0005f, "GAIN");
30 | configParam(LOSS_KNOB, 0.f, 0.001f, 0.0005f, "LOSS");
31 | configParam(RESET_BUTTON, 0.f, 1.f, 0.f, "RESET");
32 | }
33 |
34 | dsp::BooleanTrigger themeTrigger;
35 | dsp::BooleanTrigger resetTrigger;
36 |
37 | #define SAMPLES 96
38 | float heat[SAMPLES] = {};
39 | float heatGain;
40 | float heatLoss;
41 |
42 | void process(const ProcessArgs &args) override {
43 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
44 | RSTheme++;
45 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
46 | }
47 |
48 | float cvIn = RSclamp(inputs[CV_INPUT].getVoltage(), -10.f, 10.f);
49 |
50 | heatGain = params[GAIN_KNOB].getValue();
51 | heatLoss = params[LOSS_KNOB].getValue();
52 |
53 | int heatIdx = (int)RSscale(cvIn, -10.f, +10.f, 0, SAMPLES - 1);
54 | heat[heatIdx] += heatGain;
55 | if(heat[heatIdx] > 1.f) heat[heatIdx] = 1.f;
56 |
57 | for(int i = 0; i < SAMPLES; i++) {
58 | heat[i] -= heatLoss;
59 | if(heat[i] < 0.f) heat[i] = 0.f;
60 | }
61 |
62 | if(resetTrigger.process(params[RESET_BUTTON].getValue())) {
63 | onReset();
64 | }
65 | }
66 |
67 | void onReset() override {
68 | std::memset(heat, 0, sizeof(heat));
69 | }
70 |
71 | json_t* dataToJson() override {
72 | json_t* rootJ = json_object();
73 |
74 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
75 |
76 | return rootJ;
77 | }
78 |
79 | void dataFromJson(json_t* rootJ) override {
80 | json_t* themeJ = json_object_get(rootJ, "theme");
81 |
82 | if(themeJ) RSTheme = json_integer_value(themeJ);
83 | }
84 | };
85 |
86 |
87 | struct RSCVHeatDisplay : TransparentWidget {
88 | RSCVHeat* module;
89 | float *buffer;
90 |
91 | RSCVHeatDisplay(RSCVHeat* module, float buffer[], int x, int y, int xs, int ys) {
92 | this->module = module;
93 | this->buffer = buffer;
94 |
95 | box.pos = Vec(x, y);
96 | box.size = Vec(xs, ys);
97 | };
98 |
99 | void draw(const DrawArgs& args) override {
100 |
101 | // Bounding box
102 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
103 | nvgFillColor(args.vg, COLOR_BLACK);
104 | nvgStrokeWidth(args.vg, 1.5f);
105 |
106 | nvgBeginPath(args.vg);
107 | nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 5);
108 | nvgStroke(args.vg);
109 | nvgFill(args.vg);
110 |
111 | if(!module) {
112 | return;
113 | }
114 |
115 | // Rounded edges needed here
116 | for(int i = 2; i < box.size.y - 1; i++) {
117 | nvgStrokeColor(args.vg, nvgHSL(0, 1, buffer[SAMPLES - (int)(SAMPLES / box.size.y * i)]));
118 | nvgBeginPath(args.vg);
119 | nvgMoveTo(args.vg, box.pos.x + 2, box.pos.y + i);
120 | nvgLineTo(args.vg, box.pos.x + box.size.x - 2, box.pos.y + i);
121 | nvgStroke(args.vg);
122 | }
123 | };
124 | };
125 |
126 |
127 | struct RSCVHeatWidget : ModuleWidget {
128 | RSCVHeat* module;
129 |
130 | RSCVHeatWidget(RSCVHeat *module) {
131 | INFO("Racket Science: RSCVHeatWidget()");
132 |
133 | setModule(module);
134 | this->module = module;
135 |
136 | box.size = Vec(RACK_GRID_WIDTH * 6, RACK_GRID_HEIGHT);
137 | int middle = box.size.x / 2 + 1;
138 |
139 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSCVHeat::THEME_BUTTON));
140 |
141 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "CVHEAT", 14, module));
142 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
143 |
144 | middle = middle + (middle / 2);
145 |
146 | addParam(createParamCentered(Vec(middle, 36), module, RSCVHeat::GAIN_KNOB));
147 | addChild(new RSLabelCentered(middle, 60, "GAIN", 10, module));
148 |
149 | addParam(createParamCentered(Vec(middle, 78), module, RSCVHeat::LOSS_KNOB));
150 | addChild(new RSLabelCentered(middle, 102, "LOSS", 10, module));
151 |
152 | addParam(createParamCentered(Vec(middle, 118), module, RSCVHeat::RESET_BUTTON));
153 | addChild(new RSLabelCentered(middle, 121, "RESET", 10, module));
154 |
155 | // Top label & invisibutton for top scale
156 |
157 | // CV heat display
158 | addChild(new RSCVHeatDisplay(module, module->heat, 3, 10, (box.size.x / 2) - 11, 340));
159 |
160 | // Bottom label & invisibutton for bottom scale
161 |
162 | addInput(createInputCentered(Vec(middle, 335), module, RSCVHeat::CV_INPUT));
163 | addChild(new RSLabelCentered(middle, 357, "CV", 10, module));
164 | }
165 |
166 | void customDraw(const DrawArgs& args) {}
167 | #include "RSModuleWidgetDraw.hpp"
168 |
169 | void step() override {
170 | if(!module) return;
171 |
172 | ModuleWidget::step();
173 | }
174 | };
175 |
176 | Model *modelRSCVHeat = createModel("RSCVHeat");
--------------------------------------------------------------------------------
/src/RSGroundControl.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | static bool owned = false;
6 |
7 | struct RSGroundControl : RSModule {
8 | bool running = false;
9 |
10 | enum ParamIds {
11 | BGHUE_KNOB, BGSAT_KNOB, BGLUM_KNOB,
12 | LBHUE_KNOB, LBSAT_KNOB, LBLUM_KNOB,
13 | SSHUE_KNOB, SSSAT_KNOB, SSLUM_KNOB,
14 | LEDA_KNOB, LEDB_KNOB,
15 | THEME_KNOB,
16 | NUM_PARAMS
17 | };
18 | enum InputIds {
19 | STEALTH_INPUT,
20 | NUM_INPUTS
21 | };
22 | enum OutputIds {
23 | NUM_OUTPUTS
24 | };
25 | enum LightIds {
26 | NUM_LIGHTS
27 | };
28 |
29 | RSGroundControl() {
30 | if(!owned) {
31 | owned = true;
32 | running = true;
33 | }
34 |
35 | RSTheme = 0;
36 |
37 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
38 |
39 | configParam(BGHUE_KNOB, 0.f, 1.f, 0.5f, "HUE");
40 | configParam(BGSAT_KNOB, 0.f, 1.f, 0.5f, "SAT");
41 | configParam(BGLUM_KNOB, 0.f, 1.f, 0.5f, "LUM");
42 | configParam(LBHUE_KNOB, 0.f, 1.f, 0.5f, "HUE");
43 | configParam(LBSAT_KNOB, 0.f, 1.f, 0.5f, "SAT");
44 | configParam(LBLUM_KNOB, 0.f, 1.f, 0.5f, "LUM");
45 | configParam(SSHUE_KNOB, 0.f, 1.f, 0.5f, "HUE");
46 | configParam(SSSAT_KNOB, 0.f, 1.f, 0.5f, "SAT");
47 | configParam(SSLUM_KNOB, 0.f, 1.f, 0.5f, "LUM");
48 | configParam(LEDA_KNOB, 0.f, 1.f, 0.5f, "HUE");
49 | configParam(LEDB_KNOB, 0.f, 1.f, 0.5f, "HUE");
50 |
51 | configParam(THEME_KNOB, 0.f, RSGlobal.themeCount - 1.f, 0.0f, "THEME");
52 | }
53 |
54 | void process(const ProcessArgs &args) override {
55 | }
56 |
57 | void updateParams() {
58 | if(!running) return;
59 |
60 | params[RSGroundControl::BGHUE_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].bghsl.hue);
61 | params[RSGroundControl::BGSAT_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].bghsl.sat);
62 | params[RSGroundControl::BGLUM_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].bghsl.lum);
63 | params[RSGroundControl::LBHUE_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].lbhsl.hue);
64 | params[RSGroundControl::LBSAT_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].lbhsl.sat);
65 | params[RSGroundControl::LBLUM_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].lbhsl.lum);
66 | params[RSGroundControl::SSHUE_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].sshsl.hue);
67 | params[RSGroundControl::SSSAT_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].sshsl.sat);
68 | params[RSGroundControl::SSLUM_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].sshsl.lum);
69 | params[RSGroundControl::LEDA_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].ledAh);
70 | params[RSGroundControl::LEDB_KNOB].setValue(RSGlobal.themes[RSGlobal.themeIdx].ledBh);
71 | }
72 |
73 | void onReset() override {
74 | // How about just removing the RacketScience settings dir & calling SaveRSGlobal?
75 | float hue = 0.f;
76 | float hueStep = 1.f / RSGlobal.themeCount;
77 | // Amend this to 1-13, customise 0, 14 & 15
78 | for(int i = 0; i < RSGlobal.themeCount; i++, hue += hueStep) {
79 | RSGlobal.themes[i].bghsl = {hue, .6f, .5f};
80 | RSGlobal.themes[i].lbhsl = {hue, .8f, .9f};
81 | RSGlobal.themes[i].sshsl = {hue, .7f, .8f};
82 | // LEDs here too once complete
83 | updateRSTheme(i);
84 | }
85 |
86 | RSGlobal.themeIdx = 0;
87 | updateParams();
88 | saveRSGlobal();
89 | }
90 |
91 | json_t* dataToJson() override {
92 | json_t* rootJ = json_object();
93 |
94 | return rootJ;
95 | }
96 |
97 | void dataFromJson(json_t* rootJ) override {
98 |
99 | }
100 |
101 | ~RSGroundControl() {
102 | if(running) {
103 | owned = false;
104 | }
105 | }
106 | };
107 |
108 | // Move to RSComponents.hpp once perfected
109 | struct RSLedAWidget : TransparentWidget {
110 | NVGcolor bgColor = nvgRGBA(0, 0, 0, 0);
111 | NVGcolor color = nvgRGBA(0, 0, 0, 0);
112 | NVGcolor borderColor = nvgRGBA(0, 0, 0, 0);
113 |
114 | RSLedAWidget(int x, int y, int size = 15) {
115 | box.pos = Vec(x, y);
116 | box.size = Vec(size, size);
117 | }
118 |
119 | void draw(const DrawArgs& args) override {
120 | drawLed(args);
121 | drawHalo(args);
122 | }
123 |
124 | void drawLed(const DrawArgs& args) {
125 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
126 | color = RSGlobal.themes[RSGlobal.themeIdx].lAColor;
127 | nvgFillColor(args.vg, color);
128 | nvgBeginPath(args.vg);
129 | // nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 10);
130 | nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 10);
131 | nvgStroke(args.vg);
132 | nvgFill(args.vg);
133 | }
134 |
135 | void drawHalo(const DrawArgs& args) {
136 | float radius = box.size.x / 2;
137 | float oradius = radius * 4.f;
138 |
139 | nvgBeginPath(args.vg);
140 | nvgRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y);
141 |
142 | NVGpaint paint;
143 | NVGcolor icol = color::mult(color, 0.7f);
144 | NVGcolor ocol = nvgRGB(0, 0, 0);
145 | paint = nvgRadialGradient(args.vg, radius, radius, radius, oradius, icol, ocol);
146 | nvgFillPaint(args.vg, paint);
147 | nvgGlobalCompositeOperation(args.vg, NVG_LIGHTER);
148 | nvgFill(args.vg);
149 | }
150 | };
151 |
152 | struct RSLedBWidget : TransparentWidget {
153 | NVGcolor bgColor = nvgRGBA(0, 0, 0, 0);
154 | NVGcolor color = nvgRGBA(0, 0, 0, 0);
155 | NVGcolor borderColor = nvgRGBA(0, 0, 0, 0);
156 |
157 | RSLedBWidget(int x, int y, int size = 15) {
158 | box.pos = Vec(x, y);
159 | box.size = Vec(size, size);
160 | }
161 |
162 | void draw(const DrawArgs& args) override {
163 | drawLed(args);
164 | drawHalo(args);
165 | }
166 |
167 | void drawLed(const DrawArgs& args) {
168 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
169 | color = RSGlobal.themes[RSGlobal.themeIdx].lBColor;
170 | nvgFillColor(args.vg, color);
171 | nvgBeginPath(args.vg);
172 | // nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 10);
173 | nvgRoundedRect(args.vg, 0, 0, box.size.x, box.size.y, 10);
174 | nvgStroke(args.vg);
175 | nvgFill(args.vg);
176 | }
177 |
178 | void drawHalo(const DrawArgs& args) {
179 | float radius = box.size.x / 2;
180 | float oradius = radius * 4.f;
181 |
182 | nvgBeginPath(args.vg);
183 | nvgRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y);
184 |
185 | NVGpaint paint;
186 | NVGcolor icol = color::mult(color, 0.7f);
187 | NVGcolor ocol = nvgRGB(0, 0, 0);
188 | paint = nvgRadialGradient(args.vg, radius, radius, radius, oradius, icol, ocol);
189 | nvgFillPaint(args.vg, paint);
190 | nvgGlobalCompositeOperation(args.vg, NVG_LIGHTER);
191 | nvgFill(args.vg);
192 | }
193 | };
194 |
195 | struct RSGroundControlWidget : ModuleWidget {
196 | RSGroundControl* module;
197 |
198 | RSGroundControlWidget(RSGroundControl *module) {
199 | INFO("Racket Science: RSGroundControlWidget()");
200 |
201 | setModule(module);
202 | this->module = module;
203 |
204 | box.size = Vec(RACK_GRID_WIDTH * 10, RACK_GRID_HEIGHT);
205 | int middle = box.size.x / 2 + 1;
206 | int quarter = middle / 2;
207 |
208 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "GROUND CONTROL", 14));
209 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12));
210 | if(module)
211 | if(!module->running) {
212 | addChild(new RSLabelCentered(middle, box.size.y / 2, "DISABLED", 16));
213 | addChild(new RSLabelCentered(middle, box.size.y / 2 + 12, "ONLY ONE INSTANCE OF GC REQUIRED"));
214 | return;
215 | }
216 |
217 | addChild(new RSLabelCentered(middle, 80, "THIS SPACE AVAILABLE", 16));
218 | addChild(new RSLabelCentered(middle, 100, "FOR SHORT TERM RENT", 16));
219 |
220 | /* What else to include?
221 | Current date & time
222 | Elapsed time since start
223 | General purpose CV controllable timer
224 | Countdown alarm
225 | General alarm
226 |
227 | Have a panel behind each set of HSL knobs to show color selected
228 |
229 | */
230 |
231 | {
232 | int top = 190, left = 40;
233 | int xsp = 30, ysp = 30, los = 30; // x spacing, y spacing, label offset
234 |
235 |
236 | addChild(new RSLabelCentered(middle, top, "ANY COLOUR YOU LIKE", 12));
237 |
238 | addChild(new RSLabelCentered(left + (xsp * 1), top + (ysp / 2), "HUE"));
239 | addChild(new RSLabelCentered(left + (xsp * 2), top + (ysp / 2), "SAT"));
240 | addChild(new RSLabelCentered(left + (xsp * 3), top + (ysp / 2), "LUM"));
241 |
242 | addChild(new RSLabelCentered(left - 10, top + (ysp * 1) + 3, "BACKGROUND"));
243 | addChild(new RSLabelCentered(left - 10, top + (ysp * 2) + 3, "LABELS"));
244 | addChild(new RSLabelCentered(left - 10, top + (ysp * 3) + 3, "SCRIBBLES"));
245 | addChild(new RSLabelCentered(left - 20, top + (ysp * 4) + 3, "LEDS A"));
246 | addChild(new RSLabelCentered(left - 20, top + (ysp * 5) + 3, "LEDS B"));
247 |
248 | addParam(createParamCentered(Vec(left + (xsp * 1), top + (ysp * 1)), module, RSGroundControl::BGHUE_KNOB));
249 | addParam(createParamCentered(Vec(left + (xsp * 2), top + (ysp * 1)), module, RSGroundControl::BGSAT_KNOB));
250 | addParam(createParamCentered(Vec(left + (xsp * 3), top + (ysp * 1)), module, RSGroundControl::BGLUM_KNOB));
251 |
252 | addParam(createParamCentered(Vec(left + (xsp * 1), top + (ysp * 2)), module, RSGroundControl::LBHUE_KNOB));
253 | addParam(createParamCentered(Vec(left + (xsp * 2), top + (ysp * 2)), module, RSGroundControl::LBSAT_KNOB));
254 | addParam(createParamCentered(Vec(left + (xsp * 3), top + (ysp * 2)), module, RSGroundControl::LBLUM_KNOB));
255 |
256 | addParam(createParamCentered(Vec(left + (xsp * 1), top + (ysp * 3)), module, RSGroundControl::SSHUE_KNOB));
257 | addParam(createParamCentered(Vec(left + (xsp * 2), top + (ysp * 3)), module, RSGroundControl::SSSAT_KNOB));
258 | addParam(createParamCentered(Vec(left + (xsp * 3), top + (ysp * 3)), module, RSGroundControl::SSLUM_KNOB));
259 |
260 | addParam(createParamCentered(Vec(left + (xsp * 1), top + (ysp * 4)), module, RSGroundControl::LEDA_KNOB));
261 | addParam(createParamCentered(Vec(left + (xsp * 1), top + (ysp * 5)), module, RSGroundControl::LEDB_KNOB));
262 |
263 | addParam(createParamCentered(Vec(left + (xsp * 2.5), top + (ysp * 4.5)), module, RSGroundControl::THEME_KNOB));
264 | addChild(new RSLabelCentered(left + (xsp * 2.5), top + (ysp * 5.8), "THEME"));
265 |
266 | addChild(new RSLedAWidget(left - 3, top + (ysp * 4) - 7));
267 | addChild(new RSLedBWidget(left - 3, top + (ysp * 5) - 7));
268 |
269 | }
270 |
271 | if(!module) return;
272 |
273 | module->params[RSGroundControl::THEME_KNOB].setValue(RSGlobal.themeIdx);
274 |
275 | module->updateParams();
276 | }
277 |
278 | void customDraw(const DrawArgs& args) {}
279 | #include "RSModuleWidgetDraw.hpp"
280 |
281 | void step() override {
282 | if(!module) return;
283 | if(!module->running) return;
284 |
285 | static struct rsglobal lastRSGlobal;
286 |
287 | static int lastTheme = 0;
288 | int theme = (int)module->params[RSGroundControl::THEME_KNOB].getValue();
289 |
290 | if(theme != lastTheme) {
291 | RSGlobal.themeIdx = lastTheme = theme;
292 | module->updateParams();
293 | updateRSTheme(theme);
294 | saveRSGlobal();
295 | }
296 |
297 | // Ideally only want to do the following if any params have changed
298 | // How can we achieve that? Can we test if the mouse is over our module, that would help
299 | // No point checking each param for change before reflecting that in RSGlobal, may as well just assign blindly
300 |
301 | RSGlobal.themes[RSGlobal.themeIdx].bghsl.hue = module->params[RSGroundControl::BGHUE_KNOB].getValue();
302 | RSGlobal.themes[RSGlobal.themeIdx].bghsl.sat = module->params[RSGroundControl::BGSAT_KNOB].getValue();
303 | RSGlobal.themes[RSGlobal.themeIdx].bghsl.lum = module->params[RSGroundControl::BGLUM_KNOB].getValue();
304 |
305 | RSGlobal.themes[RSGlobal.themeIdx].lbhsl.hue = module->params[RSGroundControl::LBHUE_KNOB].getValue();
306 | RSGlobal.themes[RSGlobal.themeIdx].lbhsl.sat = module->params[RSGroundControl::LBSAT_KNOB].getValue();
307 | RSGlobal.themes[RSGlobal.themeIdx].lbhsl.lum = module->params[RSGroundControl::LBLUM_KNOB].getValue();
308 |
309 | RSGlobal.themes[RSGlobal.themeIdx].sshsl.hue = module->params[RSGroundControl::SSHUE_KNOB].getValue();
310 | RSGlobal.themes[RSGlobal.themeIdx].sshsl.sat = module->params[RSGroundControl::SSSAT_KNOB].getValue();
311 | RSGlobal.themes[RSGlobal.themeIdx].sshsl.lum = module->params[RSGroundControl::SSLUM_KNOB].getValue();
312 |
313 | RSGlobal.themes[RSGlobal.themeIdx].ledAh = module->params[RSGroundControl::LEDA_KNOB].getValue();
314 | RSGlobal.themes[RSGlobal.themeIdx].ledBh = module->params[RSGroundControl::LEDB_KNOB].getValue();
315 |
316 | updateRSTheme(RSGlobal.themeIdx);
317 |
318 | // memcmp should suffice, nothing unusual in rsglobal struct
319 | if(memcmp(&RSGlobal, &lastRSGlobal, sizeof(rsglobal)) != 0) { // Theme settings have changed
320 | lastRSGlobal = RSGlobal; // memcpy?
321 | saveRSGlobal();
322 | }
323 |
324 | ModuleWidget::step();
325 | }
326 | };
327 |
328 | Model *modelRSGroundControl = createModel("RSGroundControl");
329 |
330 |
--------------------------------------------------------------------------------
/src/RSHeat.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSHeat : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | RESET_BUTTON,
9 | GAIN_KNOB,
10 | LOSS_KNOB,
11 | NUM_PARAMS
12 | };
13 | enum InputIds {
14 | CV_INPUT,
15 | GATE_INPUT,
16 | NUM_INPUTS
17 | };
18 | enum OutputIds {
19 | NUM_OUTPUTS
20 | };
21 | enum LightIds {
22 | ENUMS(SEMITONE_LIGHTS, 12),
23 | ENUMS(OCTAVE_LIGHTS, 10),
24 | NUM_LIGHTS
25 | };
26 |
27 | dsp::BooleanTrigger themeTrigger;
28 |
29 | dsp::SchmittTrigger gateTrigger;
30 | dsp::BooleanTrigger resetTrigger;
31 |
32 | float semiHeat[12] = {};
33 | float octHeat[10] = {};
34 | float heatGain = 0.1f;
35 | float heatLoss = 0.1f;
36 |
37 | RSHeat() {
38 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
39 |
40 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
41 |
42 | configParam(GAIN_KNOB, 0.01f, 5.0f, 0.05f, "GAIN");
43 | configParam(LOSS_KNOB, 0.01f, 0.5f, 0.05f, "LOSS");
44 | }
45 |
46 | void process(const ProcessArgs &args) override {
47 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
48 | RSTheme++;
49 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
50 | }
51 |
52 | float vOctIn = RSclamp(inputs[CV_INPUT].getVoltage(), -10.f, 10.f);
53 | int noteIdx = note(vOctIn);
54 | int octIdx = clamp(octave(vOctIn) + 4, 0, 9);
55 |
56 | heatGain = params[GAIN_KNOB].getValue();
57 | heatLoss = params[LOSS_KNOB].getValue();
58 |
59 | if(gateTrigger.process(inputs[GATE_INPUT].getVoltage())) {
60 | if(semiHeat[noteIdx] < 10.f) semiHeat[noteIdx] += heatGain;
61 | if(semiHeat[noteIdx] > 10.f) semiHeat[noteIdx] = 10.f;
62 | if(octHeat[octIdx] + heatGain < 10.f) octHeat[octIdx] += heatGain;
63 | if(octHeat[octIdx] > 10.f) octHeat[octIdx] = 10.f;
64 | }
65 |
66 | if(resetTrigger.process(params[RESET_BUTTON].getValue())) {
67 | for(int i = 0; i < 12; i++) semiHeat[i] = 0.f;
68 | for(int i = 0; i < 10; i++) octHeat[i] = 0.f;
69 | }
70 | }
71 |
72 | void onReset() override {
73 | std::memset(semiHeat, 0, sizeof(semiHeat));
74 | std::memset(octHeat, 0, sizeof(octHeat));
75 | }
76 |
77 | json_t* dataToJson() override {
78 | json_t* rootJ = json_object();
79 |
80 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
81 |
82 | return rootJ;
83 | }
84 |
85 | void dataFromJson(json_t* rootJ) override {
86 | json_t* themeJ = json_object_get(rootJ, "theme");
87 |
88 | if(themeJ) RSTheme = json_integer_value(themeJ);
89 | }
90 | };
91 |
92 | struct RSHeatWidget : ModuleWidget {
93 | RSHeat* module;
94 | Widget* panelBorder;
95 |
96 | RSHeatWidget(RSHeat *module) {
97 | INFO("Racket Science: RSHeatWidget()");
98 |
99 | setModule(module);
100 | this->module = module;
101 |
102 | panelBorder = new PanelBorder;
103 | addChild(panelBorder);
104 |
105 | box.size.x = mm2px(5.08 * 5);
106 | int middle = box.size.x / 2 + 1;
107 | int third = box.size.x / 3;
108 |
109 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSHeat::THEME_BUTTON));
110 |
111 | addChild(new RSLabelCentered(middle, box.pos.y + 14, "HEAT", 15, module));
112 | //addChild(new RSLabelCentered(middle, box.pos.y + 30, "Module Subtitle", 14));
113 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module)); // >= 4HP
114 | //addChild(new RSLabelCentered(middle, box.size.y - 15, "Racket", 12));
115 | //addChild(new RSLabelCentered(middle, box.size.y - 4, "Science", 12));
116 |
117 | addInput(createInputCentered(Vec(middle / 2, 30), module, RSHeat::CV_INPUT));
118 | addChild(new RSLabelCentered(middle / 2, 52, "V/OCT", 10, module));
119 |
120 | addInput(createInputCentered(Vec(middle + middle / 2, 30), module, RSHeat::GATE_INPUT));
121 | addChild(new RSLabelCentered(middle + middle / 2, 52, "GATE", 10, module));
122 |
123 | addParam(createParamCentered(Vec(middle, 68), module, RSHeat::RESET_BUTTON));
124 | addChild(new RSLabelCentered(middle, 71, "RESET", 10, module));
125 |
126 | addParam(createParamCentered(Vec(middle / 2, 108), module, RSHeat::GAIN_KNOB));
127 | addChild(new RSLabelCentered(middle / 2, 132, "GAIN", 10, module));
128 |
129 | addParam(createParamCentered(Vec(middle + middle / 2, 108), module, RSHeat::LOSS_KNOB));
130 | addChild(new RSLabelCentered(middle + middle / 2, 132, "LOSS", 10, module));
131 |
132 | LightWidget *lightWidget;
133 |
134 | // Semitone lights
135 | for(int i = 0 ; i < 12; i++) {
136 | int offset;
137 | switch(i) {
138 | case 1: case 3: case 5: case 8: case 10: offset = 7; break;
139 | default: offset = -7;
140 | }
141 | lightWidget = createLightCentered>(Vec(third - offset, 144 + (i * 19)), module, RSHeat::SEMITONE_LIGHTS + i);
142 | lightWidget->bgColor = nvgRGBA(10, 10, 10, 128);
143 | addChild(lightWidget);
144 | }
145 |
146 | // Octave lights
147 | for(int i = 0; i < 10; i++) {
148 | lightWidget = createLightCentered>(Vec(third * 2 + 7, 144 + (i * 23.25)), module, RSHeat::OCTAVE_LIGHTS + i);
149 | lightWidget->bgColor = nvgRGBA(10, 10, 10, 128);
150 | addChild(lightWidget);
151 | }
152 | }
153 |
154 | void step() override {
155 | if(!module) return;
156 |
157 | for(int i = 0; i < 12; i++) {
158 | module->lights[11 - i].setBrightness(module->semiHeat[i] / 10);
159 | }
160 | for(int i = 0; i < 10; i++) {
161 | module->lights[21 - i].setBrightness(module->octHeat[i] / 10);
162 | }
163 |
164 | for(int i = 0; i < 12; i++) {
165 | if(module->semiHeat[i] > 0.f) module->semiHeat[i] -= module->heatLoss;
166 | if(module->semiHeat[i] < 0.f) module->semiHeat[i] = 0.f;
167 | }
168 | for(int i = 0; i < 10; i++) {
169 | if(module->octHeat[i] > 0.f) module->octHeat[i] -= module->heatLoss;
170 | if(module->octHeat[i] < 0.f) module->octHeat[i] = 0.f;
171 | }
172 |
173 | ModuleWidget::step();
174 | }
175 |
176 | void customDraw(const DrawArgs& args) {}
177 | #include "RSModuleWidgetDraw.hpp"
178 | };
179 |
180 | Model *modelRSHeat = createModel("RSHeat");
--------------------------------------------------------------------------------
/src/RSLaunchControl.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSLaunchControl : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | ARM_PARAM,
9 | STEPS_PARAM,
10 | NUM_PARAMS
11 | };
12 | enum InputIds {
13 | PHASE_IN,
14 | ARM_IN,
15 | NUM_INPUTS
16 | };
17 | enum OutputIds {
18 | PHASE_OUT,
19 | RUNNING_OUT,
20 | STEP_OUT,
21 | EOC_OUT,
22 | NUM_OUTPUTS
23 | };
24 | enum LightIds {
25 | STOPPED_LIGHT, // Red
26 | ARMED_LIGHT, // Yellow
27 | RUNNING_LIGHT, // Green
28 | NUM_LIGHTS
29 | };
30 |
31 | dsp::BooleanTrigger themeTrigger;
32 |
33 | dsp::BooleanTrigger armInTrigger, armTrigger;
34 |
35 | dsp::SchmittTrigger eocTrigger;
36 |
37 | dsp::PulseGenerator stepPulse;
38 | dsp::PulseGenerator eocPulse;
39 |
40 | bool armed = false;
41 | bool running = false;
42 | bool step = false;
43 | bool eoc = false;
44 |
45 | float phaseIn, priorPhaseIn;
46 |
47 | RSLaunchControl() {
48 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
49 |
50 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
51 |
52 | configParam(ARM_PARAM, 0.f, 1.f, 0.f, "ARM");
53 | configParam(STEPS_PARAM, 2.f, 64.f, 8.f, "STEPS");
54 | }
55 |
56 | void process(const ProcessArgs &args) override {
57 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
58 | RSTheme++;
59 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
60 | }
61 |
62 | if(inputs[ARM_IN].isConnected()) {
63 | if(armInTrigger.process(inputs[ARM_IN].getVoltage())) {
64 | //if(!running) {
65 | INFO("Racket Science: Launch Control armed");
66 | armed = true;
67 | //}
68 | }
69 | }
70 |
71 | if(armTrigger.process(params[ARM_PARAM].getValue())) {
72 | if(!running) {
73 | INFO("Racket Science: Launch Control armed via button");
74 | armed = true;
75 | }
76 | }
77 |
78 | phaseIn = inputs[PHASE_IN].getVoltage();
79 |
80 | if(armed) {
81 | if(phaseIn < priorPhaseIn) {
82 | INFO("Racket Science: Launch Control running");
83 | running = true;
84 | armed = false;
85 | priorPhaseIn = phaseIn; // So we don't stop immediately below
86 | params[ARM_PARAM].setValue(0.f);
87 | }
88 | }
89 |
90 | if(running) {
91 | outputs[PHASE_OUT].setVoltage(phaseIn);
92 | if(phaseIn < priorPhaseIn) {
93 | INFO("Racket Science: Launch Control stopped");
94 | running = false;
95 | eocPulse.trigger();
96 | stepPulse.trigger();
97 | }
98 | }
99 |
100 | float steps = params[STEPS_PARAM].getValue();
101 | float phaseStep = 10.f / steps;
102 | if(running) {
103 | for(float step = phaseStep; step < 10.f; step += phaseStep) {
104 | if(phaseIn > step && priorPhaseIn <= step) {
105 | INFO("Racket Science: step %f", phaseIn);
106 | stepPulse.trigger();
107 | }
108 | }
109 | }
110 |
111 | step = stepPulse.process(1.f / args.sampleRate);
112 | eoc = eocPulse.process(1.f / args.sampleRate);
113 |
114 | outputs[EOC_OUT].setVoltage(eoc ? 10.f : 0.f);
115 | outputs[RUNNING_OUT].setVoltage(running ? 10.f : 0.f);
116 | outputs[STEP_OUT].setVoltage(step ? 10.f : 0.f);
117 |
118 | //params[ARM_PARAM].setValue(armed ? 1.f : 0.f);
119 | lights[STOPPED_LIGHT].setSmoothBrightness(!running ? 1.f : 0.f, 1.f);
120 | lights[ARMED_LIGHT].setSmoothBrightness(armed ? 1.f : 0.f, 1.f);
121 | lights[RUNNING_LIGHT].setSmoothBrightness(running ? 1.f : 0.f, 1.f);
122 |
123 | priorPhaseIn = phaseIn;
124 | }
125 |
126 | json_t* dataToJson() override {
127 | json_t* rootJ = json_object();
128 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
129 |
130 | return rootJ;
131 | }
132 |
133 | void dataFromJson(json_t* rootJ) override {
134 | json_t* themeJ = json_object_get(rootJ, "theme");
135 | if(themeJ) RSTheme = json_integer_value(themeJ);
136 | }
137 | };
138 |
139 |
140 | struct RSLaunchControlWidget : ModuleWidget {
141 | RSLaunchControl* module;
142 |
143 | RSLaunchControlWidget(RSLaunchControl *module) {
144 | INFO("Racket Science: RSLaunchControlWidget()");
145 |
146 | setModule(module);
147 | this->module = module;
148 |
149 | box.size = Vec(RACK_GRID_WIDTH * 26, RACK_GRID_HEIGHT);
150 | int middle = box.size.x / 2 + 1;
151 |
152 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSLaunchControl::THEME_BUTTON));
153 |
154 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "LAUNCH CONTROL", 14, module));
155 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
156 |
157 | int x, y;
158 |
159 | LightWidget *lightWidget;
160 |
161 | // PHASE IN
162 | x = 25; y = 50;
163 | addInput(createInputCentered(Vec(x, y), module, RSLaunchControl::PHASE_IN));
164 | addChild(new RSLabelCentered(x, y - 18, "PHASE", 10, module));
165 |
166 | // ARM IN
167 | x += 35;
168 | addInput(createInputCentered(Vec(x, y), module, RSLaunchControl::ARM_IN));
169 | addChild(new RSLabelCentered(x, y - 18, "ARM", 10, module));
170 |
171 | // ARM BUTTON
172 | x += 35;
173 | addParam(createParamCentered(Vec(x, y), module, RSLaunchControl::ARM_PARAM));
174 | addChild(new RSLabelCentered(x, y + 3, "ARM", 10, module));
175 |
176 | // STOPPED LIGHT
177 | x += 35;
178 | addChild(createLightCentered>(Vec(x, y), module, RSLaunchControl::STOPPED_LIGHT));
179 | addChild(new RSLabelCentered(x, y - 18, "STOPPED", 10, module));
180 |
181 | // ARMED LIGHT
182 | x += 35;
183 | addChild(createLightCentered>(Vec(x, y), module, RSLaunchControl::ARMED_LIGHT));
184 | addChild(new RSLabelCentered(x, y - 18, "ARMED", 10, module));
185 |
186 | // RUNNING LIGHT
187 | x += 35;
188 | addChild(createLightCentered>(Vec(x, y), module, RSLaunchControl::RUNNING_LIGHT));
189 | addChild(new RSLabelCentered(x, y - 18, "RUNNING", 10, module));
190 |
191 | // RUNNING OUT
192 | x += 35;
193 | addOutput(createOutputCentered(Vec(x, y), module, RSLaunchControl::RUNNING_OUT));
194 | addChild(new RSLabelCentered(x, y - 18, "RUNNING", 10, module));
195 |
196 | // PHASE OUT
197 | x += 35;
198 | addOutput(createOutputCentered(Vec(x, y), module, RSLaunchControl::PHASE_OUT));
199 | addChild(new RSLabelCentered(x, y - 18, "PHASE", 10, module));
200 |
201 | // STEPS
202 | x += 35;
203 | addParam(createParamCentered(Vec(x, y), module, RSLaunchControl::STEPS_PARAM));
204 | addChild(new RSLabelCentered(x, y - 18, "STEPS", 10, module));
205 |
206 | // STEP OUT
207 | x += 35;
208 | addOutput(createOutputCentered(Vec(x, y), module, RSLaunchControl::STEP_OUT));
209 | addChild(new RSLabelCentered(x, y - 18, "STEP", 10, module));
210 |
211 | // EOC OUT
212 | x += 35;
213 | addOutput(createOutputCentered(Vec(x, y), module, RSLaunchControl::EOC_OUT));
214 | addChild(new RSLabelCentered(x, y - 18, "EOC", 10, module));
215 |
216 | }
217 |
218 | void step() override {
219 | if(!module) return;
220 |
221 | ModuleWidget::step();
222 | }
223 |
224 | void customDraw(const DrawArgs& args) {}
225 | #include "RSModuleWidgetDraw.hpp"
226 | };
227 |
228 | Model *modelRSLaunchControl = createModel("RSLaunchControl");
--------------------------------------------------------------------------------
/src/RSMFH.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSMFH : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | VOLTAGE_KNOB,
9 | NUM_PARAMS
10 | };
11 | enum InputIds {
12 | TRIG_IN,
13 | NUM_INPUTS
14 | };
15 | enum OutputIds {
16 | MINF_OUT,
17 | PINF_OUT,
18 | NAN_OUT,
19 | VOLTAGE_OUT,
20 | EVIL_OUT,
21 | NUM_OUTPUTS
22 | };
23 | enum LightIds {
24 | NUM_LIGHTS
25 | };
26 |
27 | dsp::BooleanTrigger themeTrigger;
28 |
29 | RSMFH() {
30 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
31 |
32 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
33 |
34 | configParam(VOLTAGE_KNOB, -20.f, +20.f, 0.f, "VOTLAGE");
35 | }
36 |
37 | void process(const ProcessArgs &args) override {
38 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
39 | RSTheme++;
40 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
41 | }
42 |
43 | outputs[MINF_OUT].setChannels(16);
44 | outputs[PINF_OUT].setChannels(16);
45 | outputs[NAN_OUT].setChannels(16);
46 | outputs[VOLTAGE_OUT].setChannels(16);
47 | outputs[EVIL_OUT].setChannels(16);
48 |
49 | for(int c = 0; c < 16; c++) {
50 | outputs[MINF_OUT].setVoltage(-INFINITY, c);
51 | outputs[PINF_OUT].setVoltage(INFINITY, c);
52 | outputs[NAN_OUT].setVoltage(NAN, c);
53 | outputs[VOLTAGE_OUT].setVoltage(params[VOLTAGE_KNOB].getValue(), c);
54 |
55 | switch(rand() % 6) {
56 | case 0: outputs[EVIL_OUT].setVoltage(-INFINITY, c); break;
57 | case 1: outputs[EVIL_OUT].setVoltage(INFINITY, c); break;
58 | case 2: outputs[EVIL_OUT].setVoltage(-666.666f, c); break;
59 | case 3: outputs[EVIL_OUT].setVoltage(666.666f, c); break;
60 | case 4: outputs[EVIL_OUT].setVoltage(NAN, c); break;
61 | default: outputs[EVIL_OUT].setVoltage(rand(), c);
62 | }
63 | }
64 | }
65 |
66 | json_t* dataToJson() override {
67 | json_t* rootJ = json_object();
68 |
69 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
70 |
71 | return rootJ;
72 | }
73 |
74 | void dataFromJson(json_t* rootJ) override {
75 | json_t* themeJ = json_object_get(rootJ, "theme");
76 |
77 | if(themeJ) RSTheme = json_integer_value(themeJ);
78 | }
79 | };
80 |
81 | struct RSMFHWidget : ModuleWidget {
82 | RSMFH* module;
83 |
84 | RSMFHWidget(RSMFH *module) {
85 | INFO("Racket Science: RSMFHWidget()");
86 |
87 | setModule(module);
88 | this->module = module;
89 |
90 | box.size = Vec(RACK_GRID_WIDTH * 3, RACK_GRID_HEIGHT);
91 | int middle = box.size.x / 2 + 1;
92 |
93 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSMFH::THEME_BUTTON));
94 |
95 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "MODULE", 14, module));
96 | addChild(new RSLabelCentered(middle, box.pos.y + 25, "FROM", 14, module));
97 | addChild(new RSLabelCentered(middle, box.pos.y + 37, "HELL", 14, module));
98 |
99 | addChild(new RSLabelCentered(middle, box.size.y - 15, "Racket", 12, module));
100 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Science", 12, module));
101 |
102 | addOutput(createOutputCentered(Vec(23, 72), module, RSMFH::MINF_OUT));
103 | addChild(new RSLabelCentered(middle, 94, "-INF", 10, module));
104 |
105 | addOutput(createOutputCentered(Vec(23, 112), module, RSMFH::PINF_OUT));
106 | addChild(new RSLabelCentered(middle, 134, "+INF", 10, module));
107 |
108 | addOutput(createOutputCentered(Vec(23, 152), module, RSMFH::NAN_OUT));
109 | addChild(new RSLabelCentered(middle, 174, "NAN", 10, module));
110 |
111 | addOutput(createOutputCentered(Vec(23, 218), module, RSMFH::VOLTAGE_OUT));
112 | addParam(createParamCentered(Vec(23, 248), module, RSMFH::VOLTAGE_KNOB));
113 | addChild(new RSLabelCentered(middle, 270, "-20 +20", 10, module));
114 |
115 | addChild(new RSLabel(middle - 15, 306, "!EVIL!", 16, COLOR_RED));
116 | addOutput(createOutputCentered(Vec(23, 322), module, RSMFH::EVIL_OUT));
117 | addChild(new RSLabel(middle - 15, 348, "!EVIL!", 16, COLOR_RED));
118 | }
119 |
120 | void customDraw(const DrawArgs& args) {}
121 | #include "RSModuleWidgetDraw.hpp"
122 |
123 | void step() override {
124 | if(!module) return;
125 |
126 | ModuleWidget::step();
127 | }
128 | };
129 |
130 | Model *modelRSMFH = createModel("RSMFH");
--------------------------------------------------------------------------------
/src/RSMajorTom.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSMajorTom : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 |
9 | ATT_A_SCALE,
10 | ATT_A_OFFSET,
11 | ATT_B_SCALE,
12 | ATT_B_OFFSET,
13 |
14 | PULSE_A_BUTTON,
15 | GATE_A_BUTTON,
16 | DOOR_A_BUTTON,
17 |
18 | PULSE_B_BUTTON,
19 | GATE_B_BUTTON,
20 | DOOR_B_BUTTON,
21 |
22 | NUM_PARAMS
23 | };
24 | enum InputIds {
25 | ATT_A_IN,
26 | ATT_B_IN,
27 |
28 | NUM_INPUTS
29 | };
30 | enum OutputIds {
31 | ATT_A_OUT,
32 | ATT_B_OUT,
33 |
34 | PULSEGATE_A_OUT,
35 | PULSEGATE_B_OUT,
36 |
37 | NUM_OUTPUTS
38 | };
39 | enum LightIds {
40 | NUM_LIGHTS
41 | };
42 |
43 | dsp::BooleanTrigger themeTrigger;
44 |
45 | dsp::SchmittTrigger pulseTiggerA, pulseTriggerB;
46 | dsp::BooleanTrigger gateTriggerA, gateTriggerB;
47 | dsp::PulseGenerator pulseGeneratorA, pulseGeneratorB;
48 | bool pulseA, pulseB;
49 |
50 | RSMajorTom() {
51 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
52 |
53 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
54 |
55 | configParam(ATT_A_SCALE, -1.f, 1.f, 0.f, "SCALE");
56 | configParam(ATT_A_OFFSET, -10.f, 10.f, 0.f, "OFFSET");
57 | configParam(ATT_B_SCALE, -1.f, 1.f, 0.f, "SCALE");
58 | configParam(ATT_B_OFFSET, -10.f, 10.f, 0.f, "OFFSET");
59 |
60 | configParam(PULSE_A_BUTTON, 0.f, 1.f, 0.f, "PULSE");
61 | configParam(GATE_A_BUTTON, 0.f, 1.f, 0.f, "GATE");
62 | configParam(DOOR_A_BUTTON, 0.f, 1.f, 0.f, "DOOR");
63 |
64 | configParam(PULSE_B_BUTTON, 0.f, 1.f, 0.f, "PULSE");
65 | configParam(GATE_B_BUTTON, 0.f, 1.f, 0.f, "GATE");
66 | configParam(DOOR_B_BUTTON, 0.f, 1.f, 0.f, "DOOR");
67 |
68 | pulseA = pulseB = false;
69 | }
70 |
71 | void process(const ProcessArgs &args) override {
72 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
73 | RSTheme++;
74 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
75 | }
76 |
77 | // Attenuverters
78 | float cvIn, cvOut;
79 |
80 | cvIn = RSclamp(inputs[ATT_A_IN].getVoltage(),-10.f, 10.f);
81 | cvOut = RSclamp(cvIn * params[ATT_A_SCALE].getValue() + params[ATT_A_OFFSET].getValue(), -10.f, 10.f);
82 | outputs[ATT_A_OUT].setVoltage(cvOut);
83 |
84 | cvIn = RSclamp(inputs[ATT_B_IN].getVoltage(),-10.f, 10.f);
85 | cvOut = RSclamp(cvIn * params[ATT_B_SCALE].getValue() + params[ATT_B_OFFSET].getValue(), -10.f, 10.f);
86 | outputs[ATT_B_OUT].setVoltage(cvOut);
87 |
88 | // Pulses / gates
89 | if(params[DOOR_A_BUTTON].getValue()) pulseA = true;
90 | else {
91 | pulseA = false;
92 | if(pulseTiggerA.process(params[PULSE_A_BUTTON].getValue() > 0.f)) pulseGeneratorA.trigger(1e-3f);
93 | pulseA = pulseGeneratorA.process(args.sampleTime);
94 | if(params[GATE_A_BUTTON].getValue()) pulseA = true;
95 | }
96 |
97 | if(params[DOOR_B_BUTTON].getValue()) pulseB = true;
98 | else {
99 | pulseB = false;
100 | if(pulseTriggerB.process(params[PULSE_B_BUTTON].getValue() > 0.f)) pulseGeneratorB.trigger(1e-3f);
101 | pulseB = pulseGeneratorB.process(args.sampleTime);
102 | if(params[GATE_B_BUTTON].getValue()) pulseB = true;
103 | }
104 |
105 | outputs[PULSEGATE_A_OUT].setVoltage(pulseA ? 10.f : 0.f);
106 | outputs[PULSEGATE_B_OUT].setVoltage(pulseB ? 10.f : 0.f);
107 | }
108 |
109 | json_t* dataToJson() override {
110 | json_t* rootJ = json_object();
111 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
112 |
113 | return rootJ;
114 | }
115 |
116 | void dataFromJson(json_t* rootJ) override {
117 | json_t* themeJ = json_object_get(rootJ, "theme");
118 | if(themeJ) RSTheme = json_integer_value(themeJ);
119 | }
120 | };
121 |
122 |
123 | struct RSMajorTomWidget : ModuleWidget {
124 | RSMajorTom* module;
125 |
126 | int x, y, smlGap, lrgGap, labOfs;
127 |
128 | RSMajorTomWidget(RSMajorTom *module) {
129 | INFO("Racket Science: RSMajorTomWidget()");
130 |
131 | setModule(module);
132 | this->module = module;
133 |
134 | box.size = Vec(RACK_GRID_WIDTH * 9, RACK_GRID_HEIGHT);
135 | int middle = box.size.x / 2 + 1;
136 |
137 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSMajorTom::THEME_BUTTON));
138 |
139 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "MAJOR TOM", 14, module));
140 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
141 |
142 | x = 25; y = 50;
143 | smlGap = 30; lrgGap = 65;
144 | labOfs = 20;
145 |
146 | addInput(createInputCentered(Vec(x, y), module, RSMajorTom::ATT_A_IN));
147 | x += smlGap; y -= labOfs;
148 | addChild(new RSLabelCentered(x, y, "SCALE", 10, module));
149 | y += labOfs;
150 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::ATT_A_SCALE));
151 | x += smlGap; y -= labOfs;
152 | addChild(new RSLabelCentered(x, y, "OFFSET", 10, module));
153 | y += labOfs;
154 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::ATT_A_OFFSET));
155 | x += smlGap;
156 | addOutput(createOutputCentered(Vec(x, y), module, RSMajorTom::ATT_A_OUT));
157 |
158 | x = 25; y += smlGap;
159 | addInput(createInputCentered(Vec(x, y), module, RSMajorTom::ATT_B_IN));
160 | x += smlGap;
161 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::ATT_B_SCALE));
162 | x += smlGap;
163 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::ATT_B_OFFSET));
164 | x += smlGap;
165 | addOutput(createOutputCentered(Vec(x, y), module, RSMajorTom::ATT_B_OUT));
166 | y += smlGap;
167 |
168 | // Pulses / gates
169 | addChild(new RSLabelCentered(middle, y, "PULSES / GATES / DOORS", 10, module));
170 | x = 25; y += labOfs;
171 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::PULSE_A_BUTTON));
172 | addChild(new RSLabelCentered(x, y + 3, "PULSE", 10, module));
173 | x += smlGap;
174 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::GATE_A_BUTTON));
175 | addChild(new RSLabelCentered(x, y + 3, "GATE", 10, module));
176 | x += smlGap;
177 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::DOOR_A_BUTTON));
178 | addChild(new RSLabelCentered(x, y + 3, "DOOR", 10, module));
179 | x += smlGap;
180 | addOutput(createOutputCentered(Vec(x, y), module, RSMajorTom::PULSEGATE_A_OUT));
181 |
182 | x = 25; y += smlGap;
183 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::PULSE_B_BUTTON));
184 | addChild(new RSLabelCentered(x, y + 3, "PULSE", 10, module));
185 | x += smlGap;
186 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::GATE_B_BUTTON));
187 | addChild(new RSLabelCentered(x, y + 3, "GATE", 10, module));
188 | x += smlGap;
189 | addParam(createParamCentered(Vec(x, y), module, RSMajorTom::DOOR_B_BUTTON));
190 | addChild(new RSLabelCentered(x, y + 3, "DOOR", 10, module));
191 | x += smlGap;
192 | addOutput(createOutputCentered(Vec(x, y), module, RSMajorTom::PULSEGATE_B_OUT));
193 | y += smlGap;
194 |
195 | // Whatever goes here
196 | addChild(new RSLabelCentered(middle, y, "something sometihng", 10, module));
197 | x = 25; y += labOfs;
198 |
199 |
200 | }
201 |
202 | void step() override {
203 | if(!module) return;
204 |
205 | ModuleWidget::step();
206 | }
207 |
208 | void customDraw(const DrawArgs& args) {}
209 | #include "RSModuleWidgetDraw.hpp"
210 | };
211 |
212 | Model *modelRSMajorTom = createModel("RSMajorTom");
--------------------------------------------------------------------------------
/src/RSMissionControl.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSMissionControl : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | NUM_INPUTS
12 | };
13 | enum OutputIds {
14 | NUM_OUTPUTS
15 | };
16 | enum LightIds {
17 | NUM_LIGHTS
18 | };
19 |
20 | dsp::BooleanTrigger themeTrigger;
21 |
22 | RSMissionControl() {
23 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
24 |
25 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
26 |
27 | }
28 |
29 | void process(const ProcessArgs &args) override {
30 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
31 | RSTheme++;
32 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
33 | }
34 |
35 | }
36 |
37 | json_t* dataToJson() override {
38 | json_t* rootJ = json_object();
39 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
40 |
41 | return rootJ;
42 | }
43 |
44 | void dataFromJson(json_t* rootJ) override {
45 | json_t* themeJ = json_object_get(rootJ, "theme");
46 | if(themeJ) RSTheme = json_integer_value(themeJ);
47 | }
48 | };
49 |
50 |
51 | struct RSMissionControlWidget : ModuleWidget {
52 | RSMissionControl* module;
53 |
54 | RSMissionControlWidget(RSMissionControl *module) {
55 | INFO("Racket Science: RSMissionControlWidget()");
56 |
57 | setModule(module);
58 | this->module = module;
59 |
60 | box.size = Vec(RACK_GRID_WIDTH * 30, RACK_GRID_HEIGHT);
61 | int middle = box.size.x / 2 + 1;
62 |
63 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSMissionControl::THEME_BUTTON));
64 |
65 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "MISSION CONTROL", 14, module));
66 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
67 |
68 | }
69 |
70 | void step() override {
71 | if(!module) return;
72 |
73 | ModuleWidget::step();
74 | }
75 |
76 | void customDraw(const DrawArgs& args) {}
77 | #include "RSModuleWidgetDraw.hpp"
78 | };
79 |
80 | Model *modelRSMissionControl = createModel("RSMissionControl");
--------------------------------------------------------------------------------
/src/RSModule.hpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | struct RSModule : Module {
4 | int RSTheme = 1;
5 | };
6 |
7 | static void updateRSTheme(int themeIdx) {
8 | RSGlobal.themes[themeIdx].bgColor = nvgHSL(RSGlobal.themes[themeIdx].bghsl.hue,
9 | RSGlobal.themes[themeIdx].bghsl.sat,
10 | RSGlobal.themes[themeIdx].bghsl.lum);
11 | RSGlobal.themes[themeIdx].lbColor = nvgHSL(RSGlobal.themes[themeIdx].lbhsl.hue,
12 | RSGlobal.themes[themeIdx].lbhsl.sat,
13 | RSGlobal.themes[themeIdx].lbhsl.lum);
14 | RSGlobal.themes[themeIdx].ssColor = nvgHSL(RSGlobal.themes[themeIdx].sshsl.hue,
15 | RSGlobal.themes[themeIdx].sshsl.sat,
16 | RSGlobal.themes[themeIdx].sshsl.lum);
17 | float ledSat = 1.0f;
18 | float ledLum = 0.5f;
19 | RSGlobal.themes[themeIdx].lAColor = nvgHSL(RSGlobal.themes[themeIdx].ledAh, ledSat, ledLum);
20 | RSGlobal.themes[themeIdx].lBColor = nvgHSL(RSGlobal.themes[themeIdx].ledBh, ledSat, ledLum);
21 | }
22 |
23 | static void saveRSGlobal() {
24 | INFO("Racket Science: saveRSGlobal()");
25 |
26 | std::string RSGFile = asset::user("RacketScience/RSGlobal.json");;
27 | FILE *file = fopen(RSGFile.c_str(), "w");
28 | if(file) {
29 | json_t* rootJ = json_object();
30 | json_object_set_new(rootJ, "themeCount", json_integer(RSGlobal.themeCount));
31 | json_object_set_new(rootJ, "themeIdx", json_integer(RSGlobal.themeIdx));
32 | json_object_set_new(rootJ, "rateDivider", json_integer(RSGlobal.rateDivider));
33 | json_object_set_new(rootJ, "logLevel", json_integer(RSGlobal.logLevel));
34 |
35 | for(int theme = 0; theme < RSGlobal.themeCount; theme++) {
36 | json_t* themeJ = json_object();
37 | json_object_set_new(themeJ, "bghue", json_real(RSGlobal.themes[theme].bghsl.hue));
38 | json_object_set_new(themeJ, "bgsat", json_real(RSGlobal.themes[theme].bghsl.sat));
39 | json_object_set_new(themeJ, "bglum", json_real(RSGlobal.themes[theme].bghsl.lum));
40 |
41 | json_object_set_new(themeJ, "lbhue", json_real(RSGlobal.themes[theme].lbhsl.hue));
42 | json_object_set_new(themeJ, "lbsat", json_real(RSGlobal.themes[theme].lbhsl.sat));
43 | json_object_set_new(themeJ, "lblum", json_real(RSGlobal.themes[theme].lbhsl.lum));
44 |
45 | json_object_set_new(themeJ, "sshue", json_real(RSGlobal.themes[theme].sshsl.hue));
46 | json_object_set_new(themeJ, "sssat", json_real(RSGlobal.themes[theme].sshsl.sat));
47 | json_object_set_new(themeJ, "sslum", json_real(RSGlobal.themes[theme].sshsl.lum));
48 |
49 | json_object_set_new(themeJ, "ledAhue", json_real(RSGlobal.themes[theme].ledAh));
50 | json_object_set_new(themeJ, "ledBhue", json_real(RSGlobal.themes[theme].ledBh));
51 |
52 | json_object_set_new(rootJ, ("theme" + std::to_string(theme)).c_str(), themeJ);
53 | }
54 |
55 | json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
56 | fclose(file);
57 | }
58 | }
59 |
60 |
61 | static void loadRSGlobal() {
62 | INFO("Racket Science: loadRSGlobal()");
63 |
64 | std::string RSGDir = rack::asset::user("RacketScience/");
65 | std::string RSGFile = rack::asset::user("RacketScience/RSGlobal.json");
66 |
67 | if(!rack::system::isDirectory(RSGDir) || !rack::system::isFile(RSGFile)) {
68 | INFO("Racket Science: Creating default themes");
69 |
70 | rack::system::createDirectory(RSGDir);
71 |
72 | float hue = 0.f;
73 | float hueStep = 1.f / RSGlobal.themeCount;
74 | for(int i = 0; i < RSGlobal.themeCount; i++, hue += hueStep) {
75 | RSGlobal.themes[i].bghsl = {hue, .5f, .3f};
76 | RSGlobal.themes[i].lbhsl = {hue, .8f, .8f};
77 | RSGlobal.themes[i].sshsl = {hue, .6f, .7f};
78 | // LEDs here too once complete
79 | updateRSTheme(i);
80 | }
81 | saveRSGlobal();
82 | }
83 | else {
84 | FILE *file = fopen(RSGFile.c_str(), "r");
85 | if(file) {
86 | json_error_t error;
87 | json_t* rootJ = json_loadf(file, 0, &error);
88 | if(!rootJ) {
89 | std::string message = string::f("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
90 | osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
91 | }
92 | else {
93 | json_t* themeCountJ = json_object_get(rootJ, "themeCount");
94 | // if(themeCountJ) RSGlobal.themeCount = json_integer_value(themeCountJ);
95 | json_t* themeIdxJ = json_object_get(rootJ, "themeIdx");
96 | if(themeIdxJ) RSGlobal.themeIdx = json_integer_value(themeIdxJ);
97 | json_t* rateDividerJ = json_object_get(rootJ, "rateDivider");
98 | if(rateDividerJ) RSGlobal.rateDivider = json_integer_value(rateDividerJ);
99 | json_t* logLevelJ = json_object_get(rootJ, "logLevel");
100 | if(logLevelJ) RSGlobal.logLevel = json_integer_value(logLevelJ);
101 |
102 | for(int theme = 0; theme < RSGlobal.themeCount; theme++) {
103 | json_t* themeJ = json_object_get(rootJ, ("theme" + std::to_string(theme)).c_str());
104 | if(themeJ) {
105 | json_t* bghueJ = json_object_get(themeJ, "bghue");
106 | if(bghueJ) RSGlobal.themes[theme].bghsl.hue = json_real_value(bghueJ);
107 | json_t* bgsatJ = json_object_get(themeJ, "bgsat");
108 | if(bgsatJ) RSGlobal.themes[theme].bghsl.sat = json_real_value(bgsatJ);
109 | json_t* bglumJ = json_object_get(themeJ, "bglum");
110 | if(bglumJ) RSGlobal.themes[theme].bghsl.lum = json_real_value(bglumJ);
111 |
112 | json_t* lbhueJ = json_object_get(themeJ, "lbhue");
113 | if(lbhueJ) RSGlobal.themes[theme].lbhsl.hue = json_real_value(lbhueJ);
114 | json_t* lbsatJ = json_object_get(themeJ, "lbsat");
115 | if(lbsatJ) RSGlobal.themes[theme].lbhsl.sat = json_real_value(lbsatJ);
116 | json_t* lblumJ = json_object_get(themeJ, "lblum");
117 | if(lblumJ) RSGlobal.themes[theme].lbhsl.lum = json_real_value(lblumJ);
118 |
119 | json_t* sshueJ = json_object_get(themeJ, "sshue");
120 | if(sshueJ) RSGlobal.themes[theme].sshsl.hue = json_real_value(sshueJ);
121 | json_t* sssatJ = json_object_get(themeJ, "sssat");
122 | if(sssatJ) RSGlobal.themes[theme].sshsl.sat = json_real_value(sssatJ);
123 | json_t* sslumJ = json_object_get(themeJ, "sslum");
124 | if(sslumJ) RSGlobal.themes[theme].sshsl.lum = json_real_value(sslumJ);
125 |
126 | json_t* ledAhueJ = json_object_get(themeJ, "ledAhue");
127 | if(ledAhueJ) RSGlobal.themes[theme].ledAh = json_real_value(ledAhueJ);
128 | json_t* ledBhueJ = json_object_get(themeJ, "ledBhue");
129 | if(ledBhueJ) RSGlobal.themes[theme].ledBh = json_real_value(ledBhueJ);
130 |
131 | updateRSTheme(theme);
132 | }
133 | }
134 | }
135 | fclose(file);
136 | }
137 | }
138 | }
--------------------------------------------------------------------------------
/src/RSModuleWidgetDraw.hpp:
--------------------------------------------------------------------------------
1 | // Draws panel background instead of using SVG file
2 | // Calls customDraw() should we need to draw anything else
3 | void draw(const DrawArgs& args) override {
4 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
5 |
6 | if(module && module->RSTheme > 0)
7 | nvgFillColor(args.vg, RSGlobal.themes[module->RSTheme - 1].bgColor); // Module has own theme
8 | else
9 | nvgFillColor(args.vg, RSGlobal.themes[RSGlobal.themeIdx].bgColor); // Module uses global theme
10 |
11 | if(!module) { // Module browser being populated, pick random theme
12 | nvgFillColor(args.vg, RSGlobal.themes[rand() % RSGlobal.themeCount].bgColor);
13 | }
14 |
15 | nvgStrokeWidth(args.vg, 2);
16 | nvgBeginPath(args.vg);
17 | nvgRoundedRect(args.vg, 1, 1, box.size.x - 1, box.size.y - 1, 5);
18 | nvgStroke(args.vg);
19 | nvgFill(args.vg);
20 |
21 | // Maybe look into gradient background option controlled by Ground Control
22 |
23 | customDraw(args);
24 |
25 | ModuleWidget::draw(args);
26 | }
27 |
--------------------------------------------------------------------------------
/src/RSPhaseFour.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSPhaseFour : RSModule {
6 | static const int samples = 1000;
7 | static const int rows = 4;
8 |
9 | enum ParamIds {
10 | THEME_BUTTON,
11 |
12 | ENUMS(SCRUB_KNOBS, rows),
13 |
14 | ENUMS(ENABLE_BUTTONS, rows),
15 | ENUMS(WRITE_BUTTONS, rows),
16 |
17 | ENUMS(CLEAR_BUTTONS, rows),
18 | ENUMS(RAND_BUTTONS, rows),
19 |
20 | ENUMS(DIVIDE_KNOBS, rows),
21 | ENUMS(SELECT_KNOBS, rows),
22 | ENUMS(ADJUST_KNOBS, rows),
23 | ENUMS(MOVE_KNOBS, rows),
24 |
25 | ENUMS(BAKE_BUTTONS, rows),
26 |
27 | NUM_PARAMS
28 | };
29 | enum InputIds {
30 | ENUMS(PHASE_INS, rows),
31 |
32 | ENUMS(WRITE_INS, rows),
33 | ENUMS(CV_INS, rows),
34 |
35 | ENUMS(CLEAR_INS, rows),
36 | ENUMS(RAND_INS, rows),
37 |
38 | NUM_INPUTS
39 | };
40 | enum OutputIds {
41 | ENUMS(REC_CV_OUTS, rows),
42 | ENUMS(OVL_CV_OUTS, rows),
43 |
44 | NUM_OUTPUTS
45 | };
46 | enum LightIds {
47 | NUM_LIGHTS
48 | };
49 |
50 |
51 | dsp::BooleanTrigger themeTrigger;
52 |
53 | dsp::BooleanTrigger clearTrigger[rows];
54 | dsp::BooleanTrigger randTrigger[rows];
55 | dsp::BooleanTrigger bakeTrigger[rows];
56 |
57 | RSScribbleStrip *ss = NULL;
58 |
59 | float recBuffer[rows][samples] = {}; unsigned int recIdx[rows];
60 | float ovlBuffer[rows][samples] = {}; unsigned int ovlIdx[rows];
61 | bool enable[rows], write[rows];
62 |
63 | float phaseIn[rows], priorPhaseIn[rows];
64 | unsigned int divide[rows], priorDivide[rows];
65 |
66 |
67 | RSPhaseFour() {
68 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
69 |
70 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
71 |
72 | for(int row = 0; row < rows; row++) {
73 | configParam(SCRUB_KNOBS + row, -INFINITY, INFINITY, 0.f, "SCRUB");
74 |
75 | configParam(ENABLE_BUTTONS + row, 0.f, 1.f, 1.f, "WRITE ENABLE");
76 | configParam(WRITE_BUTTONS + row, 0.f, 1.f, 0.f, "WRITE");
77 |
78 | configParam(CLEAR_BUTTONS + row, 0.f, 1.f, 0.f, "CLEAR");
79 | configParam(RAND_BUTTONS + row, 0.f, 1.f, 0.f, "RANDOMIZE");
80 |
81 | configParam(DIVIDE_KNOBS + row, 1.f, 64.f, 1.f, "DIVIDE");
82 | configParam(SELECT_KNOBS + row, 1.f, 64.f, 1.f, "SELECT");
83 | configParam(ADJUST_KNOBS + row, -10.f, 10.f, 0.f, "ADJUST");
84 | configParam(MOVE_KNOBS + row, -INFINITY, INFINITY, 0.f, "MOVE");
85 |
86 | recIdx[row] = 0; ovlIdx[row] = 0;
87 | enable[row] = false; write[row] = false;
88 | phaseIn[row] = 0.f; priorPhaseIn[row] = 0.f;
89 | divide[row] = 0; priorDivide[row] = 0;
90 | }
91 | }
92 |
93 | void process(const ProcessArgs &args) override {
94 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
95 | RSTheme++;
96 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
97 | }
98 |
99 |
100 | for(int row = 0; row < rows; row++) {
101 | // Get idx from phase in or scrub knob
102 | if(inputs[PHASE_INS + row].isConnected()) {
103 | phaseIn[row] = RSclamp(inputs[PHASE_INS + row].getVoltage(), 0.f, 10.f);
104 | recIdx[row] = phaseIn[row] * samples / 10.f;
105 | params[SCRUB_KNOBS + row].setValue(phaseIn[row] / 4.15);
106 | }
107 | else {
108 | phaseIn[row] = params[SCRUB_KNOBS + row].getValue();
109 | recIdx[row] = std::fmod(abs(phaseIn[row] / 2.41), 1.f) * samples;
110 | if(phaseIn[row] < 0.f) recIdx[row] = samples - recIdx[row]; // So negative phase doesn't reverse head direction
111 | }
112 | if(recIdx[row] >= samples) {
113 | INFO("Racket Science: overrun idx=%i", recIdx[row]);
114 | recIdx[row] = samples - 1; // Just in case
115 | }
116 |
117 | // Get CV in
118 | float cvIn = RSclamp(inputs[CV_INS + row].getVoltage(), -10.f, 10.f);
119 |
120 | // Get write from write in or button if enabled
121 | write[row] = false;
122 | if(params[ENABLE_BUTTONS + row].getValue()) {
123 | if(inputs[WRITE_INS + row].isConnected()) write[row] = RSclamp(inputs[WRITE_INS + row].getVoltage(), 0.f, 1.f) ? true : false;
124 | else write[row] = params[WRITE_BUTTONS + row].getValue() ? true : false;
125 | if(write[row]) {
126 | recBuffer[row][recIdx[row]] = cvIn;
127 | // Calc / recalc min / max here
128 | }
129 | }
130 |
131 | // Should do following in widget step()
132 | divide[row] = (int)params[DIVIDE_KNOBS + row].getValue();
133 | if(divide[row] != priorDivide[row]) updateOverlay(row);
134 | priorDivide[row] = divide[row]; // Else we eat CPU
135 |
136 | if(clearTrigger[row].process(params[CLEAR_BUTTONS + row].getValue() > 0.f)) onClear(row);
137 | if(randTrigger[row].process(params[RAND_BUTTONS + row].getValue() > 0.f)) onRand(row);
138 |
139 | // Bake
140 | if(bakeTrigger[row].process(params[BAKE_BUTTONS + row].getValue() > 0.f)) onBake(row);
141 |
142 |
143 | // rec CV out
144 | outputs[REC_CV_OUTS + row].setVoltage(recBuffer[row][recIdx[row]]);
145 |
146 | // ovl CV out
147 | outputs[OVL_CV_OUTS + row].setVoltage(ovlBuffer[row][recIdx[row]]); // [ovlIdx[row]]
148 |
149 | }
150 | }
151 |
152 | void updateOverlay(int row) {
153 | float step = (float)samples / (float)divide[row];
154 | for(int i = 0; i < samples - 1; i += step) {
155 | for(int j = i; j < i + step; j++) {
156 | if(j < samples) ovlBuffer[row][j] = recBuffer[row][i];
157 | }
158 | }
159 | }
160 |
161 | void onClear(int row) {
162 | for(int i = 0; i < samples; i ++) {
163 | recBuffer[row][i] = 0.f;
164 | ovlBuffer[row][i] = 0.f;
165 | }
166 | }
167 |
168 | void onRand(int row) {
169 | std::random_device rd;
170 | std::mt19937 e2(rd());
171 | std::uniform_real_distribution<> dist(-10.f, 10.f);
172 | for(int i = 0; i < samples; i++) recBuffer[row][i] = dist(e2);
173 | updateOverlay(row);
174 | }
175 |
176 | void onBake(int row) {
177 | for(int i = 0; i < samples; i++) recBuffer[row][i] = ovlBuffer[row][i];
178 | }
179 |
180 | void onReset() override {
181 | for(int row = 0; row < rows; row++) onClear(row);
182 | }
183 |
184 | void onRandomize() override {
185 | std::random_device rd;
186 | std::mt19937 e2(rd());
187 | std::uniform_real_distribution<> dist(-10.f, 10.f);
188 | for(int row = 0; row < rows; row++) onRand(row);
189 | }
190 |
191 | json_t* dataToJson() override {
192 | json_t* rootJ = json_object();
193 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
194 |
195 | if(ss) {
196 | json_t* SS = json_string(ss->text.c_str());
197 | json_object_set_new(rootJ, "ss", SS);
198 | }
199 |
200 | json_t* recSamples = json_array();
201 | json_t* ovlSamples = json_array();
202 |
203 | for(int row = 0; row < rows; row++) {
204 | for(int i = 0; i < samples; i++) {
205 | json_array_append_new(recSamples, json_real(recBuffer[row][i]));
206 | json_array_append_new(ovlSamples, json_real(ovlBuffer[row][i]));
207 | }
208 | }
209 |
210 | json_object_set_new(rootJ, "recSamples", recSamples);
211 | json_object_set_new(rootJ, "ovlSamples", ovlSamples);
212 |
213 | return rootJ;
214 | }
215 |
216 | void dataFromJson(json_t* rootJ) override {
217 | json_t* themeJ = json_object_get(rootJ, "theme");
218 | if(themeJ) RSTheme = json_integer_value(themeJ);
219 |
220 | json_t* SS = json_object_get(rootJ, "ss");
221 | if(SS) ss->text = json_string_value(SS);
222 |
223 | json_t* recSamples = json_object_get(rootJ, "recSamples");
224 | json_t* ovlSamples = json_object_get(rootJ, "ovlSamples");
225 |
226 | if(recSamples && ovlSamples) {
227 | for(int row = 0; row < rows; row++) {
228 | for(int i = 0; i < samples; i++) {
229 | recBuffer[row][i] = json_number_value(json_array_get(recSamples, i));
230 | ovlBuffer[row][i] = json_number_value(json_array_get(ovlSamples, i));
231 | }
232 | }
233 | }
234 | }
235 | };
236 |
237 | struct RSPhaseDisplay : TransparentWidget {
238 | std::shared_ptr font;
239 | RSPhaseFour *module;
240 | unsigned int row;
241 | unsigned int *idx;
242 | bool *write;
243 |
244 | RSPhaseDisplay(RSPhaseFour *module, int row, int x, int y, int xs, int ys) {
245 | this->module = module;
246 | this->row = row;
247 | this->idx = &module->recIdx[row];
248 | this->write = &module->write[row];
249 |
250 | font = APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/Ubuntu Condensed 400.ttf"));
251 |
252 | box.pos = Vec(x / 2, y / 2);
253 | box.size = Vec(xs, ys);
254 | };
255 |
256 | void draw(const DrawArgs& args) override {
257 | auto startTime = std::chrono::system_clock::now();
258 |
259 | nvgFontSize(args.vg, 10);
260 | nvgFontFaceId(args.vg, font->handle);
261 | nvgTextLetterSpacing(args.vg, 0);
262 |
263 | // Bounding box
264 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
265 | nvgFillColor(args.vg, COLOR_BLACK);
266 | nvgStrokeWidth(args.vg, 1.5f);
267 |
268 | nvgBeginPath(args.vg);
269 | nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 5);
270 | nvgStroke(args.vg);
271 | nvgFill(args.vg);
272 |
273 | if(!module) { // Stand out in the module browser
274 | nvgFontSize(args.vg, 60);
275 | nvgFillColor(args.vg, COLOR_RS_BRONZE);
276 | nvgTextAlign(args.vg, NVG_ALIGN_CENTER);
277 |
278 | char msg[30];
279 | switch(this->row) {
280 | case 0: {
281 | std::time_t t = std::time(0);
282 | std::tm* now = std::localtime(&t);
283 | switch(now->tm_wday) {
284 | case 0: strcpy(msg, "Happy Sunday"); break;
285 | case 1: strcpy(msg, "Happy Monday"); break;
286 | case 2: strcpy(msg, "Happy Tuesday"); break;
287 | case 3: strcpy(msg, "Happy Wednesday"); break;
288 | case 4: strcpy(msg, "Happy Thursday"); break;
289 | case 5: strcpy(msg, "Happy Friday"); break;
290 | case 6: strcpy(msg, "Happy Saturday"); break;
291 | }
292 | if(now->tm_mday == 25 && now->tm_mon + 1 == 12) strcpy(msg, "MERRY XMAS!");
293 | break;
294 | }
295 | case 1: strcpy(msg, "R a c k e t S c i e n c e"); break;
296 | case 2: strcpy(msg, "This space available for rent"); break;
297 | case 3: strcpy(msg, "Please consider donating :)"); break;
298 | }
299 | nvgText(args.vg, box.pos.x + box.size.x / 2, box.pos.y + 50, msg, NULL);
300 | return;
301 | }
302 |
303 | // Center line
304 | int centerLine = box.pos.y + (box.size.y / 2);
305 | nvgStrokeColor(args.vg, COLOR_BLUE);
306 | nvgBeginPath(args.vg);
307 | nvgMoveTo(args.vg, box.pos.x, centerLine);
308 | nvgLineTo(args.vg, box.pos.x + box.size.x, centerLine);
309 | nvgStroke(args.vg);
310 |
311 | // recBuffer
312 | nvgStrokeColor(args.vg, COLOR_GREEN);
313 | nvgBeginPath(args.vg);
314 | nvgMoveTo(args.vg, box.pos.x, centerLine - (module->recBuffer[row][0] / 20 * box.size.y));
315 | for(int i = 0; i < box.size.x; i++) {
316 | unsigned int idx = module->samples / box.size.x * i;
317 | int val = module->recBuffer[row][idx] / 20.f * box.size.y;
318 | nvgLineTo(args.vg, box.pos.x + i, centerLine - val);
319 | }
320 | nvgStroke(args.vg);
321 |
322 | // ovlBuffer
323 | nvgStrokeColor(args.vg, COLOR_RED);
324 | nvgBeginPath(args.vg);
325 | nvgMoveTo(args.vg, box.pos.x, centerLine - (module->ovlBuffer[row][0] / 20 * box.size.y));
326 | for(int i = 0; i < box.size.x; i++) {
327 | unsigned int idx = module->samples / box.size.x * i;
328 | int val = module->ovlBuffer[row][idx] / 20 * box.size.y;
329 | nvgLineTo(args.vg, box.pos.x + i, centerLine - val);
330 | }
331 | nvgStroke(args.vg);
332 |
333 | // Divisions
334 | nvgStrokeColor(args.vg, COLOR_WHITE);
335 | for(int i = box.size.x / module->divide[row]; i < box.size.x; i += box.size.x / module->divide[row]) {
336 | nvgBeginPath(args.vg);
337 | nvgMoveTo(args.vg, box.pos.x + i, box.pos.y);
338 | nvgLineTo(args.vg, box.pos.x + i, box.pos.y + box.size.y);
339 | nvgStroke(args.vg);
340 | }
341 |
342 | // Index
343 | nvgStrokeColor(args.vg, *write == true ? COLOR_RED : COLOR_RS_GREY);
344 | nvgStrokeWidth(args.vg, 1.f);
345 |
346 | nvgBeginPath(args.vg);
347 | nvgMoveTo(args.vg, box.pos.x + (box.size.x / module->samples * *idx), box.pos.y);
348 | nvgLineTo(args.vg, box.pos.x + (box.size.x / module->samples * *idx), box.pos.y + box.size.y);
349 | nvgStroke(args.vg);
350 | };
351 | };
352 |
353 | struct RSPhaseFourWidget : ModuleWidget {
354 | RSPhaseFour* module;
355 |
356 | int x, y, smlGap, lrgGap, labOfs;
357 |
358 | RSLabelCentered *patternLabel;
359 |
360 | RSPhaseFourWidget(RSPhaseFour *module) {
361 | INFO("Racket Science: RSPhaseFourWidget()");
362 |
363 | setModule(module);
364 | this->module = module;
365 |
366 | box.size = Vec(RACK_GRID_WIDTH * 97, RACK_GRID_HEIGHT);
367 | int middle = box.size.x / 2 + 1;
368 |
369 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSPhaseFour::THEME_BUTTON));
370 |
371 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "PHASE IV", 14, module));
372 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
373 |
374 | x = 40; y = 50;
375 | smlGap = 30; lrgGap = 65;
376 | labOfs = 22;
377 |
378 |
379 | // Left side labels
380 |
381 | // Skip over display
382 |
383 | // Right side labels
384 |
385 |
386 | for(int row = 0, rowGap = 90; row < module->rows; row++, y += rowGap) addRow(row, x, y, 70);
387 | }
388 |
389 | void addRow(int row, int x, int y, int h) {
390 | // Add scrub knob & phase in
391 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::SCRUB_KNOBS + row));
392 | addInput(createInputCentered(Vec(x, y), module, RSPhaseFour::PHASE_INS + row));
393 | x += lrgGap - 15;
394 |
395 | // Add CV in
396 | addInput(createInputCentered(Vec(x, y), module, RSPhaseFour::CV_INS + row));
397 | addChild(new RSLabelCentered(x, y + labOfs, "CV IN", 10, module));
398 | x += smlGap;
399 |
400 | // Add write enable, button & in
401 | y -= 18;
402 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::ENABLE_BUTTONS + row));
403 | addChild(new RSLabelCentered(x, y + 3, "ENABLE", 9, module));
404 | y += 30;
405 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::WRITE_BUTTONS + row));
406 | addInput(createInputCentered(Vec(x, y), module, RSPhaseFour::WRITE_INS + row));
407 | addChild(new RSLabelCentered(x, y + labOfs, "WRITE"));
408 | x += smlGap;
409 | y -= 30;
410 |
411 | // Add clear & randomize
412 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::CLEAR_BUTTONS + row));
413 | addChild(new RSLabelCentered(x, y - 14, "CLEAR", 10, module));
414 | y += 30;
415 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::RAND_BUTTONS + row));
416 | addChild(new RSLabelCentered(x, y + 22, "RAND", 10, module));
417 | x += smlGap;
418 | y -= 30;
419 |
420 | // Add divide, select, adjust & move knobs
421 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::DIVIDE_KNOBS + row));
422 | addChild(new RSLabelCentered(x, y - 14, "DIVIDE", 10, module));
423 | y += 30;
424 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::ADJUST_KNOBS + row));
425 | addChild(new RSLabelCentered(x, y + 22, "ADJUST", 10, module));
426 | x += smlGap;
427 | y -= 30;
428 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::SELECT_KNOBS + row));
429 | addChild(new RSLabelCentered(x, y - 14, "SELECT", 10, module));
430 | y += 30;
431 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::MOVE_KNOBS + row));
432 | addChild(new RSLabelCentered(x, y + 22, "MOVE", 10, module));
433 | x += smlGap;
434 |
435 | // Add bake buttons
436 | addParam(createParamCentered(Vec(x, y), module, RSPhaseFour::BAKE_BUTTONS + row));
437 | addChild(new RSLabelCentered(x, y -14, "BAKE", 10, module));
438 | x += smlGap;
439 |
440 | //x = 370; // So we line up with Fido3 steps
441 | addChild(new RSPhaseDisplay(module, row, x, y - 45, 1040, h));
442 | x += 1040 + smlGap;
443 | y -= smlGap;
444 |
445 | // rec CV out
446 | addOutput(createOutputCentered(Vec(x, y), module, RSPhaseFour::REC_CV_OUTS + row));
447 | y += smlGap;
448 |
449 | // ovl CV out
450 | addOutput(createOutputCentered(Vec(x, y), module, RSPhaseFour::OVL_CV_OUTS + row));
451 | }
452 |
453 | void step() override {
454 | if(!module) return;
455 |
456 | ModuleWidget::step();
457 | }
458 |
459 | void customDraw(const DrawArgs& args) {}
460 | #include "RSModuleWidgetDraw.hpp"
461 | };
462 |
463 | Model *modelRSPhaseFour = createModel("RSPhaseFour");
--------------------------------------------------------------------------------
/src/RSShades.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSShades : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | ENUMS(HUE_IN, 12),
12 | ENUMS(SAT_IN, 12),
13 | ENUMS(LUM_IN, 12),
14 | NUM_INPUTS
15 | };
16 | enum OutputIds {
17 | NUM_OUTPUTS
18 | };
19 | enum LightIds {
20 | NUM_LIGHTS
21 | };
22 |
23 | dsp::BooleanTrigger themeTrigger;
24 |
25 | RSShades() {
26 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
27 |
28 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
29 | }
30 |
31 | void process(const ProcessArgs &args) override {
32 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
33 | RSTheme++;
34 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
35 | }
36 | }
37 |
38 | json_t* dataToJson() override {
39 | json_t* rootJ = json_object();
40 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
41 |
42 | return rootJ;
43 | }
44 |
45 | void dataFromJson(json_t* rootJ) override {
46 | json_t* themeJ = json_object_get(rootJ, "theme");
47 | if(themeJ) RSTheme = json_integer_value(themeJ);
48 | }
49 | };
50 |
51 |
52 | struct RSShadesWidget : ModuleWidget {
53 | RSShades* module;
54 |
55 | RSShadesWidget(RSShades *module) {
56 | INFO("Racket Science: RSShadesWidget()");
57 |
58 | setModule(module);
59 | this->module = module;
60 |
61 | box.size = Vec(RACK_GRID_WIDTH * 10, RACK_GRID_HEIGHT);
62 | int middle = box.size.x / 2 + 1;
63 |
64 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSShades::THEME_BUTTON));
65 |
66 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "SHADES", 14, module));
67 |
68 | addChild(new RSLabelCentered( 40, 25, "HUE", 10, module));
69 | addChild(new RSLabelCentered( 80, 25, "SAT", 10, module));
70 | addChild(new RSLabelCentered(120, 25, "LUM", 10, module));
71 |
72 | for(int i = 0; i < 12; i++) {
73 | int offset;
74 | switch(i) {
75 | case 1: case 3: case 5: case 8: case 10: offset = 7; break;
76 | default: offset = -7;
77 | }
78 | addChild(new RSLabelCentered(12, 46 + (i * 28), std::to_string(12 - i).c_str(), 10, module));
79 | addInput(createInputCentered(Vec( 40 - offset, 42 + (i * 28)), module, RSShades::HUE_IN + i));
80 | addInput(createInputCentered(Vec( 80 - offset, 42 + (i * 28)), module, RSShades::SAT_IN + i));
81 | addInput(createInputCentered(Vec(120 - offset, 42 + (i * 28)), module, RSShades::LUM_IN + i));
82 | }
83 |
84 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module));
85 | }
86 |
87 | void step() override {
88 | if(!module) return;
89 |
90 | for(int i = 0; i < 12; i++) {
91 | if(module->inputs[RSShades::HUE_IN_LAST - i].isConnected()) {
92 | RSGlobal.themes[i + 1].bghsl.hue = RSclamp(module->inputs[RSShades::HUE_IN_LAST - i].getVoltage(), 0.f, 10.f) / 10.f;
93 | RSGlobal.themes[i + 1].bgColor = nvgHSL(RSGlobal.themes[i + 1].bghsl.hue,
94 | RSGlobal.themes[i + 1].bghsl.sat,
95 | RSGlobal.themes[i + 1].bghsl.lum);
96 | }
97 | if(module->inputs[RSShades::SAT_IN_LAST - i].isConnected()) {
98 | RSGlobal.themes[i + 1].bghsl.sat = RSclamp(module->inputs[RSShades::SAT_IN_LAST - i].getVoltage(), 0.f, 10.f) / 10.f;
99 | RSGlobal.themes[i + 1].bgColor = nvgHSL(RSGlobal.themes[i + 1].bghsl.hue,
100 | RSGlobal.themes[i + 1].bghsl.sat,
101 | RSGlobal.themes[i + 1].bghsl.lum);
102 | }
103 | if(module->inputs[RSShades::LUM_IN_LAST - i].isConnected()) {
104 | RSGlobal.themes[i + 1].bghsl.lum = RSclamp(module->inputs[RSShades::LUM_IN_LAST - i].getVoltage(), 0.f, 10.f) / 10.f;
105 | RSGlobal.themes[i + 1].bgColor = nvgHSL(RSGlobal.themes[i + 1].bghsl.hue,
106 | RSGlobal.themes[i + 1].bghsl.sat,
107 | RSGlobal.themes[i + 1].bghsl.lum);
108 | }
109 | }
110 |
111 | ModuleWidget::step();
112 | }
113 |
114 | void customDraw(const DrawArgs& args) {}
115 | #include "RSModuleWidgetDraw.hpp"
116 | };
117 |
118 | Model *modelRSShades = createModel("RSShades");
--------------------------------------------------------------------------------
/src/RSSkeleton.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSSkeleton : RSModule {
6 | enum ParamIds {
7 | THEME_BUTTON,
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | NUM_INPUTS
12 | };
13 | enum OutputIds {
14 | NUM_OUTPUTS
15 | };
16 | enum LightIds {
17 | NUM_LIGHTS
18 | };
19 |
20 | dsp::BooleanTrigger themeTrigger;
21 |
22 | RSSkeleton() {
23 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
24 |
25 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
26 |
27 | }
28 |
29 | void process(const ProcessArgs &args) override {
30 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
31 | RSTheme++;
32 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
33 | }
34 |
35 | }
36 |
37 | json_t* dataToJson() override {
38 | json_t* rootJ = json_object();
39 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
40 |
41 | return rootJ;
42 | }
43 |
44 | void dataFromJson(json_t* rootJ) override {
45 | json_t* themeJ = json_object_get(rootJ, "theme");
46 | if(themeJ) RSTheme = json_integer_value(themeJ);
47 | }
48 | };
49 |
50 |
51 | struct RSSkeletonWidget : ModuleWidget {
52 | RSSkeleton* module;
53 |
54 | RSSkeletonWidget(RSSkeleton *module) {
55 | INFO("Racket Science: RSSkeletonWidget()");
56 |
57 | setModule(module);
58 | this->module = module;
59 |
60 | box.size = Vec(RACK_GRID_WIDTH * 3, RACK_GRID_HEIGHT);
61 | int middle = box.size.x / 2 + 1;
62 |
63 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSSkeleton::THEME_BUTTON));
64 |
65 | addChild(new RSLabelCentered(middle, box.pos.y + 13, "TITLE1", 14, module));
66 | addChild(new RSLabelCentered(middle, box.pos.y + 25, "TITLE2", 14, module));
67 | addChild(new RSLabelCentered(middle, box.pos.y + 37, "TITLE3", 14, module));
68 |
69 | addChild(new RSLabelCentered(middle, box.size.y - 15, "Racket", 12, module));
70 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Science", 12, module));
71 |
72 | }
73 |
74 | void step() override {
75 | if(!module) return;
76 |
77 | ModuleWidget::step();
78 | }
79 |
80 | void customDraw(const DrawArgs& args) {}
81 | #include "RSModuleWidgetDraw.hpp"
82 | };
83 |
84 | Model *modelRSSkeleton = createModel("RSSkeleton");
--------------------------------------------------------------------------------
/src/RSVectorVictor.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 |
6 | struct RSVectorVictor : Module {
7 | enum ParamIds {
8 | NUM_PARAMS
9 | };
10 | enum InputIds {
11 | PHASEA_INPUT,
12 | WRITEA_INPUT,
13 | INA_INPUT,
14 | PHASEB_INPUT,
15 | WRITEB_INPUT,
16 | INB_INPUT,
17 | NUM_INPUTS
18 | };
19 | enum OutputIds {
20 | OUTA_OUTPUT,
21 | OUTB_OUTPUT,
22 | NUM_OUTPUTS
23 | };
24 | enum LightIds {
25 | WRITEA_LIGHT,
26 | WRITEB_LIGHT,
27 | NUM_LIGHTS
28 | };
29 |
30 | RSVectorVictor() {
31 | INFO("Racket Science: %i params %i inputs %i outputs %i lights", NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
32 |
33 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
34 | }
35 |
36 | #define SAMPLES 1000
37 | float bufferA[SAMPLES] = {};
38 | float bufferB[SAMPLES] = {};
39 |
40 | void onReset() override {
41 | std::memset(bufferA, 0, sizeof(bufferA));
42 | std::memset(bufferB, 0, sizeof(bufferB));
43 | }
44 |
45 | void process(const ProcessArgs &args) override {
46 | unsigned int idxA, idxB;
47 | float inA, inB;
48 |
49 | idxA = (unsigned int)abs(inputs[PHASEA_INPUT].getVoltage() * SAMPLES / 10); if(idxA > SAMPLES - 1) idxA = SAMPLES - 1;
50 | idxB = (unsigned int)abs(inputs[PHASEB_INPUT].getVoltage() * SAMPLES / 10); if(idxB > SAMPLES - 1) idxB = SAMPLES - 1;
51 |
52 | inA = inputs[INA_INPUT].getVoltage();
53 | inB = inputs[INB_INPUT].getVoltage();
54 |
55 | if(inputs[WRITEA_INPUT].getVoltage()) bufferA[idxA] = inA;
56 | if(inputs[WRITEB_INPUT].getVoltage()) bufferB[idxB] = inB;
57 |
58 | outputs[OUTA_OUTPUT].setVoltage(bufferA[idxA]);
59 | outputs[OUTB_OUTPUT].setVoltage(bufferB[idxB]);
60 |
61 | lights[WRITEA_LIGHT].setSmoothBrightness(inputs[WRITEA_INPUT].getVoltage() ? 1 : 0, 10);
62 | lights[WRITEB_LIGHT].setSmoothBrightness(inputs[WRITEB_INPUT].getVoltage() ? 1 : 0, 10);
63 | }
64 |
65 | json_t* dataToJson() override {
66 | INFO("Racket Science: d2j()");
67 | json_t* rootJ = json_object();
68 |
69 | json_t* samplesAJ = json_array();
70 | json_t* samplesBJ = json_array();
71 |
72 | for(int i = 0; i < SAMPLES; i++) {
73 | json_array_append_new(samplesAJ, json_real(bufferA[i]));
74 | json_array_append_new(samplesBJ, json_real(bufferB[i]));
75 | }
76 |
77 | json_object_set_new(rootJ, "samplesA", samplesAJ);
78 | json_object_set_new(rootJ, "samplesB", samplesBJ);
79 |
80 | return rootJ;
81 | }
82 |
83 | void dataFromJson(json_t* rootJ) override {
84 | INFO("Racket Science: dfj()");
85 | json_t* samplesAJ = json_object_get(rootJ, "samplesA");
86 | json_t* samplesBJ = json_object_get(rootJ, "samplesB");
87 |
88 | if(samplesAJ) {
89 | for(int i = 0; i < SAMPLES; i++) {
90 | bufferA[i] = json_number_value(json_array_get(samplesAJ, i));
91 | bufferB[i] = json_number_value(json_array_get(samplesBJ, i));
92 | }
93 | }
94 | }
95 | };
96 |
97 |
98 | struct RSVectorVictorWidget : ModuleWidget {
99 | RSVectorVictor *module;
100 |
101 | RSVectorVictorWidget(RSVectorVictor *module) {
102 | INFO("Racket Science: RSVectorVictorWidget()");
103 |
104 | setModule(module);
105 | this->module = module;
106 |
107 | setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/RSVectorVictor.svg")));
108 |
109 | addInput(createInputCentered(mm2px(Vec(7.398, 33.299)), module, RSVectorVictor::PHASEA_INPUT));
110 | addInput(createInputCentered(mm2px(Vec(18.102, 33.299)), module, RSVectorVictor::WRITEA_INPUT));
111 | addInput(createInputCentered(mm2px(Vec(7.398, 52.578)), module, RSVectorVictor::INA_INPUT));
112 | addInput(createInputCentered(mm2px(Vec(7.393, 80.144)), module, RSVectorVictor::PHASEB_INPUT));
113 | addInput(createInputCentered(mm2px(Vec(18.097, 80.144)), module, RSVectorVictor::WRITEB_INPUT));
114 | addInput(createInputCentered(mm2px(Vec(7.393, 99.422)), module, RSVectorVictor::INB_INPUT));
115 |
116 | addOutput(createOutputCentered(mm2px(Vec(18.102, 52.578)), module, RSVectorVictor::OUTA_OUTPUT));
117 | addOutput(createOutputCentered(mm2px(Vec(18.097, 99.422)), module, RSVectorVictor::OUTB_OUTPUT));
118 |
119 | addChild(createLightCentered>(mm2px(Vec(18.102, 23.655)), module, RSVectorVictor::WRITEA_LIGHT));
120 | addChild(createLightCentered>(mm2px(Vec(18.097, 70.5)), module, RSVectorVictor::WRITEB_LIGHT));
121 |
122 | INFO("Racket Science: exiting RSVectorVictorWidget()");
123 | }
124 | };
125 |
126 |
127 | Model *modelRSVectorVictor = createModel("RSVectorVictor");
128 |
--------------------------------------------------------------------------------
/src/RSXYGLR.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RS.hpp"
4 |
5 | struct RSXYGLR : RSModule {
6 | static const int samples = 1000;
7 |
8 | enum ParamIds {
9 | THEME_BUTTON,
10 | CLEAR_BUTTON,
11 | NUM_PARAMS
12 | };
13 | enum InputIds {
14 | PHASE_IN,
15 | X_IN,
16 | Y_IN,
17 | G_IN,
18 | NUM_INPUTS
19 | };
20 | enum OutputIds {
21 | X_OUT,
22 | Y_OUT,
23 | G_OUT,
24 | NUM_OUTPUTS
25 | };
26 | enum LightIds {
27 | NUM_LIGHTS
28 | };
29 |
30 | dsp::BooleanTrigger themeTrigger;
31 |
32 | float x[samples] = {};
33 | float y[samples] = {};
34 | bool g[samples] = {};
35 |
36 | float phaseIn, xIn, yIn;
37 | bool gIn;
38 | unsigned int idx = 0;
39 |
40 | RSXYGLR() {
41 | config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
42 |
43 | configParam(THEME_BUTTON, 0.f, 1.f, 0.f, "THEME");
44 |
45 | configParam(CLEAR_BUTTON, 0.f, 1.f, 0.f, "CLEAR");
46 | }
47 |
48 | void process(const ProcessArgs &args) override {
49 | if(themeTrigger.process(params[THEME_BUTTON].getValue())) {
50 | RSTheme++;
51 | if(RSTheme > RSGlobal.themeCount) RSTheme = 1;
52 | }
53 |
54 | xIn = RSclamp(inputs[X_IN].getVoltage(), 0.f, 10.f);
55 | yIn = RSclamp(inputs[Y_IN].getVoltage(), 0.f, 10.f);
56 | gIn = inputs[G_IN].getVoltage() > 0.f ? true : false;
57 |
58 | if(inputs[PHASE_IN].isConnected()) {
59 | phaseIn = RSclamp(inputs[PHASE_IN].getVoltage(), 0.f, 10.f);
60 | idx = phaseIn * samples / 10.f;
61 | if(idx >= samples) idx = samples - 1;
62 |
63 | if(gIn) {
64 | x[idx] = xIn;
65 | y[idx] = yIn;
66 | g[idx] = gIn;
67 | }
68 |
69 | outputs[X_OUT].setVoltage(x[idx]);
70 | outputs[Y_OUT].setVoltage(y[idx]);
71 | outputs[G_OUT].setVoltage(g[idx] ? 10.f : 0.f);
72 | }
73 | else {
74 | outputs[X_OUT].setVoltage(xIn);
75 | outputs[Y_OUT].setVoltage(yIn);
76 | outputs[G_OUT].setVoltage(gIn);
77 | }
78 |
79 | if(params[CLEAR_BUTTON].getValue()) onReset();
80 | }
81 |
82 | void onReset() override {
83 | std::memset(x, 0, sizeof(x));
84 | std::memset(y, 0, sizeof(y));
85 | std::memset(g, 0, sizeof(g));
86 | }
87 |
88 | json_t* dataToJson() override {
89 | json_t* rootJ = json_object();
90 |
91 | json_object_set_new(rootJ, "theme", json_integer(RSTheme));
92 |
93 | json_t* xSamples = json_array();
94 | json_t* ySamples = json_array();
95 | json_t* gSamples = json_array();
96 |
97 | for(int i = 0; i < samples; i++) {
98 | json_array_append_new(xSamples, json_real(x[i]));
99 | json_array_append_new(ySamples, json_real(y[i]));
100 | json_array_append_new(gSamples, json_boolean(g[i]));
101 | }
102 |
103 | json_object_set_new(rootJ, "x", xSamples);
104 | json_object_set_new(rootJ, "y", ySamples);
105 | json_object_set_new(rootJ, "g", gSamples);
106 |
107 | return rootJ;
108 | }
109 |
110 | void dataFromJson(json_t* rootJ) override {
111 | json_t* themeJ = json_object_get(rootJ, "theme");
112 |
113 | if(themeJ) RSTheme = json_integer_value(themeJ);
114 |
115 | json_t* xSamples = json_object_get(rootJ, "x");
116 | json_t* ySamples = json_object_get(rootJ, "y");
117 | json_t* gSamples = json_object_get(rootJ, "g");
118 |
119 | if(xSamples && ySamples && gSamples) {
120 | for(int i = 0; i < samples; i++) {
121 | x[i] = json_number_value(json_array_get(xSamples, i));
122 | y[i] = json_number_value(json_array_get(ySamples, i));
123 | g[i] = json_boolean_value(json_array_get(gSamples, i));
124 | }
125 | }
126 | }
127 | };
128 |
129 | struct RSTouchPadDisplay : TransparentWidget {
130 | RSXYGLR *module;
131 |
132 | RSTouchPadDisplay(RSXYGLR *module, int x, int y, int xs, int ys) {
133 | this->module = module;
134 |
135 | box.pos = Vec(x / 2, y / 2);
136 | box.size = Vec(xs, ys);
137 | };
138 |
139 | void draw(const DrawArgs& args) override {
140 | // Bounding box
141 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
142 | nvgFillColor(args.vg, COLOR_BLACK);
143 | nvgStrokeWidth(args.vg, 1.5f);
144 |
145 | nvgBeginPath(args.vg);
146 | nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 5);
147 | nvgStroke(args.vg);
148 | nvgFill(args.vg);
149 |
150 | if(!module) return;
151 |
152 | // Indicate last reported position
153 | nvgFillColor(args.vg, module->gIn ? COLOR_RED : COLOR_GREEN);
154 | nvgBeginPath(args.vg);
155 | nvgCircle(args.vg, module->xIn * (box.size.x / 12.f) + 30, box.size.y - (module->yIn * (box.size.y / 12.f)) -5, 10.f);
156 | nvgStroke(args.vg);
157 | nvgFill(args.vg);
158 |
159 | // Indicate phase indexed position
160 | if(module->g[module->idx]) {
161 | nvgFillColor(args.vg, COLOR_RED);
162 | nvgBeginPath(args.vg);
163 | nvgCircle(args.vg, module->x[module->idx] * (box.size.x / 12.f) + 30, box.size.y - (module->y[module->idx] * (box.size.y / 12.f)) - 5, 10.f);
164 | nvgStroke(args.vg);
165 | nvgFill(args.vg);
166 | }
167 | };
168 | };
169 |
170 | struct RSXYGBufferDisplay : TransparentWidget {
171 | RSXYGLR *module;
172 |
173 | RSXYGBufferDisplay(RSXYGLR *module, int x, int y, int xs, int ys) {
174 | this->module = module;
175 |
176 | box.pos = Vec(x / 2, y / 2);
177 | box.size = Vec(xs, ys);
178 | };
179 |
180 | void draw(const DrawArgs& args) override {
181 | // Bounding box
182 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
183 | nvgFillColor(args.vg, COLOR_BLACK);
184 | nvgStrokeWidth(args.vg, 1.5f);
185 |
186 | nvgBeginPath(args.vg);
187 | nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 5);
188 | nvgStroke(args.vg);
189 | nvgFill(args.vg);
190 |
191 | if(!module) return;
192 |
193 | // Display g buffer contents
194 | nvgStrokeColor(args.vg, COLOR_RED);
195 | nvgBeginPath(args.vg);
196 | for(int i = 0; i < box.size.x; i++) {
197 | unsigned int idx = module->samples / box.size.x * i;
198 | if(module->g[idx]) {
199 | nvgMoveTo(args.vg, box.pos.x + i, box.pos.y);
200 | nvgLineTo(args.vg, box.pos.x + i, box.pos.y + box.size.y);
201 | }
202 | }
203 | nvgStroke(args.vg);
204 |
205 | // Display X buffer contents
206 | nvgStrokeColor(args.vg, COLOR_GREEN);
207 | nvgBeginPath(args.vg);
208 | nvgMoveTo(args.vg, box.pos.x, box.pos.y + box.size.y);
209 | for(int i = 0; i < box.size.x; i++) {
210 | unsigned int idx = module->samples / box.size.x * i;
211 | nvgLineTo(args.vg, box.pos.x + i, box.pos.y + box.size.y - (module->x[idx] / 10 * box.size.y));
212 | }
213 | nvgStroke(args.vg);
214 |
215 | // Display Y buffer contents
216 | nvgStrokeColor(args.vg, COLOR_BLUE);
217 | nvgBeginPath(args.vg);
218 | nvgMoveTo(args.vg, box.pos.x, box.pos.y + box.size.y);
219 | for(int i = 0; i < box.size.x; i++) {
220 | unsigned int idx = module->samples / box.size.x * i;
221 | nvgLineTo(args.vg, box.pos.x + i, box.pos.y + box.size.y - (module->y[idx] / 10 * box.size.y));
222 | }
223 | nvgStroke(args.vg);
224 |
225 | // Indicate buffer index
226 | nvgStrokeColor(args.vg, module->gIn ? COLOR_RED : COLOR_RS_GREY);
227 | nvgStrokeWidth(args.vg, 1.f);
228 | nvgBeginPath(args.vg);
229 | nvgMoveTo(args.vg, box.pos.x + (box.size.x / module->samples * module->idx), box.pos.y);
230 | nvgLineTo(args.vg, box.pos.x + (box.size.x / module->samples * module->idx), box.pos.y + box.size.y);
231 | nvgStroke(args.vg);
232 |
233 | nvgStrokeColor(args.vg, COLOR_RS_BRONZE);
234 | nvgBeginPath(args.vg);
235 | nvgRoundedRect(args.vg, box.pos.x, box.pos.y, box.size.x, box.size.y, 5);
236 | nvgStroke(args.vg);
237 | };
238 | };
239 |
240 | struct RSXYGLRWidget : ModuleWidget {
241 | RSXYGLR* module;
242 | Widget* panelBorder;
243 |
244 | RSXYGLRWidget(RSXYGLR *module) {
245 | INFO("Racket Science: RSXYGLRWidget()");
246 |
247 | setModule(module);
248 | this->module = module;
249 |
250 | panelBorder = new PanelBorder;
251 | addChild(panelBorder);
252 |
253 | box.size = Vec(RACK_GRID_WIDTH * 20, RACK_GRID_HEIGHT);
254 | int middle = box.size.x / 2 + 1;
255 | int third = box.size.x / 3;
256 |
257 | addParam(createParamCentered(Vec(box.pos.x + 5, box.pos.y + 5), module, RSXYGLR::THEME_BUTTON));
258 |
259 | addChild(new RSLabelCentered(middle, box.pos.y + 14, "XYGLR - Touchpad Loop Recorder", 15, module));
260 | //addChild(new RSLabelCentered(middle, box.pos.y + 30, "Touchpad Loop Recorder", 14));
261 | addChild(new RSLabelCentered(middle, box.size.y - 4, "Racket Science", 12, module)); // >= 4HP
262 | //addChild(new RSLabelCentered(middle, box.size.y - 15, "Racket", 12));
263 | //addChild(new RSLabelCentered(middle, box.size.y - 4, "Science", 12));
264 |
265 | int x = 30, y = box.size.y - 50;
266 |
267 | addChild(new RSLabelCentered(x, y, "PHASE", 10));
268 | addInput(createInputCentered(Vec(x, y + 20), module, RSXYGLR::PHASE_IN));
269 |
270 | x += 40;
271 | addChild(new RSLabelCentered(x, y, "CLEAR", 10));
272 | addParam(createParamCentered(Vec(x, y + 20), module, RSXYGLR::CLEAR_BUTTON));
273 |
274 | x += 40;
275 | addChild(new RSLabelCentered(x, y, "X", 10));
276 | addInput(createInputCentered(Vec(x, y + 20), module, RSXYGLR::X_IN));
277 |
278 | x += 30;
279 | addChild(new RSLabelCentered(x, y, "Y", 10));
280 | addInput(createInputCentered(Vec(x, y + 20), module, RSXYGLR::Y_IN));
281 |
282 | x += 30;
283 | addChild(new RSLabelCentered(x, y, "G", 10));
284 | addInput(createInputCentered(Vec(x, y + 20), module, RSXYGLR::G_IN));
285 |
286 | x += 40;
287 | addChild(new RSLabelCentered(x, y, "X", 10));
288 | addOutput(createOutputCentered(Vec(x, y + 20), module, RSXYGLR::X_OUT));
289 |
290 | x += 30;
291 | addChild(new RSLabelCentered(x, y, "Y", 10));
292 | addOutput(createOutputCentered(Vec(x, y + 20), module, RSXYGLR::Y_OUT));
293 |
294 | x += 30;
295 | addChild(new RSLabelCentered(x, y, "G", 10));
296 | addOutput(createOutputCentered(Vec(x, y + 20), module, RSXYGLR::G_OUT));
297 |
298 | addChild(new RSTouchPadDisplay(module, 20, 20, 260, 200));
299 | addChild(new RSXYGBufferDisplay(module, 20, 240, 260, 70));
300 | }
301 |
302 | void step() override {
303 | if(!module) return;
304 |
305 | ModuleWidget::step();
306 | }
307 |
308 | void customDraw(const DrawArgs& args) {}
309 | #include "RSModuleWidgetDraw.hpp"
310 | };
311 |
312 | Model *modelRSXYGLR = createModel("RSXYGLR");
--------------------------------------------------------------------------------
/src/plugin.cpp:
--------------------------------------------------------------------------------
1 | #include "plugin.hpp"
2 |
3 | #include "RSModule.hpp"
4 |
5 | rsglobal RSGlobal;
6 |
7 | Plugin *pluginInstance;
8 |
9 | void init(Plugin *p) {
10 | pluginInstance = p;
11 |
12 | // Add modules here
13 | // Order dicates order they will show in module browser
14 | p->addModel(modelRSGroundControl);
15 | p->addModel(modelRSMajorTom);
16 | p->addModel(modelRSHeat);
17 | p->addModel(modelRSReheat);
18 | p->addModel(modelRSCVHeat);
19 | p->addModel(modelRSBoogieBay);
20 | p->addModel(modelRSBoogieBayH8);
21 | p->addModel(modelRSMFH);
22 | p->addModel(modelRSBlank);
23 | p->addModel(modelRSShades);
24 | p->addModel(modelRSFido316);
25 | p->addModel(modelRSPhaseOne);
26 | p->addModel(modelRSPhaseFour);
27 | p->addModel(modelRSMissionControl);
28 | p->addModel(modelRSLaunchControl);
29 | p->addModel(modelRSXYGLR);
30 |
31 | p->addModel(modelRSVectorVictor);
32 |
33 | // Any other plugin initialization may go here.
34 | // As an alternative, consider lazy-loading assets and lookup tables when your module is created to reduce startup times of Rack.
35 |
36 | loadRSGlobal();
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/src/plugin.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | struct rshsl {
5 | float hue;
6 | float sat;
7 | float lum;
8 | };
9 |
10 | struct rstheme {
11 | // We store HSL so we can set HSL knobs when switching theme without messing around converting to RGB etc
12 | struct rshsl bghsl;
13 | struct rshsl lbhsl;
14 | struct rshsl sshsl;
15 | float ledAh;
16 | float ledBh;
17 | // Then we also store the actual colors, which are updated using updateRSTheme
18 | NVGcolor bgColor;
19 | NVGcolor lbColor;
20 | NVGcolor ssColor;
21 | NVGcolor lAColor;
22 | NVGcolor lBColor;
23 | };
24 |
25 | struct rsglobal {
26 | static const int themeCount = 16;
27 | int themeIdx;
28 | rstheme themes[themeCount];
29 | int logLevel;
30 | int rateDivider;
31 | };
32 |
33 | extern rsglobal RSGlobal;
34 |
35 | using namespace rack;
36 |
37 | // Declare the Plugin, defined in plugin.cpp
38 | extern Plugin *pluginInstance;
39 |
40 | // Declare each Model, defined in each module source file
41 | extern Model *modelRSVectorVictor;
42 |
43 | extern Model *modelRSGroundControl;
44 | extern Model *modelRSMajorTom;
45 | extern Model *modelRSHeat;
46 | extern Model *modelRSReheat;
47 | extern Model *modelRSCVHeat;
48 | extern Model *modelRSBoogieBay;
49 | extern Model *modelRSBoogieBayH8;
50 | extern Model *modelRSMFH;
51 | extern Model *modelRSBlank;
52 | extern Model *modelRSShades;
53 | extern Model *modelRSFido316;
54 | extern Model *modelRSPhaseOne;
55 | extern Model *modelRSPhaseFour;
56 | extern Model *modelRSMissionControl;
57 | extern Model *modelRSLaunchControl;
58 | extern Model *modelRSXYGLR;
59 |
--------------------------------------------------------------------------------