├── README.md
├── libraries
├── AsyncUpdater.js
├── GroupXFCurves.js
├── Helpers.js
├── ModalWindows.js
├── Notification.js
├── PersistentData.js
├── PreloadBar.js
├── Rect.js
├── TableCurves.js
├── Toaster.js
├── VuMeter.js
└── l10n.js
└── modules
├── ArticulationSwitcher.js
├── AttackOverlayHandler.js
├── BasicNonRepeatingRoundRobin.js
├── BorrowedSampleRoundRobin.js
├── BreathControl.js
├── Cc64Retrigger.js
├── ChordIdentifier.js
├── ColourPicker.js
├── ControllerToVelocity.js
├── DynamicsXfadeTables.js
├── GenericMicMixer.js
├── GlobalVariables.js
├── GuitarChordDictionary.js
├── GuitarPicker.js
├── GuitarStrummer.js
├── HarpGliss.js
├── Humaniser.js
├── KeyRangeColour.js
├── MappingOffset.js
├── MonoMode.js
├── MultiCCMapper.js
├── OctaveDoubler.js
├── PortGlide.js
├── RoundRobinBypassHandler.js
├── SamplerActivator.js
├── SimpleKeyswitcher.js
├── SyntheticRelease.js
├── TrillMaker.js
├── VelocityFilter.js
├── VelocityMappedRoundRobin.js
├── VelocityScaler.js
├── VelocityShaper.js
└── VelocityToCC.js
/README.md:
--------------------------------------------------------------------------------
1 | # HISEScripts
2 |
3 | A collection of libraries and modules for use with HISE (http://hise.audio).
4 |
5 | Released under various free software licenses, please see notes at the top of individual files for specific licensing information.
6 |
7 | If you would like to use one or more of these scripts in a proprietary project, and the script's license does not permit that, please contact me to discuss a commercial license.
8 |
--------------------------------------------------------------------------------
/libraries/AsyncUpdater.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: asyncUpdater.js
3 | * Original Author: Christoph Hart
4 | * Modifications by David Healey
5 | * License: Public Domain
6 | */
7 |
8 | /*
9 | This library is only of benefit in a script that does not have deferred callbacks enabled.
10 |
11 | The purpose of this library is to trigger a function asynchronously from a MIDI callback.
12 | You'll usually want to use this to update the GUI from with onNoteOn, onNoteOff or onController.;
13 | Updating the GUI from one of these callbacks can cause audio dropouts because these callbacks run on the
14 | real-time thread, this library solves that by providing a method to defer a function using a timer object.;
15 |
16 | This library only supports the use of one deferred function at a time, to defer multiple functions you will need to
17 | either combine those functions into one or modify this library to allow for multiple functions.
18 | */
19 |
20 | namespace AsyncUpdater
21 | {
22 | const var updater = Engine.createTimerObject();
23 | reg func;
24 | reg param;
25 |
26 | updater.callback = function()
27 | {
28 | if (func)
29 | {
30 | param == null ? func() : func(param);
31 | }
32 | this.stopTimer();
33 | };
34 |
35 | //This is the only function you need to call. Pass in the name of the function you want to defer (f)
36 | // and if needed a parameter (p). If no parameter is required then set p to null.
37 | inline function deferFunction(f, p)
38 | {
39 | func = f;
40 | param = p;
41 | updater.startTimer(25);
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/libraries/GroupXFCurves.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Author: David Healey
3 | * License: Public Domain
4 | */
5 |
6 | namespace GroupXFCurves
7 | {
8 | inline function draw(samplerId, numGroups, shape)
9 | {
10 | local table = Synth.getTableProcessor(samplerId);
11 |
12 | switch (numGroups) {
13 | case 2:
14 | shape == "s" ? twoGroupsS(table) : twoGroups(table);
15 | break;
16 | case 3:
17 | shape == "s" ? threeGroupsS(table) : threeGroups(table);
18 | break;
19 | case 4:
20 | shape == "s" ? fourGroupsS(table) : fourGroups(table);
21 | break;
22 | }
23 | }
24 |
25 | inline function twoGroups(table)
26 | {
27 | table.reset(0);
28 | table.reset(1);
29 |
30 | //Group1
31 | table.setTablePoint(0, 0, 0.0, 1.0, 0.4);
32 | table.setTablePoint(0, 1, 1.0, 0.0, 0.4);
33 |
34 | //Group2
35 | table.setTablePoint(1, 0, 0.0, 0.0, 0.4);
36 | table.setTablePoint(1, 1, 1.0, 1.0, 0.6);
37 | }
38 |
39 | inline function twoGroupsS(table)
40 | {
41 | table.reset(0);
42 | table.reset(1);
43 |
44 | //Group1
45 | table.setTablePoint(0, 1, 0.0, 1.0, 1.0);
46 | table.addTablePoint(0, 0.5, 0.5);
47 | table.setTablePoint(0, 1, 0.5, 0.5, 0.4);
48 | table.setTablePoint(0, 2, 1.0, 0.0, 0.6);
49 |
50 | //Group2
51 | table.setTablePoint(1, 0, 0.0, 0.0, 0.6);
52 | table.addTablePoint(1, 0.5, 0.5);
53 | table.setTablePoint(1, 1, 0.5, 0.5, 0.4);
54 | table.setTablePoint(1, 2, 1.0, 1.0, 0.6);
55 | }
56 |
57 |
58 | inline function threeGroups(table)
59 | {
60 | table.reset(0);
61 | table.reset(1);
62 | table.reset(2);
63 |
64 | // Group1
65 | table.setTablePoint(0, 0, 0.0, 1.0, 0.4);
66 | table.addTablePoint(0, 0.5, 0.0);
67 | table.setTablePoint(0, 1, 0.5, 0.0, 0.4);
68 | table.setTablePoint(0, 2, 1.0, 0.0, 0.4);
69 |
70 | // Group2
71 | table.setTablePoint(1, 0, 0.0, 0.0, 0.6);
72 | table.addTablePoint(1, 0.5, 1.0);
73 | table.setTablePoint(1, 1, 0.5, 1.0, 0.6);
74 | table.setTablePoint(1, 2, 1.0, 0.0, 0.4);
75 |
76 | // Group3
77 | table.setTablePoint(2, 0, 0.0, 0.0, 0.4);
78 | table.addTablePoint(2, 0.5, 0.0);
79 | table.setTablePoint(2, 1, 0.5, 0.0, 0.4);
80 | table.setTablePoint(2, 2, 1.0, 1.0, 0.6);
81 | }
82 |
83 | inline function threeGroupsS(table)
84 | {
85 | table.reset(0);
86 | table.reset(1);
87 | table.reset(2);
88 |
89 | //Group1
90 | table.setTablePoint(0, 0, 0.0, 1.0, 1.0);
91 | table.addTablePoint(0, 0.25, 0.5);
92 | table.setTablePoint(0, 1, 0.25, 0.5, 0.4);
93 | table.addTablePoint(0, 0.5, 0.0);
94 | table.setTablePoint(0, 2, 0.5, 0.0, 0.6);
95 | table.setTablePoint(0, 3, 1.0, 0.0, 0.6);
96 |
97 | //Group2
98 | table.setTablePoint(1, 0, 0.0, 0.0, 1.0);
99 | table.addTablePoint(1, 0.25, 0.5);
100 | table.setTablePoint(1, 1, 0.25, 0.5, 0.4);
101 | table.addTablePoint(1, 0.5, 1.0);
102 | table.setTablePoint(1, 2, 0.5, 1.0, 0.6);
103 | table.addTablePoint(1, 0.75, 0.5);
104 | table.setTablePoint(1, 3, 0.75, 0.5, 0.4);
105 | table.setTablePoint(1, 4, 1.0, 0.0, 0.6);
106 |
107 | //Group3
108 | table.setTablePoint(2, 0, 0.0, 0.0, 1.0);
109 | table.addTablePoint(2, 0.5, 0.0);
110 | table.setTablePoint(2, 1, 0.5, 0, 0.6);
111 | table.addTablePoint(2, 0.75, 0.5);
112 | table.setTablePoint(2, 2, 0.75, 0.5, 0.4);
113 | table.setTablePoint(2, 3, 1.0, 1.0, 0.6);
114 | }
115 |
116 | inline function fourGroups(table)
117 | {
118 | table.reset(0);
119 | table.reset(1);
120 | table.reset(2);
121 | table.reset(3);
122 |
123 | // Group1
124 | table.setTablePoint(0, 0, 0.0, 1.0, 0.4);
125 | table.addTablePoint(0, 1, 1);
126 | table.setTablePoint(0, 1, 0.33, 0.0, 0.4);
127 | table.setTablePoint(0, 2, 1.0, 0.0, 0.4);
128 |
129 | //Group2
130 | table.setTablePoint(1, 0, 0.0, 0.0, 0.4);
131 | table.addTablePoint(1, 0.33, 1.0);
132 | table.setTablePoint(1, 1, 0.33, 1.0, 0.6);
133 | table.addTablePoint(1, 0.66, 0.0);
134 | table.setTablePoint(1, 2, 0.66, 0.0, 0.4);
135 | table.setTablePoint(1, 3, 1.0, 0.0, 0.4);
136 |
137 | //Group3
138 | table.setTablePoint(2, 0, 0.0, 0.0, 0.4);
139 | table.addTablePoint(2, 0.33, 0.0);
140 | table.setTablePoint(2, 1, 0.33, 0.0, 0.4);
141 | table.addTablePoint(2, 0.66, 1.0);
142 | table.setTablePoint(2, 2, 0.66, 1.0, 0.6);
143 | table.setTablePoint(2, 3, 1.0, 0.0, 0.4);
144 |
145 | //Group4
146 | table.setTablePoint(3, 0, 0.0, 0.0, 0.4);
147 | table.addTablePoint(3, 0.66, 0.0);
148 | table.setTablePoint(3, 1, 0.66, 0.0, 0.6);
149 | table.setTablePoint(3, 2, 1.0, 1.0, 0.6);
150 | }
151 |
152 | inline function fourGroupsS(table)
153 | {
154 | table.reset(0);
155 | table.reset(1);
156 | table.reset(2);
157 | table.reset(3);
158 |
159 | //Group1
160 | table.setTablePoint(0, 0, 0.0, 1.0, 1.0);
161 | table.addTablePoint(0, 0.16, 0.5);
162 | table.setTablePoint(0, 1, 0.16, 0.5, 0.4);
163 | table.addTablePoint(0, 0.33, 0.0);
164 | table.setTablePoint(0, 2, 0.33, 0.0, 0.6);
165 | table.setTablePoint(0, 3, 1.0, 0.0, 0.0);
166 |
167 | //Group2
168 | table.setTablePoint(1, 0, 1.0, 0.0, 1.0);
169 | table.addTablePoint(1, 0.16, 0.5);
170 | table.setTablePoint(1, 1, 0.16, 0.5, 0.4);
171 | table.addTablePoint(1, 0.33, 1.0);
172 | table.setTablePoint(1, 2, 0.33, 1.0, 0.6);
173 | table.addTablePoint(1, 0.5, 0.5);
174 | table.setTablePoint(1, 3, 0.5, 0.5, 0.4);
175 | table.addTablePoint(1, 0.65, 0.0);
176 | table.setTablePoint(1, 4, 0.65, 0.0, 0.6);
177 | table.setTablePoint(1, 5, 1.0, 0.0, 0.4);
178 |
179 | //Group3
180 | table.setTablePoint(2, 0, 0.0, 0.0, 1.0);
181 | table.addTablePoint(2, 0.33, 0.0);
182 | table.setTablePoint(2, 1, 0.33, 0.0, 0.4);
183 | table.addTablePoint(2, 0.5, 0.5);
184 | table.setTablePoint(2, 2, 0.5, 0.5, 0.4);
185 | table.addTablePoint(2, 0.66, 1.0);
186 | table.setTablePoint(2, 3, 0.66, 1.0, 0.6);
187 | table.addTablePoint(2, 0.83, 0.5);
188 | table.setTablePoint(2, 4, 0.83, 0.5, 0.4);
189 | table.setTablePoint(2, 5, 1.0, 0.0, 0.6);
190 |
191 | //Group4
192 | table.setTablePoint(3, 0, 0.0, 0.0, 1.0);
193 | table.addTablePoint(3, 0.66, 0.0);
194 | table.setTablePoint(3, 1, 0.66, 0.0, 0.6);
195 | table.addTablePoint(3, 0.83, 0.5);
196 | table.setTablePoint(3, 2, 0.83, 0.5, 0.4);
197 | table.setTablePoint(3, 3, 1.0, 1.0, 0.6);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/libraries/Helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: helpers.js
3 | * Main Author: David Healey
4 | * Additional Functions by Christoph Hart
5 | * Date: 29/07/2017
6 | * Modified: 03/12/2017
7 | * License: Public Domain
8 | */
9 |
10 | namespace Helpers
11 | {
12 | //Returns note letter only, for letter + octave use built in Engine.getMidiNoteName()
13 | inline function noteNumberToLetter(n)
14 | {
15 | local noteLetters = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
16 | return noteLetters[n % 12];
17 | }
18 |
19 | inline function getAllNoteNames()
20 | {
21 | local i;
22 | local allNoteNames = [];
23 |
24 | for (i = 0; i < 128; i++)
25 | {
26 | allNoteNames[i] = Engine.getMidiNoteName(i);
27 | }
28 |
29 | return allNoteNames;
30 | }
31 |
32 | inline function tempoIndexToString(tempoIndex)
33 | {
34 | local tempos = ["1/1", "1/2", "1/2T", "1/4", "1/4T", "1/8", "1/8T", "1/16", "1/16T", "1/32", "1/32T"];
35 | return tempos[tempoIndex];
36 | }
37 |
38 | inline function inRange(value, min, max)
39 | {
40 | return value >= min && value <= max;
41 | }
42 |
43 | inline function isEven(n)
44 | {
45 | return !(n & 1);
46 | }
47 |
48 | inline function isBlackKey(n)
49 | {
50 | local blackKeys = [1, 3, 6, 8, 10]; //Black keys in one octave assuming C = 0
51 |
52 | if (blackKeys.indexOf(parseInt(n) % 12) != -1)
53 | {
54 | return true;
55 | }
56 | return false;
57 | }
58 |
59 | inline function skew(value, max, skewFactor)
60 | {
61 | // values > 1 will yield more resolution at the lower end
62 | local normalised = value / max;
63 |
64 | // this will "bend" the line towards the lower end
65 | local skewed = Math.pow(normalised, skewFactor);
66 | return skewed * max;
67 | }
68 |
69 | inline function remapRange(value, oldMin, oldMax, newMin, newMax)
70 | {
71 | if (oldMax - oldMin == 0)
72 | return newMin;
73 | else
74 | return (((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin;
75 | }
76 |
77 | inline function getSamplers(exclude)
78 | {
79 | local samplerIds = Synth.getIdList("Sampler");
80 | local samplers = [];
81 |
82 | for (id in samplerIds)
83 | {
84 | if (typeof exclude == "string")
85 | if (Engine.matchesRegex(id, exclude) == true) continue; // Skip IDs that match exclusion
86 |
87 | samplers.push(Synth.getSampler(id));
88 | }
89 |
90 | return samplers;
91 | }
92 |
93 | inline function getIdListContainingString(type, containing)
94 | {
95 | local idList = Synth.getIdList(type);
96 | local filteredIds = [];
97 | local i;
98 |
99 | for (i = 0; i < idList.length; i++)
100 | {
101 | if (idList[i].indexOf(containing) == -1) continue; // Skip ids that don't contain the substring
102 | filteredIds.push(idList[i]);
103 | }
104 |
105 | return filteredIds;
106 | }
107 |
108 | inline function keyExists(obj, key)
109 | {
110 | return !(obj[key] == void); // Important: not undefined!
111 | }
112 |
113 | inline function getSize(obj)
114 | {
115 | local count = 0;
116 |
117 | for (k in obj)
118 | count++;
119 |
120 | return count;
121 | }
122 |
123 | inline function getPropertyByIndex(obj, index)
124 | {
125 | local count = 0;
126 |
127 | for (k in obj)
128 | {
129 | if (count == index)
130 | return obj[k];
131 |
132 | count++;
133 | }
134 |
135 | return undefined;
136 | }
137 |
138 | inline function getIndexByProperty(obj, prop)
139 | {
140 | local count = 0;
141 |
142 | for (k in obj)
143 | {
144 | if (k == prop) return count;
145 | count++;
146 | }
147 |
148 | return -1;
149 | }
150 |
151 | inline function cartesianProduct(array)
152 | {
153 | local i;
154 | local j;
155 | local k;
156 |
157 | local result = [[]];
158 |
159 | for (i = 0; i < array.length; i++)
160 | {
161 | local subArray = array[i];
162 | local temp = [];
163 |
164 | for (j = 0; j < result.length; j++)
165 | {
166 | for (k = 0; k < subArray.length; k++)
167 | {
168 | local c = concatArrays(result[j], [subArray[k]]);
169 | temp.push(c);
170 | }
171 | }
172 |
173 | result = temp;
174 | }
175 |
176 | return result;
177 | }
178 |
179 | inline function concatArrays(a, b)
180 | {
181 | local c = [];
182 | local i;
183 |
184 | for (i = 0; i < a.length; i++)
185 | c[i] = a[i];
186 |
187 | for (i = 0; i < b.length; i++)
188 | c.push(b[i]);
189 |
190 | return c;
191 | }
192 |
193 | inline function mergeObjects(arrayOfObjects)
194 | {
195 | local target = {};
196 |
197 | for (object in arrayOfObjects)
198 | {
199 | if (!isDefined(object) || !object.length) continue;
200 |
201 | for (key in object)
202 | {
203 | target[key] = object[key];
204 | }
205 | }
206 |
207 | return target;
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/libraries/ModalWindows.js:
--------------------------------------------------------------------------------
1 | namespace ModalWindows
2 | {
3 | reg modals = {};
4 |
5 | // pnlModalBackground
6 | const pnlModalBackground = Content.getComponent("pnlModalBackground");
7 |
8 | pnlModalBackground.setMouseCallback(function(event)
9 | {
10 | if (event.clicked)
11 | hideAllModals();
12 | });
13 |
14 | // Functions
15 | inline function hideAllModals()
16 | {
17 | for (id in modals)
18 | modals[id].showControl(false);
19 |
20 | pnlModalBackground.showControl(false);
21 | }
22 |
23 | inline function add(component)
24 | {
25 | modals[component.get("id")] = component;
26 | }
27 |
28 | inline function show(id)
29 | {
30 | pnlModalBackground.showControl(true);
31 | modals[id].showControl(true);
32 | }
33 |
34 | // Calls
35 | hideAllModals();
36 | }
--------------------------------------------------------------------------------
/libraries/Notification.js:
--------------------------------------------------------------------------------
1 | namespace Notification
2 | {
3 | // pnlNotification
4 | const pnlNotification = Content.getComponent("pnlNotification");
5 | pnlNotification.set("x", 901);
6 |
7 | pnlNotification.setPaintRoutine(function(g)
8 | {
9 | var a = this.getLocalBounds(0);
10 |
11 | g.drawDropShadow([a[0] + 5, a[1], a[2], a[3] - 10], Colours.withAlpha(Colours.black, 1.0 - (this.data.counter - 20) / 100), 10);
12 |
13 | g.setColour(Colours.withAlpha(this.get("bgColour"), 1.0 - (this.data.counter - 20) / 100));
14 | g.fillRect([a[0] + 5, a[1], a[2], a[3] - 10]);
15 |
16 | g.setFont("medium", 18);
17 | g.setColour(Colours.withAlpha(this.get("textColour"), 1.0 - (this.data.counter - 20) / 100));
18 | g.drawMultiLineText(this.get("text"), [25, 30], a[2] - 60, "left", 2.0);
19 | });
20 |
21 | pnlNotification.setTimerCallback(function()
22 | {
23 | if (this.get("x") <= 601)
24 | this.data.counter++;
25 | else
26 | this.set("x", this.get("x") - 20);
27 |
28 | if (this.data.counter >= 120)
29 | {
30 | this.showControl(false);
31 | this.stopTimer();
32 | }
33 |
34 | this.repaint();
35 | });
36 |
37 | pnlNotification.setMouseCallback(function(event)
38 | {
39 | if (event.clicked)
40 | pnlNotification.data.counter = 120;
41 | });
42 |
43 | // Functions
44 | inline function show(text)
45 | {
46 | pnlNotification.set("text", text);
47 | pnlNotification.showControl(true);
48 |
49 | pnlNotification.data.counter = 0;
50 | pnlNotification.set("x", 901);
51 | pnlNotification.startTimer(50);
52 | }
53 | }
--------------------------------------------------------------------------------
/libraries/PersistentData.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Author: Christoph Hart & David Healey
3 | * License: Public Domain
4 | */
5 |
6 | namespace PersistentData
7 | {
8 | //The data object. This will be overwritten by the onControl callback of the restorer
9 | reg persistentData = {};
10 |
11 | /*This panel will restore the storage object in its control callback.
12 | If the value is not an object, it won't do anything but use the given
13 | storage variable as value*/
14 | const pnlDataRestorer = Content.addPanel("pnlDataRestorer", 0, 0);
15 | pnlDataRestorer.set("saveInPreset", true);
16 | pnlDataRestorer.set("visible", false);
17 | pnlDataRestorer.setControlCallback(onpnlDataRestorerControl);
18 |
19 | inline function onpnlDataRestorerControl(component, value)
20 | {
21 | // Check if the panel's value is an object
22 | // If not, set the value to the storage object
23 |
24 | if (typeof(value) == "object")
25 | persistentData = value; //Copy the object from the panel to the storage object
26 | else
27 | component.setValue(persistentData); //Initialize the restorer
28 | };
29 |
30 | inline function set(id, value)
31 | {
32 | persistentData[id] = value;
33 | pnlDataRestorer.setValue(persistentData);
34 | }
35 |
36 | inline function get(id)
37 | {
38 | return persistentData[id];
39 | }
40 | }
--------------------------------------------------------------------------------
/libraries/PreloadBar.js:
--------------------------------------------------------------------------------
1 | //License: Public Domain
2 | //Author: Christoph Hart + David Healey
3 |
4 | namespace PreloadBar
5 | {
6 | reg component;
7 |
8 | inline function init(panelId, properties)
9 | {
10 | component = Content.getComponent(panelId);
11 |
12 | if (typeof properties == "object")
13 | {
14 | for (p in properties)
15 | component.set(p, properties[p]);
16 | }
17 |
18 | component.setPaintRoutine(function(g)
19 | {
20 | g.fillAll(this.get("bgColour"));
21 | g.setColour(this.get("itemColour"));
22 | g.fillRect([0, 0, this.getWidth() * this.data.progress, this.getHeight()]);
23 | });
24 |
25 | component.setTimerCallback(function()
26 | {
27 | this.data.progress = Engine.getPreloadProgress();
28 | this.repaint();
29 | });
30 |
31 | component.setLoadingCallback(function(isPreloading)
32 | {
33 | this.data.progress = 0.0;
34 | this.set("visible", isPreloading);
35 | isPreloading ? this.startTimer(30) : this.stopTimer();
36 | });
37 | }
38 |
39 | inline function setPosition(x, y, w, h)
40 | {
41 | component.setPosition(x, y, w, h);
42 | }
43 | }
--------------------------------------------------------------------------------
/libraries/Rect.js:
--------------------------------------------------------------------------------
1 | /*
2 | License: Public Domain
3 | Author: Christoph Hart, David Healey
4 | */
5 |
6 | namespace Rect
7 | {
8 | inline function verticalCentre(h1, h2)
9 | {
10 | return h1 / 2 - h2 / 2;
11 | }
12 |
13 | inline function reduced(area, amount)
14 | {
15 | return [area[0] + amount, area[1] + amount, area[2] - 2 * amount, area[3] - 2 * amount];
16 | }
17 |
18 | inline function withSizeKeepingCentre(area, width, height)
19 | {
20 | return [area[0] + (area[2] - width) / 2, area[1] + (area[3] - height) / 2, width, height];
21 | }
22 |
23 | inline function removeFromLeft(area, amount)
24 | {
25 | area[0] += amount;
26 | area[2] -= amount;
27 | return [area[0] - amount, area[1], amount, area[3]];
28 | }
29 |
30 | inline function removeFromRight(area, amount)
31 | {
32 | area[2] -= amount;
33 | return [area[0] + area[2], area[1], amount, area[3]];
34 | }
35 |
36 | inline function removeFromTop(area, amount)
37 | {
38 | area[1] += amount;
39 | area[3] -= amount;
40 | return [area[0], area[1] - amount, area[2], amount];
41 | }
42 |
43 | inline function removeFromBottom(area, amount)
44 | {
45 | area[3] -= amount;
46 | return [area[0], area[1] + area[3], area[2], amount];
47 | }
48 |
49 | inline function translated(area, xDelta, yDelta)
50 | {
51 | return [area[0] + xDelta, area[1] + yDelta, area[2], area[3]];
52 | }
53 | }
--------------------------------------------------------------------------------
/libraries/TableCurves.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Author: David Healey
3 | * License: Public Domain
4 | */
5 |
6 | namespace TableCurves
7 | {
8 | inline function equalPower2(tableProcessor, stage)
9 | {
10 | tableProcessor.reset(0);
11 |
12 | switch (stage)
13 | {
14 | case 0:
15 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
16 | tableProcessor.setTablePoint(0, 1, 1, 0, 0.25);
17 | break;
18 |
19 | case 1:
20 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
21 | tableProcessor.setTablePoint(0, 1, 1, 1, 0.75);
22 | break;
23 | }
24 | };
25 |
26 | inline function equalPower3(tableProcessor, stage)
27 | {
28 | tableProcessor.reset(0);
29 |
30 | switch (stage)
31 | {
32 | case 0:
33 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
34 | tableProcessor.addTablePoint(0, 1, 1);
35 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0, 0.25);
36 | tableProcessor.setTablePoint(0, 2, 1, 0, 0);
37 | break;
38 |
39 | case 1:
40 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
41 | tableProcessor.addTablePoint(0, 1, 1);
42 | tableProcessor.setTablePoint(0, 1, 1/2, 1, 0.75);
43 | tableProcessor.setTablePoint(0, 2, 1, 0, 0.25);
44 | break;
45 |
46 | case 2:
47 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
48 | tableProcessor.addTablePoint(0, 1, 1);
49 | tableProcessor.setTablePoint(0, 1, 1/2, 0, 1);
50 | tableProcessor.setTablePoint(0, 2, 1, 1, 0.75);
51 | break;
52 | }
53 | };
54 |
55 | inline function equalPower4(tableProcessor, stage)
56 | {
57 | tableProcessor.reset(0);
58 |
59 | switch (stage)
60 | {
61 | case 0:
62 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
63 | tableProcessor.addTablePoint(0, 1, 1);
64 | tableProcessor.setTablePoint(0, 1, 1.0/3, 0, 0.25);
65 | tableProcessor.setTablePoint(0, 2, 1, 0, 0);
66 | break;
67 |
68 | case 1:
69 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
70 | tableProcessor.addTablePoint(0, 1, 1);
71 | tableProcessor.setTablePoint(0, 1, 1/3, 1, 0.75);
72 | tableProcessor.addTablePoint(0, 1, 1);
73 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 0, 0.25);
74 | tableProcessor.setTablePoint(0, 3, 1, 0, 0.25);
75 | break;
76 |
77 | case 2:
78 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
79 | tableProcessor.addTablePoint(0, 1, 1);
80 | tableProcessor.setTablePoint(0, 1, 1/3, 0, 0.25);
81 | tableProcessor.addTablePoint(0, 1, 1);
82 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 1, 0.75);
83 | tableProcessor.setTablePoint(0, 3, 1, 0, 0.25);
84 | break;
85 |
86 | case 3:
87 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
88 | tableProcessor.addTablePoint(0, 1, 1);
89 | tableProcessor.setTablePoint(0, 1, 1/3+1/3, 0, 0.75);
90 | tableProcessor.addTablePoint(0, 1, 1);
91 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 0, 0.25);
92 | tableProcessor.setTablePoint(0, 3, 1, 1, 0.75);
93 | break;
94 | }
95 | };
96 |
97 | inline function equalPower5(tableProcessor, stage)
98 | {
99 | tableProcessor.reset(0);
100 |
101 | switch (stage)
102 | {
103 | case 0:
104 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
105 | tableProcessor.addTablePoint(0, 1, 1);
106 | tableProcessor.setTablePoint(0, 1, 1/4, 0, 0.25);
107 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.25);
108 | break;
109 |
110 | case 1:
111 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
112 | tableProcessor.addTablePoint(0, 1, 1);
113 | tableProcessor.setTablePoint(0, 1, 1/4, 1, 0.75);
114 | tableProcessor.addTablePoint(0, 1, 1);
115 | tableProcessor.setTablePoint(0, 2, 1/4+1/4, 0, 0.25);
116 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
117 | break;
118 |
119 | case 2:
120 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
121 | tableProcessor.addTablePoint(0, 1, 1);
122 | tableProcessor.setTablePoint(0, 1, 1/4, 0, 0.75);
123 | tableProcessor.addTablePoint(0, 1, 1);
124 | tableProcessor.setTablePoint(0, 2, 1/4+1/4, 1, 0.75);
125 | tableProcessor.addTablePoint(0, 1, 1);
126 | tableProcessor.setTablePoint(0, 3, (1/4)*3, 0, 0.25);
127 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.25);
128 | break;
129 |
130 | case 3:
131 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
132 | tableProcessor.addTablePoint(0, 1, 1);
133 | tableProcessor.setTablePoint(0, 1, 1/4*2, 0, 0.75);
134 | tableProcessor.addTablePoint(0, 1, 1);
135 | tableProcessor.setTablePoint(0, 2, 1/4+1/4*2, 1, 0.75);
136 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
137 | break;
138 |
139 | case 4:
140 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
141 | tableProcessor.addTablePoint(0, 1, 1);
142 | tableProcessor.setTablePoint(0, 1, 1/4*3, 0, 0.75);
143 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
144 | break;
145 | }
146 | };
147 |
148 | inline function equalPower6(tableProcessor, stage)
149 | {
150 | tableProcessor.reset(0);
151 |
152 | switch (stage)
153 | {
154 | case 0:
155 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
156 | tableProcessor.addTablePoint(0, 1, 1);
157 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.25);
158 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.75);
159 | break;
160 |
161 | case 1:
162 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
163 | tableProcessor.addTablePoint(0, 1, 1);
164 | tableProcessor.setTablePoint(0, 1, 1/5, 1, 0.75);
165 | tableProcessor.addTablePoint(0, 1, 1);
166 | tableProcessor.setTablePoint(0, 2, 1/5*2, 0, 0.25);
167 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
168 | break;
169 |
170 | case 2:
171 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
172 | tableProcessor.addTablePoint(0, 1, 1);
173 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.75);
174 | tableProcessor.addTablePoint(0, 1, 1);
175 | tableProcessor.setTablePoint(0, 2, 1/5*2, 1, 0.75);
176 | tableProcessor.addTablePoint(0, 1, 1);
177 | tableProcessor.setTablePoint(0, 3, 1/5*3, 0, 0.25);
178 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
179 | break;
180 |
181 | case 3:
182 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
183 | tableProcessor.addTablePoint(0, 1, 1);
184 | tableProcessor.setTablePoint(0, 1, 1/5*2, 0, 0.75);
185 | tableProcessor.addTablePoint(0, 1, 1);
186 | tableProcessor.setTablePoint(0, 2, 1/5*3, 1, 0.75);
187 | tableProcessor.addTablePoint(0, 1, 1);
188 | tableProcessor.setTablePoint(0, 3, 1/5*4, 0, 0.25);
189 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
190 | break;
191 |
192 | case 4:
193 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
194 | tableProcessor.addTablePoint(0, 1, 1);
195 | tableProcessor.setTablePoint(0, 1, 1/5*3, 0, 0.75);
196 | tableProcessor.addTablePoint(0, 1, 1);
197 | tableProcessor.setTablePoint(0, 2, 1/5*4, 1, 0.75);
198 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
199 | break;
200 |
201 | case 5:
202 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
203 | tableProcessor.addTablePoint(0, 1, 1);
204 | tableProcessor.setTablePoint(0, 1, 1/5*4, 0, 0.75);
205 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
206 | break;
207 | }
208 | };
209 |
210 | inline function equalPower7(tableProcessor, stage)
211 | {
212 | tableProcessor.reset(0);
213 |
214 | switch (stage)
215 | {
216 | case 0:
217 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
218 | tableProcessor.addTablePoint(0, 1, 1);
219 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0, 0.25);
220 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.25);
221 | break;
222 |
223 | case 1:
224 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
225 | tableProcessor.addTablePoint(0, 1, 1);
226 | tableProcessor.setTablePoint(0, 1, (1.0/4), 1, 0.75);
227 | tableProcessor.addTablePoint(0, 1, 1);
228 | tableProcessor.setTablePoint(0, 2, (1.0/4)*2, 0, 0.25);
229 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
230 | break;
231 |
232 | case 2:
233 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
234 | tableProcessor.addTablePoint(0, 1, 1);
235 | tableProcessor.setTablePoint(0, 1, (1.0/4/2), 0.0, 0.25);
236 | tableProcessor.addTablePoint(0, 1, 1);
237 | tableProcessor.setTablePoint(0, 2, (1.0/4+1/4/2), 1, 0.75);
238 | tableProcessor.addTablePoint(0, 1, 1);
239 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8, 0, 0.25);
240 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
241 | break;
242 |
243 | case 3:
244 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
245 | tableProcessor.addTablePoint(0, 1, 1);
246 | tableProcessor.setTablePoint(0, 1, (1.0/4), 0.0, 0.25);
247 | tableProcessor.addTablePoint(0, 1, 1);
248 | tableProcessor.setTablePoint(0, 2, (1.0/4*2), 1, 0.75);
249 | tableProcessor.addTablePoint(0, 1, 1);
250 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*2, 0, 0.25);
251 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
252 | break;
253 |
254 | case 4:
255 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
256 | tableProcessor.addTablePoint(0, 1, 1);
257 | tableProcessor.setTablePoint(0, 1, (1.0/4+1/4/2), 0.0, 0.25);
258 | tableProcessor.addTablePoint(0, 1, 1);
259 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8, 1, 0.75);
260 | tableProcessor.addTablePoint(0, 1, 1);
261 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*3, 0, 0.25);
262 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
263 | break;
264 |
265 | case 5:
266 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
267 | tableProcessor.addTablePoint(0, 1, 1);
268 | tableProcessor.setTablePoint(0, 1, (1.0/4*2), 0.0, 0.25);
269 | tableProcessor.addTablePoint(0, 1, 1);
270 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8*2, 1, 0.75);
271 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*4, 0, 0.25);
272 | break;
273 |
274 | case 6:
275 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
276 | tableProcessor.addTablePoint(0, 1, 1);
277 | tableProcessor.setTablePoint(0, 1, (1.0/4*2)+1/8*2, 0, 0.75);
278 | tableProcessor.setTablePoint(0, 2, ((1.0/4)*2)+1/8*4, 1, 0.75);
279 | break;
280 | }
281 | };
282 |
283 | inline function sShape2(tableProcessor, stage)
284 | {
285 | tableProcessor.reset(0);
286 |
287 | switch (stage)
288 | {
289 | case 0:
290 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
291 | tableProcessor.addTablePoint(0, 1, 1);
292 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0.5, 0.25);
293 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.75);
294 | break;
295 |
296 | case 1:
297 | tableProcessor.setTablePoint(0, 0, 0, 0, 0.75);
298 | tableProcessor.addTablePoint(0, 1, 1);
299 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0.5, 0.25);
300 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
301 | break;
302 | }
303 | };
304 |
305 | inline function sShape3(tableProcessor, stage)
306 | {
307 | tableProcessor.reset(0);
308 |
309 | switch (stage)
310 | {
311 | case 0:
312 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
313 | tableProcessor.addTablePoint(0, 1, 1);
314 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0.5, 0.25);
315 | tableProcessor.addTablePoint(0, 1, 1);
316 | tableProcessor.setTablePoint(0, 2, 1.0/2, 0, 0.75);
317 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
318 | break;
319 |
320 | case 1:
321 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
322 | tableProcessor.addTablePoint(0, 1, 1);
323 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0.5, 0.25);
324 | tableProcessor.addTablePoint(0, 1, 1);
325 | tableProcessor.setTablePoint(0, 2, 1.0/2, 1, 0.75);
326 | tableProcessor.addTablePoint(0, 1, 1);
327 | tableProcessor.setTablePoint(0, 3, 1.0/4*3, 0.5, 0.25);
328 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
329 | break;
330 |
331 | case 2:
332 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
333 | tableProcessor.addTablePoint(0, 1, 1);
334 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0, 0.75);
335 | tableProcessor.addTablePoint(0, 1, 1);
336 | tableProcessor.setTablePoint(0, 2, 1.0/4*3, 0.5, 0.25);
337 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
338 | break;
339 | }
340 | };
341 |
342 | inline function sShape4(tableProcessor, stage)
343 | {
344 | tableProcessor.reset(0);
345 |
346 | switch (stage)
347 | {
348 | case 0:
349 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
350 | tableProcessor.addTablePoint(0, 1, 1);
351 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
352 | tableProcessor.addTablePoint(0, 1, 1);
353 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0, 0.75);
354 | tableProcessor.setTablePoint(0, 3, 1, 0, 0);
355 | break;
356 |
357 | case 1:
358 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
359 | tableProcessor.addTablePoint(0, 1, 1);
360 | tableProcessor.setTablePoint(0, 1, 1/3/2, 0.5, 0.25);
361 | tableProcessor.addTablePoint(0, 1, 1);
362 | tableProcessor.setTablePoint(0, 2, 1/3, 1, 0.75);
363 | tableProcessor.addTablePoint(0, 1, 1);
364 | tableProcessor.setTablePoint(0, 3, 1/3+1/3/2, 0.5, 0.25);
365 | tableProcessor.addTablePoint(0, 1, 1);
366 | tableProcessor.setTablePoint(0, 4, 1/3+1/3, 0, 0.75);
367 | tableProcessor.setTablePoint(0, 5, 1, 0, 0.25);
368 | break;
369 |
370 | case 2:
371 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
372 | tableProcessor.addTablePoint(0, 1, 1);
373 | tableProcessor.setTablePoint(0, 1, 1/3, 0, 0.25);
374 | tableProcessor.addTablePoint(0, 1, 1);
375 | tableProcessor.setTablePoint(0, 2, 1/3+1/3/2, 0.5, 0.25);
376 | tableProcessor.addTablePoint(0, 1, 1);
377 | tableProcessor.setTablePoint(0, 3, 1/3+1/3, 1, 0.75);
378 | tableProcessor.addTablePoint(0, 1, 1);
379 | tableProcessor.setTablePoint(0, 4, 1/3+1/3+1/6, 0.5, 0.25);
380 | tableProcessor.setTablePoint(0, 5, 1, 0, 0.75);
381 | break;
382 |
383 | case 3:
384 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
385 | tableProcessor.addTablePoint(0, 1, 1);
386 | tableProcessor.setTablePoint(0, 1, 1/3+1/3, 0, 0.75);
387 | tableProcessor.addTablePoint(0, 1, 1);
388 | tableProcessor.setTablePoint(0, 2, 1/3+1/3+1/6, 0.5, 0.25);
389 | tableProcessor.setTablePoint(0, 3, 1, 1, 0.75);
390 | break;
391 | }
392 | };
393 |
394 | inline function sShape5(tableProcessor, stage)
395 | {
396 | tableProcessor.reset(0);
397 |
398 | switch (stage)
399 | {
400 | case 0:
401 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
402 | tableProcessor.addTablePoint(0, 1, 1);
403 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
404 | tableProcessor.addTablePoint(0, 1, 1);
405 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0, 0.75);
406 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
407 | break;
408 |
409 | case 1:
410 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
411 | tableProcessor.addTablePoint(0, 1, 1);
412 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
413 | tableProcessor.addTablePoint(0, 1, 1);
414 | tableProcessor.setTablePoint(0, 2, 1.0/3, 1, 0.75);
415 | tableProcessor.addTablePoint(0, 1, 1);
416 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3/2, 0.5, 0.25);
417 | tableProcessor.addTablePoint(0, 1, 1);
418 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3, 0, 0.75);
419 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
420 | break;
421 |
422 | case 2:
423 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
424 | tableProcessor.addTablePoint(0, 1, 1);
425 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0, 0.75);
426 | tableProcessor.addTablePoint(0, 1, 1);
427 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0.5, 0.25);
428 | tableProcessor.addTablePoint(0, 1, 1);
429 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3/2, 1, 0.75);
430 | tableProcessor.addTablePoint(0, 1, 1);
431 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3, 0.5, 0.25);
432 | tableProcessor.addTablePoint(0, 1, 1);
433 | tableProcessor.setTablePoint(0, 5, 1.0/3+1/3+1/6, 0, 0.75);
434 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
435 | break;
436 |
437 | case 3:
438 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
439 | tableProcessor.addTablePoint(0, 1, 1);
440 | tableProcessor.setTablePoint(0, 1, 1.0/3, 0, 0.75);
441 | tableProcessor.addTablePoint(0, 1, 1);
442 | tableProcessor.setTablePoint(0, 2, 1.0/3+1/3/2, 0.5, 0.25);
443 | tableProcessor.addTablePoint(0, 1, 1);
444 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3, 1, 0.75);
445 | tableProcessor.addTablePoint(0, 1, 1);
446 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3+1/6, 0.5, 0.25);
447 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
448 | break;
449 |
450 | case 4:
451 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
452 | tableProcessor.addTablePoint(0, 1, 1);
453 | tableProcessor.setTablePoint(0, 1, 1.0/3+1/3, 0, 0.75);
454 | tableProcessor.addTablePoint(0, 1, 1);
455 | tableProcessor.setTablePoint(0, 2, 1.0/3+1/3+1/6, 0.5, 0.25);
456 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
457 | break;
458 | }
459 | };
460 |
461 | inline function sShape6(tableProcessor, stage)
462 | {
463 | tableProcessor.reset(0);
464 |
465 | switch (stage)
466 | {
467 | case 0:
468 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
469 | tableProcessor.addTablePoint(0, 1, 1);
470 | tableProcessor.setTablePoint(0, 1, 1/5/2, 0.5, 0.25);
471 | tableProcessor.addTablePoint(0, 1, 1);
472 | tableProcessor.setTablePoint(0, 2, 1/5, 0, 0.75);
473 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
474 | break;
475 |
476 | case 1:
477 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
478 | tableProcessor.addTablePoint(0, 1, 1);
479 | tableProcessor.setTablePoint(0, 1, 1/5/2, 0.5, 0.25);
480 | tableProcessor.addTablePoint(0, 1, 1);
481 | tableProcessor.setTablePoint(0, 2, 1/5, 1, 0.75);
482 | tableProcessor.addTablePoint(0, 1, 1);
483 | tableProcessor.setTablePoint(0, 3, 1/5+1/5/2, 0.5, 0.25);
484 | tableProcessor.addTablePoint(0, 1, 1);
485 | tableProcessor.setTablePoint(0, 4, 1/5*2, 0, 0.75);
486 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
487 | break;
488 |
489 | case 2:
490 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
491 | tableProcessor.addTablePoint(0, 1, 1);
492 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.75);
493 | tableProcessor.addTablePoint(0, 1, 1);
494 | tableProcessor.setTablePoint(0, 2, 1/5+1/5/2, 0.5, 0.25);
495 | tableProcessor.addTablePoint(0, 1, 1);
496 | tableProcessor.setTablePoint(0, 3, 1/5*2, 1, 0.75);
497 | tableProcessor.addTablePoint(0, 1, 1);
498 | tableProcessor.setTablePoint(0, 4, 1/5+1/5+1/10, 0.5, 0.25);
499 | tableProcessor.addTablePoint(0, 1, 1);
500 | tableProcessor.setTablePoint(0, 5, 1/5*3, 0, 0.75);
501 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
502 | break;
503 |
504 | case 3:
505 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
506 | tableProcessor.addTablePoint(0, 1, 1);
507 | tableProcessor.setTablePoint(0, 1, 1/5+1/5, 0, 0.75);
508 | tableProcessor.addTablePoint(0, 1, 1);
509 | tableProcessor.setTablePoint(0, 2, 1/5+1/5+1/5/2, 0.5, 0.25);
510 | tableProcessor.addTablePoint(0, 1, 1);
511 | tableProcessor.setTablePoint(0, 3, 1/5*3, 1, 0.75);
512 | tableProcessor.addTablePoint(0, 1, 1);
513 | tableProcessor.setTablePoint(0, 4, 1/5*3+1/5/2, 0.5, 0.25);
514 | tableProcessor.addTablePoint(0, 1, 1);
515 | tableProcessor.setTablePoint(0, 5, 1/5*4, 0, 0.75);
516 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
517 | break;
518 |
519 | case 4:
520 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
521 | tableProcessor.addTablePoint(0, 1, 1);
522 | tableProcessor.setTablePoint(0, 1, 1/5+1/5+1/5, 0, 0.75);
523 | tableProcessor.addTablePoint(0, 1, 1);
524 | tableProcessor.setTablePoint(0, 2, 1/5+1/5+1/5+1/5/2, 0.5, 0.25);
525 | tableProcessor.addTablePoint(0, 1, 1);
526 | tableProcessor.setTablePoint(0, 3, 1/5*4, 1, 0.75);
527 | tableProcessor.addTablePoint(0, 1, 1);
528 | tableProcessor.setTablePoint(0, 4, 1/5*4+1/5/2, 0.5, 0.25);
529 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
530 | break;
531 |
532 | case 5:
533 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
534 | tableProcessor.addTablePoint(0, 1, 1);
535 | tableProcessor.setTablePoint(0, 1, 1/5*4, 0, 0.75);
536 | tableProcessor.addTablePoint(0, 1, 1);
537 | tableProcessor.setTablePoint(0, 2, 1/5*4+1/5/2, 0.5, 0.25);
538 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
539 | break;
540 | }
541 | };
542 |
543 | inline function sShape7(tableProcessor, stage)
544 | {
545 | tableProcessor.reset(0);
546 |
547 | switch (stage)
548 | {
549 | case 0:
550 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
551 | tableProcessor.addTablePoint(0, 1, 1);
552 | tableProcessor.setTablePoint(0, 1, 1.0/4/2, 0.5, 0.25);
553 | tableProcessor.addTablePoint(0, 1, 1);
554 | tableProcessor.setTablePoint(0, 2, 1.0/4, 0, 0.75);
555 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
556 | break;
557 |
558 | case 1:
559 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
560 | tableProcessor.addTablePoint(0, 1, 1);
561 | tableProcessor.setTablePoint(0, 1, 1.0/4/2, 0.5, 0.25);
562 | tableProcessor.addTablePoint(0, 1, 1);
563 | tableProcessor.setTablePoint(0, 2, (1.0/4), 1, 0.75);
564 | tableProcessor.addTablePoint(0, 1, 1);
565 | tableProcessor.setTablePoint(0, 3, (1/4+1/4/2), 0.5, 0.25);
566 | tableProcessor.addTablePoint(0, 1, 1);
567 | tableProcessor.setTablePoint(0, 4, (1.0/4)*2, 0, 0.75);
568 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
569 | break;
570 |
571 | case 2:
572 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
573 | tableProcessor.addTablePoint(0, 1, 1);
574 | tableProcessor.setTablePoint(0, 1, (1.0/4/2), 0.0, 0.25);
575 | tableProcessor.addTablePoint(0, 1, 1);
576 | tableProcessor.setTablePoint(0, 2, (1.0/4), 0.5, 0.25);
577 | tableProcessor.addTablePoint(0, 1, 1);
578 | tableProcessor.setTablePoint(0, 3, (1.0/4+1/4/2), 1, 0.75);
579 | tableProcessor.addTablePoint(0, 1, 1);
580 | tableProcessor.setTablePoint(0, 4, ((1.0/4)*2), 0.5, 0.25);
581 | tableProcessor.addTablePoint(0, 1, 1);
582 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8, 0, 0.75);
583 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
584 | break;
585 |
586 | case 3:
587 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
588 | tableProcessor.addTablePoint(0, 1, 1);
589 | tableProcessor.setTablePoint(0, 1, (1.0/4), 0.0, 0.25);
590 | tableProcessor.addTablePoint(0, 1, 1);
591 | tableProcessor.setTablePoint(0, 2, (1.0/4+1/4/2), 0.5, 0.25);
592 | tableProcessor.addTablePoint(0, 1, 1);
593 | tableProcessor.setTablePoint(0, 3, (1.0/4*2), 1, 0.75);
594 | tableProcessor.addTablePoint(0, 1, 1);
595 | tableProcessor.setTablePoint(0, 4, ((1.0/4)*2)+1/8, 0.5, 0.25);
596 | tableProcessor.addTablePoint(0, 1, 1);
597 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*2, 0, 0.75);
598 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
599 | break;
600 |
601 | case 4:
602 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
603 | tableProcessor.addTablePoint(0, 1, 1);
604 | tableProcessor.setTablePoint(0, 1, (1.0/4+1/4/2), 0.0, 0.25);
605 | tableProcessor.addTablePoint(0, 1, 1);
606 | tableProcessor.setTablePoint(0, 2, (1.0/4*2), 0.5, 0.25);
607 | tableProcessor.addTablePoint(0, 1, 1);
608 | tableProcessor.setTablePoint(0, 3, (1.0/4*2)+1/8, 1, 0.75);
609 | tableProcessor.addTablePoint(0, 1, 1);
610 | tableProcessor.setTablePoint(0, 4, (1.0/4*2)+1/8*2, 0.5, 0.25);
611 | tableProcessor.addTablePoint(0, 1, 1);
612 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*3, 0, 0.75);
613 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
614 | break;
615 |
616 | case 5:
617 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
618 | tableProcessor.addTablePoint(0, 1, 1);
619 | tableProcessor.setTablePoint(0, 1, (1.0/4*2), 0.0, 0.25);
620 | tableProcessor.addTablePoint(0, 1, 1);
621 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8, 0.5, 0.25);
622 | tableProcessor.addTablePoint(0, 1, 1);
623 | tableProcessor.setTablePoint(0, 3, (1.0/4*2)+1/8*2, 1, 0.75);
624 | tableProcessor.addTablePoint(0, 1, 1);
625 | tableProcessor.setTablePoint(0, 4, (1.0/4*2)+1/8*3, 0.5, 0.25);
626 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*4, 0, 0.75);
627 | break;
628 |
629 | case 6:
630 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
631 | tableProcessor.addTablePoint(0, 1, 1);
632 | tableProcessor.setTablePoint(0, 1, (1.0/4*2)+1/8*2, 0, 0.75);
633 | tableProcessor.addTablePoint(0, 1, 1);
634 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8*3, 0.5, 0.25);
635 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*4, 1, 0.75);
636 | break;
637 | }
638 | };
639 | }
640 |
--------------------------------------------------------------------------------
/libraries/Toaster.js:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License
3 |
4 | Copyright (c) 2022 David Healey
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this file (Toaster.js), to deal in the Software without restriction,
8 | including without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to permit
10 | persons to whom the Software is furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | */
23 |
24 | namespace Toaster
25 | {
26 | const MODES = {Top: 0, Left: 1, Centre: 2};
27 |
28 | reg count = 0;
29 |
30 | // pnlToaster
31 | const pnlToaster = Content.getComponent("pnlToaster");
32 | pnlToaster.showControl(false);
33 |
34 | pnlToaster.setPaintRoutine(function(g)
35 | {
36 | var a = this.getLocalBounds(0);
37 |
38 | g.setColour(Colours.withAlpha(this.get("bgColour"), 0.6));
39 |
40 | switch (this.data.mode)
41 | {
42 | case MODES.Top:
43 | a = [0, 0, a[2], 50 * this.getValue()];
44 | g.fillRect(a);
45 | break;
46 |
47 | case MODES.Bottom:
48 | a = [0, a[3] - 50 * this.getValue(), a[2], 55];
49 | g.fillRect(a);
50 | break;
51 |
52 | case MODES.Centre:
53 | a = this.getLocalBounds(a[2] / 3.0);
54 | g.setColour(Colours.withAlpha(this.get("bgColour"), 0.8 * this.getValue()));
55 | g.fillRoundedRectangle(a, 8);
56 | g.setColour(Colours.withAlpha(this.get("itemColour"), 0.7 * this.getValue()));
57 | g.drawRoundedRectangle(a, 8, 2);
58 | break;
59 |
60 | default:
61 | }
62 |
63 | g.setFont("bold", 20);
64 | g.setColour(Colours.withAlpha(this.get("textColour"), this.getValue()));
65 | g.drawFittedText(this.data.message, a, "centred", 2, 1);
66 | });
67 |
68 | pnlToaster.setMouseCallback(function(event)
69 | {
70 | if (event.clicked)
71 | count = 100;
72 | });
73 |
74 | pnlToaster.setTimerCallback(function()
75 | {
76 | if (count < 40 && this.getValue() < 1.0)
77 | this.setValue(this.getValue() + 0.1);
78 | else if (count > 40)
79 | this.setValue(this.getValue() - 0.1);
80 |
81 | count++;
82 |
83 | if (this.getValue() < 0)
84 | this.stopTimer();
85 |
86 | this.showControl(this.getValue() > 0);
87 |
88 | this.repaint();
89 | });
90 |
91 | // Functions
92 | inline function show(mode, msg)
93 | {
94 | pnlToaster.data.mode = mode;
95 | pnlToaster.data.message = msg;
96 | count = 0;
97 | pnlToaster.startTimer(60);
98 | }
99 |
100 | inline function top(msg)
101 | {
102 | show(MODES.Top, msg);
103 | }
104 |
105 | inline function bottom(msg)
106 | {
107 | show(MODES.Bottom, msg);
108 | }
109 |
110 | inline function centre(msg)
111 | {
112 | show(MODES.Centre, msg);
113 | }
114 |
115 | inline function hide()
116 | {
117 | pnlToaster.showControl(false);
118 | }
119 | }
--------------------------------------------------------------------------------
/libraries/VuMeter.js:
--------------------------------------------------------------------------------
1 | /*
2 | Original Author: Christoph Hart
3 | Modifications: David Healey
4 | License: Public Domain
5 | */
6 | namespace VuMeter
7 | {
8 | /** Creates a peak meter.
9 | *
10 | * Usage: Give it a reference to a module (either synth or effect).
11 | *
12 | * It looks best using a width and height with multiple of 4.
13 | * Customize the colours using the scriptPanel colour Ids
14 | */
15 | inline function createVuMeter(name)
16 | {
17 | local widget = Content.getComponent(name);
18 |
19 | Content.setPropertiesFromJSON(name, {"saveInPreset": false, "opaque": 1});
20 |
21 | widget.setPaintRoutine(function(g)
22 | {
23 | g.fillAll(this.get("bgColour"));
24 |
25 | g.setColour(this.get("itemColour"));
26 |
27 | var lsize = parseInt(this.data.lvalue * (this.getHeight()-4));
28 | var rsize = parseInt(this.data.rvalue * (this.getHeight()-4));
29 |
30 | g.fillRect([2, this.getHeight() - lsize - 2, (this.getWidth()-4)/2-1, lsize]);
31 | g.fillRect([2 + this.getWidth() / 2 - 1, this.getHeight() - rsize - 2, (this.getWidth()-4)/2-1, rsize]);
32 |
33 | g.setColour(this.get("itemColour2"));
34 |
35 | for(i = 1; i < this.getHeight()-1; i = i + 3)
36 | g.fillRect([1, i, this.getWidth()-2, 1]);
37 | });
38 |
39 | widget.setTimerCallback(function()
40 | {
41 | var lvalue;
42 | var rvalue;
43 |
44 | if(this.data.fx)
45 | {
46 | lvalue = getNormalizedPeakValue(this.data.fx.getCurrentLevel(0));
47 | rvalue = getNormalizedPeakValue(this.data.fx.getCurrentLevel(1));
48 | }
49 | else
50 | {
51 | lvalue = getNormalizedPeakValue(Engine.getMasterPeakLevel(0));
52 | rvalue = getNormalizedPeakValue(Engine.getMasterPeakLevel(1));
53 | }
54 |
55 | this.data.lvalue = Math.max(lvalue, this.data.lvalue - 0.04);
56 | this.data.rvalue = Math.max(rvalue, this.data.rvalue - 0.04);
57 |
58 | this.repaintImmediately();
59 | });
60 |
61 | widget.startTimer(30);
62 |
63 | return widget;
64 | };
65 |
66 | inline function setModule(vuMeter, module)
67 | {
68 | vuMeter.data.fx = module;
69 | }
70 |
71 | inline function getNormalizedPeakValue(gain)
72 | {
73 | return 0.01 * (100.0 + Engine.getDecibelsForGainFactor(gain));
74 | }
75 | }
--------------------------------------------------------------------------------
/libraries/l10n.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 |
18 | namespace l10n
19 | {
20 | const systemInfo = Engine.getSystemStats();
21 |
22 | //Console.print(trace(systemInfo));
23 |
24 | // #TODO
25 | inline function get(input)
26 | {
27 | return input;
28 | }
29 | }
--------------------------------------------------------------------------------
/modules/ArticulationSwitcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: Articulation Switcher.js
3 | * Author: David Healey
4 | * Date: 30/06/2017
5 | * Modified: 20/07/2017
6 | * License: GPLv3 - https://www.gnu.org/licenses/gpl-3.0.en.html
7 | */
8 |
9 | Content.setWidth(650);
10 | Content.setHeight(50);
11 |
12 | const var UACC_NUM = 32;
13 |
14 | // Create a storage panel, hide it, and save it in the preset
15 | const var storagePanel = Content.addPanel("storagePanel", 0, 0);
16 | storagePanel.set("visible", false);
17 | storagePanel.set("saveInPreset", true);
18 |
19 | // Create object that will hold the preset values.
20 | var data = {channels:[], ks:[], uacc:[]};
21 |
22 | // Set the storage as widget value for the hidden panel.
23 | // Important: this will not clone the object but share a reference!
24 | storagePanel.setValue(data);
25 |
26 | const var muterList = Synth.getIdList("MidiMuter"); //Get MIDI Muter IDs
27 | const var filterList = Synth.getIdList("MIDI Channel Filter"); //Get MIDI Channel filter IDs
28 | const var containerList = Synth.getIdList("Container"); //Get containers ids
29 | const var articulations = []; //IDs of containers that have a midi muter whose ID includes the container name
30 |
31 | reg muters = []; //MIDI Muter modules - every articulation must have a MIDI muter with an ID that includes its container's ID
32 | reg filters = []; //MIDI Channel Filters - either every articulation must have one or none of them, other combinations may lead to strange results.
33 | reg current; //Current articulation/container
34 |
35 | //GUI
36 | const var cmbContainers = Content.addComboBox("Containers", 0, 0);
37 | const var cmbChan = Content.addComboBox("Channel", 150, 0);
38 | const var cmbKs = Content.addComboBox("Key Switch", 300, 0);
39 | const var cmbUacc = Content.addComboBox("UACC", 450, 0);
40 |
41 | //Populate cmbContainers with the names of container that have midiMuters
42 | for (i = 0; i < containerList.length; i++) //Each container
43 | {
44 | for (m in muterList) //Each MIDI Muter
45 | {
46 | if (m.indexOf(containerList[i]) == -1) continue; //Skip MIDI muter if it does not have the container name as part of its ID
47 | muters.push(Synth.getMidiProcessor(m)); //Add MIDI Muter to muters object
48 | articulations.push(containerList[i]); //Add container ID to array of articulation names
49 | cmbContainers.addItem(containerList[i]); //Populate container drop down
50 | }
51 | }
52 |
53 | //If the channel filters list was populated find the correct filter for each articulation
54 | if (filterList.length > 0)
55 | {
56 | for (i = 0; i < articulations.length; i++) //Each articulation (containers with a MIDI muter that have the include the container ID in their ID)
57 | {
58 | for (f in filterList) //Each channel filter
59 | {
60 | if (f.indexOf(articulations[i]) == -1) continue; //Skip filter if it does not have the articulation name as part of its ID
61 | filters.push(Synth.getMidiProcessor(f)); //Add channel filter for this articulation/container to the filters object
62 | }
63 | }
64 | }
65 |
66 | //Populate channel dropdwon
67 | for (i = 0; i < 17; i++)
68 | {
69 | i == 0 ? cmbChan.addItem("Omni") : cmbChan.addItem(i);
70 | }
71 |
72 | //Populate KS and UACC dropdownsContent.setHeight(50);
73 | for (i = 0; i < 128; i++)
74 | {
75 | cmbKs.addItem(Engine.getMidiNoteName(i)); //Populate KS dropdown
76 | i == 0 ? cmbUacc.addItem("Disabled") : cmbUacc.addItem(i); //Populate UACC dropdown
77 | }
78 |
79 | inline function displayCurrentSettings()
80 | {
81 | cmbContainers.setValue(current+1);
82 | if (data.channels[current] != undefined) cmbChan.setValue(data.channels[current]+1);
83 | if (data.ks[current] != undefined) cmbKs.setValue(data.ks[current]+1);
84 | if (data.uacc[current] != undefined) cmbUacc.setValue(data.uacc[current]+1);
85 | }
86 |
87 | function onNoteOn()
88 | {
89 | if (data.ks.contains(Message.getNoteNumber())) //A KS triggered callback
90 | {
91 | //Mute all articulations by default
92 | for (m in muters)
93 | {
94 | m.setAttribute(0, 1);
95 | }
96 | current = data.ks.indexOf(Message.getNoteNumber());
97 | muters[current].setAttribute(0, 0); //Unmute
98 | displayCurrentSettings();
99 | }
100 | }
101 |
102 | function onNoteOff()
103 | {
104 |
105 | }
106 | function onController()
107 | {
108 | if (Message.getControllerNumber() == UACC_NUM && Message.getControllerValue() > 0) //UACC
109 | {
110 | if (data.uacc.contains(Message.getControllerValue())) //Triggered UACC value is assigned to an articulation
111 | {
112 | //Mute all articulations by default
113 | for (m in muters)
114 | {
115 | m.setAttribute(0, 1);
116 | }
117 | current = data.ks.indexOf(Message.getControllerValue());
118 | muters[current].setAttribute(0, 0); //Unmute
119 | displayCurrentSettings();
120 | }
121 | }
122 | }
123 |
124 | function onTimer()
125 | {
126 |
127 | }
128 | function onControl(number, value)
129 | {
130 | switch(number)
131 | {
132 | case storagePanel:
133 | if (typeof storagePanel.getValue() == "object") //If the data object has been stored
134 | {
135 | data = storagePanel.getValue(); //Restore data from panel
136 | }
137 | break;
138 |
139 | case cmbContainers:
140 | current = value-1; //Currently selected articulation
141 | displayCurrentSettings();
142 | break;
143 |
144 | case cmbChan:
145 | if (filters.length > 0)
146 | {
147 | if (value == 1) //Omni
148 | {
149 | filters[current].setBypassed(true);
150 | }
151 | else
152 | {
153 | filters[current].setBypassed(false);
154 | filters[current].setAttribute(0, value-1);
155 | }
156 | data.channels[current] = value-1;
157 | }
158 | break;
159 |
160 | case cmbKs:
161 | data.ks[current] = value-1;
162 | break;
163 |
164 | case cmbUacc:
165 | data.uacc[current] = value-1;
166 | break;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/modules/AttackOverlayHandler.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 | reg id;
18 |
19 | const var btnMode = Content.addButton("btnMode", 10, 10);
20 | btnMode.set("text", "Fade Out");
21 | btnMode.set("tooltip", "When enabled notes will fade out rather than fade in.");
22 |
23 | const var knbLength = Content.addKnob("knbLength", 150, 0);
24 | knbLength.set("text", "Length");
25 | knbLength.set("mode", "Time");
26 | knbLength.set("middlePosition", 500);
27 | knbLength.setRange(5, 1000, 1);
28 |
29 | const var knbVelo = Content.addKnob("knbVelo", 300, 0);
30 | knbVelo.set("text", "Velocity Filter");
31 | knbVelo.setRange(0, 127, 1);
32 |
33 | const var btnLegato = Content.addButton("btnLegato", 450, 10);
34 | btnLegato.set("text", "Legato");function onNoteOn()
35 | {
36 | if ((!btnLegato.getValue() || g_keys <= 1) && g_realVelocity >= knbVelo.getValue())
37 | {
38 | if (btnMode.getValue()) //Fade out
39 | {
40 | id = Message.makeArtificial();
41 | Synth.addVolumeFade(id, knbLength.getValue(), -100);
42 | }
43 | else //Fade in
44 | {
45 | Synth.addVolumeFade(Message.getEventId(), 0, -99);
46 | Synth.addVolumeFade(Message.getEventId(), knbLength.getValue(), 0);
47 | }
48 | }
49 | }
50 | function onNoteOff()
51 | {
52 |
53 | }
54 | function onController()
55 | {
56 |
57 | }
58 | function onTimer()
59 | {
60 |
61 | }
62 | function onControl(number, value)
63 | {
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/modules/BasicNonRepeatingRoundRobin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: basicRoundRobin.js
3 | * Author: David Healey
4 | * Date: 02/01/2018
5 | * License: GPL V3.0
6 | */
7 |
8 | Content.setHeight(50);
9 | Content.setWidth(650);
10 |
11 | const var samplerIds = Synth.getIdList("Sampler");
12 | const var childSamplers = [];
13 | reg rr = -1;
14 | reg lastRR = -1; //RR step number used for the last onNote
15 | reg lastTime = 0; //Track how long between notes
16 |
17 | const var btnBypass = Content.addButton("Bypass", 0, 10); //Script bypass
18 | const var btnRandom = Content.addButton("Random", 150, 10); //Random mode button
19 | const var knbGroups = Content.addKnob("Groups", 300, 0); //Number of groups
20 | knbGroups.setRange(0, 100, 1);
21 |
22 | for (id in samplerIds)
23 | {
24 | childSamplers.push(Synth.getSampler(id)); //Get child sampler
25 | childSamplers[childSamplers.length-1].enableRoundRobin(false); //Disable default RR behaviour
26 | }function onNoteOn()
27 | {
28 | if (!btnBypass.getValue()) //Not bypassed and note in range
29 | {
30 | if (Engine.getUptime() - lastTime > 2) //More than 2 seconds between notes
31 | {
32 | rr = -1; //Reset RR counter
33 | }
34 | else
35 | {
36 | //Random RR
37 | if (btnRandom.getValue())
38 | {
39 | rr = Math.randInt(0, parseInt(knbGroups.getValue()));
40 | }
41 |
42 | //Random is disabled, or the randomly generated RR is the same as the last RR
43 | if (!btnRandom.getValue() || lastRR == rr)
44 | {
45 | rr = (rr + 1) % parseInt(knbGroups.getValue());
46 | }
47 | }
48 |
49 | lastRR = rr; //Make a note of the RR number for next time
50 | lastTime = Engine.getUptime();
51 |
52 | for (s in childSamplers)
53 | {
54 | s.setActiveGroup(rr+1);
55 | }
56 | }
57 | }function onNoteOff()
58 | {
59 |
60 | }
61 | function onController()
62 | {
63 |
64 | }
65 | function onTimer()
66 | {
67 |
68 | }
69 | function onControl(number, value)
70 | {
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/modules/BorrowedSampleRoundRobin.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018, 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 | Content.setWidth(730);
18 | Content.setHeight(50);
19 |
20 | reg lastTime = Engine.createMidiList(); //Track how long between notes
21 | lastTime.fill(0);
22 | reg lastRR = Engine.createMidiList(); //RR step number used for the last onNote
23 |
24 | //Knobs used to set instrument's playable range
25 | const var knbLowNote = Content.addKnob("knbLowNote", 0, 0); //Lowest playable note
26 | knbLowNote.set("text", "Low Note");
27 | knbLowNote.setRange(0, 127, 1);
28 |
29 | const var knbHighNote = Content.addKnob("knbHighNote", 150, 0); //Highest playable note
30 | knbHighNote.setRange(0, 127, 1);
31 | knbHighNote.set("text", "High Note");
32 | knbHighNote.set("defaultValue", 127);
33 |
34 | const var knbReset = Content.addKnob("knbResetTime", 300, 0); //Time in seconds until RR reset
35 | knbReset.setRange(1, 60, 1);
36 | knbReset.set("text", "Reset Time");
37 | knbReset.set("defaultValue", 3);
38 |
39 | const var knbFine = Content.addKnob("knbFineTune", 450, 0);
40 | knbFine.setRange(0, 50, 1);
41 | knbFine.set("text", "Fine Tune");
42 | knbFine.set("defaultValue", 0);
43 |
44 | const var knbGain = Content.addKnob("knbGain", 600, 0);
45 | knbGain.setRange(0, 3, 1);
46 | knbGain.set("mode", "Decibel");
47 | knbGain.set("text", "knbGain");
48 | knbGain.set("defaultValue", 0);function onNoteOn()
49 | {
50 | local n = Message.getNoteNumber();
51 |
52 | //Not legato and in playable range
53 | if (n >= knbLowNote.getValue() && n <= knbHighNote.getValue())
54 | {
55 | local v = lastRR.getValue(n); //Transpose value
56 |
57 | if (Engine.getUptime() - lastTime.getValue(n) > knbReset.getValue()) //Reset time between notes
58 | {
59 | v = 1; //Reset RR counter
60 | }
61 | else
62 | {
63 | local range = [0, 3];
64 |
65 | if (n == knbHighNote.getValue())
66 | {
67 | range[0] = 1;
68 | range[1] = 3;
69 | }
70 | else if (n == knbLowNote.getValue())
71 | {
72 | range[0] = 0;
73 | range[1] = 2;
74 | }
75 |
76 | v = Math.randInt(range[0], range[1]);
77 |
78 | //Generated RR is the same as the last RR
79 | if (lastRR.getValue(n) == v)
80 | {
81 | v = v + 1;
82 | if (v > range[1]-1) v = range[0];
83 | if (v < range[0]) v = range[1];
84 | }
85 | }
86 |
87 | lastRR.setValue(n, v); //Record the RR tranpose value for n
88 | lastTime.setValue(n, Engine.getUptime());
89 |
90 | Message.setTransposeAmount(1-v);
91 | Message.setCoarseDetune(Message.getCoarseDetune()+-(1-v));
92 | }
93 |
94 | if (knbFine.getValue() > 0)
95 | Message.setFineDetune(Message.getFineDetune() + Math.randInt(-knbFine.getValue(), knbFine.getValue()+1));
96 |
97 | if (knbGain.getValue() > 0)
98 | Message.setGain(Math.randInt(-knbGain.getValue(), knbGain.getValue()+1));
99 | }function onNoteOff()
100 | {
101 | if (knbFine.getValue() > 0)
102 | Message.setFineDetune(Message.getFineDetune() + Math.randInt(-knbFine.getValue(), knbFine.getValue()+1));
103 |
104 | if (knbGain.getValue() > 0)
105 | Message.setGain(Math.randInt(-knbGain.getValue(), knbGain.getValue()+1));
106 | }
107 | function onController()
108 | {
109 |
110 | }
111 | function onTimer()
112 | {
113 |
114 | }
115 | function onControl(number, value)
116 | {
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/modules/BreathControl.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 |
18 | Content.setWidth(600);
19 | Content.setHeight(50);
20 |
21 | const var velo = Engine.createMidiList(); //Note on velocities
22 | const var ids = Engine.createMidiList(); //Note Ids
23 | ids.fill(false);
24 |
25 | reg lastLevel = 0; //Last breath level
26 | reg lastTime = 0;
27 |
28 | //GUI
29 | const var btnBypass = Content.addButton("btnBypass", 10, 10);
30 | btnBypass.set("text", "Bypass");
31 |
32 | const var knbThreshold = Content.addKnob("knbThreshold", 160, 0);
33 | knbThreshold.set("text", "Threshold");
34 | knbThreshold.set("defaultValue", 10);
35 | knbThreshold.setRange(0, 127, 1);
36 |
37 | const var knbLevel = Content.addKnob("knbLevel", 310, 0);
38 | knbLevel.set("text", "Level");
39 | knbLevel.setRange(0, 127, 1);
40 | knbLevel.setControlCallback(onknbLevelControl);
41 |
42 | const var btnVelMode = Content.addButton("btnVelMode", 460, 10)
43 | btnVelMode.set("text", "Speed = Velocity");
44 |
45 | inline function onknbLevelControl(component, value)
46 | {
47 | if (!btnBypass.getValue())
48 | {
49 | local threshold = knbThreshold.getValue();
50 | local velocity = 1;
51 |
52 | //If btnVelMode enabled calculate the velocity
53 | if (btnVelMode.getValue())
54 | velocity = getVelocity();
55 |
56 | //Going up but haven't reached threshold, update lastTime
57 | if (lastTime == 0 && value < threshold && value > lastLevel)
58 | lastTime = Engine.getUptime();
59 |
60 | for (i = 0; i < 127; i++)
61 | {
62 | if (Synth.isKeyDown(i))
63 | {
64 | if (value >= threshold && lastLevel < threshold)
65 | {
66 | //Turn off existing note (if any)
67 | if (ids.getValue(i) != false)
68 | Synth.noteOffByEventId(ids.getValue(i));
69 |
70 | //Get velocity for note on if btnVelMode disabled
71 | if (!btnVelMode.getValue())
72 | velocity = velo.getValue(i);
73 |
74 | //Play new note
75 | ids.setValue(i, Synth.playNote(i, velocity));
76 | }
77 | else if (value < threshold && ids.getValue(i) != false)
78 | {
79 | //Turn off note
80 | Synth.noteOffByEventId(ids.getValue(i));
81 | ids.setValue(i, false);
82 | lastTime = 0;
83 | }
84 | }
85 | }
86 |
87 | //Update lastLevel value
88 | lastLevel = value;
89 | }
90 | };
91 |
92 | //Calculate the velocity based on the breath speed - up to 1 second window
93 | inline function getVelocity()
94 | {
95 | local timeDiff = Math.min(1, Engine.getUptime()-lastTime) / 1; //Limit timeDiff to 0-1 seconds
96 | local v = Math.pow(timeDiff, 0.3); //Skew value
97 | return parseInt(127-(v*117));
98 | }function onNoteOn()
99 | {
100 | if (!btnBypass.getValue())
101 | {
102 | if (knbLevel.getValue() < knbThreshold.getValue())
103 | Message.ignoreEvent(true);
104 | else
105 | {
106 | if (ids.getValue(Message.getNoteNumber()) != false)
107 | Synth.noteOffByEventId(ids.getValue(Message.getNoteNumber()));
108 |
109 | ids.setValue(Message.getNoteNumber(), Message.makeArtificial());
110 | }
111 |
112 | velo.setValue(Message.getNoteNumber(), Message.getVelocity());
113 | }
114 | }
115 | function onNoteOff()
116 | {
117 | if (ids.getValue(Message.getNoteNumber()) != false)
118 | {
119 | Synth.noteOffByEventId(ids.getValue(Message.getNoteNumber()));
120 | ids.setValue(Message.getNoteNumber(), false);
121 | lastTime = 0;
122 | }
123 | }
124 | function onController()
125 | {
126 |
127 | }
128 | function onTimer()
129 | {
130 |
131 | }
132 | function onControl(number, value)
133 | {
134 |
135 | }
--------------------------------------------------------------------------------
/modules/Cc64Retrigger.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | //INIT
19 | Content.setWidth(650);
20 |
21 | const var eventId = Engine.createMidiList();
22 | eventId.fill(-99);
23 |
24 | const var heldKeys = [];
25 |
26 | const var btnMute = Content.addButton("Mute", 10, 10);
27 | const var velocities = Engine.createMidiList();
28 |
29 | const var async = Engine.createTimerObject();
30 | async.setTimerCallback(function(){
31 |
32 | for (i = 0; i < 127; i++)
33 | {
34 | if (eventId.getValue(i) != -99 && heldKeys.indexOf(i) == -1)
35 | {
36 | Synth.noteOffByEventId(eventId.getValue(i));
37 | eventId.setValue(i, -99);
38 | }
39 | }
40 |
41 | async.stopTimer();
42 | });function onNoteOn()
43 | {
44 | if (!btnMute.getValue() && Synth.isSustainPedalDown())
45 | {
46 | local n = Message.getNoteNumber();
47 |
48 | //Turn off retriggered note if there is one
49 | if (eventId.getValue(n) != -99)
50 | {
51 | Synth.noteOffByEventId(eventId.getValue(n));
52 | eventId.setValue(n, -99);
53 | }
54 |
55 | velocities.setValue(n, Message.getVelocity());
56 | heldKeys.push(n);
57 | async.startTimer(20); //Turn off old notes
58 | }
59 | }function onNoteOff()
60 | {
61 | if (!btnMute.getValue())
62 | {
63 | local n = Message.getNoteNumber();
64 |
65 | if (Synth.isSustainPedalDown() && velocities.getValue(n) > 0)
66 | {
67 | if (eventId.getValue(n) != -99)
68 | {
69 | Synth.noteOffByEventId(eventId.getValue(n));
70 | eventId.setValue(n, -99);
71 | }
72 |
73 | eventId.setValue(n, Synth.playNote(n, velocities.getValue(n)));
74 | heldKeys.remove(n);
75 | }
76 | }
77 | }function onController()
78 | {
79 | if (Message.getControllerNumber() == 64)
80 | {
81 | Message.ignoreEvent(true);
82 |
83 | if (!Synth.isSustainPedalDown())
84 | {
85 | Engine.allNotesOff();
86 | eventId.fill(-99);
87 | }
88 | }
89 | }function onTimer()
90 | {
91 |
92 | }
93 | function onControl(number, value)
94 | {
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/modules/ChordIdentifier.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 |
18 | namespace ChordDictionary
19 | {
20 | const var patterns = [
21 | [0, 4, 7], //Major
22 | [0, 4, 7, 9], //Major 6th
23 | [0, 2, 4, 7, 9], //Major 6 add9
24 | [0, 4, 7, 11], //Major 7th
25 | [0, 4, 6, 7, 11], //Major 7#11
26 | [0, 2, 4, 7, 11], //Major 9th
27 | [0, 2, 4, 5, 7, 11], //Major 11th
28 | [0, 2, 4, 6, 7, 9, 11], //Major 13th
29 | [0, 2, 4, 6, 9, 11], //Major 13th #11
30 | [0, 3, 7], //Minor
31 | [0, 3, 7, 9], //"Minor 6th"
32 | [0, 2, 3, 7, 9], //Minor 6 add9
33 | [0, 3, 7, 10], //Minor 7th
34 | [0, 4, 6, 8, 11], //Minor 7#11
35 | [0, 2, 3, 7, 10], //Minor 9th
36 | [0, 2, 3, 5, 7, 10], //Minor 11th
37 | [0, 2, 3, 5, 7, 9, 10], //Minor 13th
38 | [0, 3, 7, 11], //mM7
39 | [0, 3, 6], //Diminished
40 | [0, 3, 6, 9], //Diminished 7th
41 | [0, 3, 6, 11], //Diminished Major 7th
42 | [0, 3, 6, 10], //Half diminished 7th
43 | [0, 4, 7, 10], //Dominant 7th
44 | [0, 2, 7], //Sus2
45 | [0, 5, 7], //Sus4
46 | [0, 4, 7, 10], //Harmonic 7th
47 | [0, 4, 8], //Augmented
48 | [0, 6, 10], //Augmented 6th v1
49 | [0, 4, 6, 10], //Augmented 6th v2
50 | [0, 4, 7, 10], //Augmented 6th v3
51 | [0, 4, 8, 10], //Augmented 7th
52 | [0, 2, 4, 6, 7, 10], //Augmented 11th
53 | [0, 4, 8, 11], //Augmented major seventh chord
54 | [0, 2, 4, 7, 10], //Dominant 9th
55 | [0, 1, 3, 5, 6, 10], //Magic chord
56 | [0, 2, 4, 7], //Mu Chord
57 | [0, 2, 4, 6, 9, 10], //Mystic chord
58 | [1, 5, 8], //Neapolitan chord
59 | [0, 2, 4, 8, 10], //9th Augmented 5th
60 | [0, 2, 4, 6, 10], //9th flat 5th
61 | [0, 1, 4, 6, 7, 10], //Petrushka chord
62 | [0, 7], //Power chord
63 | [0, 4, 7, 9, 10], //7 6 Chord
64 | [0, 5, 7, 10], //7th sus4
65 | [0, 3, 5, 7, 10], //So What
66 | [0, 3, 6, 10], //Tristan
67 | [0, 1, 6], //Viennese trichord 1
68 | [0, 6, 7], //Viennese trichord 2
69 | [0, 5, 6, 7], //Dream chord
70 | [0, 1, 4, 7, 9], //Elektra
71 | [0, 4, 8, 9, 11], //Farben
72 | ];
73 |
74 | const var names = [
75 | "Major",
76 | "Major 6th",
77 | "Major 6 add9",
78 | "Major 7th",
79 | "Major 7#11",
80 | "Major 9th",
81 | "Major 11th",
82 | "Major 13th",
83 | "Major 13th #11",
84 | "Minor",
85 | "Minor 6th",
86 | "Minor 6 add9",
87 | "Minor 7th",
88 | "Minor 7#11",
89 | "Minor 9th",
90 | "Minor 11th",
91 | "Minor 13th",
92 | "mM7",
93 | "Dimished",
94 | "Dimished 7th",
95 | "Diminished Major 7th",
96 | "Half Diminished 7th",
97 | "Dominant 7th",
98 | "Sus2",
99 | "Sus4",
100 | "Harmonic 7th",
101 | "Augmented",
102 | "Augmented 6th", //v1
103 | "Augmented 6th", //v2
104 | "Augmented 6th", //v3
105 | "Augmented 7th",
106 | "Augmented 11th",
107 | "Augmented major seventh chord",
108 | "Dominant 9th",
109 | "Magic Chord",
110 | "Mu",
111 | "Mystic",
112 | "Neapolitan",
113 | "9th Augmented 5th",
114 | "9th flat 5",
115 | "Petrushka",
116 | "Power Chord",
117 | "7/6",
118 | "7th Sus4",
119 | "So What",
120 | "Tristan",
121 | "Viennese trichord", //v1
122 | "Viennese trichord", //v2
123 | "Dream",
124 | "Elektra",
125 | "Farben"
126 | ];
127 | }
128 |
129 | const var noteNames = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
130 |
131 | reg heldKeys = [];
132 |
133 | inline function determineChord(data)
134 | {
135 | local notes = []; //Holds sorted data
136 | local search = []; //Holds pattern to search for in the dictionary
137 | local chordRoot;
138 | local chordName = "";
139 |
140 | //Sort the data and remove duplicates
141 | for ( i = 0; i < data.length; i++)
142 | {
143 | if (data[i] != undefined && !notes.contains(data[i] % 12))
144 | notes.push(data[i] % 12);
145 | }
146 |
147 | notes.sort();
148 |
149 | //Seach dictionary for match to notes array
150 | //Shuffle array if no match is found until all choices have been tried.
151 | for (i = 0; i < notes.length; i++)
152 | {
153 | for (j = 0; j < notes.length; j++)
154 | {
155 | chordRoot = notes[i]; //Try next note as root
156 | search[j] = (notes[j] + ((j < i) * 12)) - chordRoot;
157 | }
158 |
159 | search.sort();
160 |
161 | //Check if the search pattern is present in the dictionary
162 | local result = ChordDictionary.patterns.indexOf(search);
163 |
164 | //Exit loop if search match entry in chord dictionary
165 | if (result != -1)
166 | {
167 | local chordType = ChordDictionary.patterns[result];
168 | chordName = noteNames[chordRoot] + " " + ChordDictionary.names[result];
169 | break;
170 | }
171 | else //No matching chord in dictionary
172 | chordName = "---";
173 | }
174 | Console.print(chordName);
175 | }function onNoteOn()
176 | {
177 | heldKeys.push(Message.getNoteNumber()); //Add this key to end of the heldKeys array
178 |
179 | if (heldKeys.length > 2)
180 | determineChord(heldKeys);
181 | }
182 | function onNoteOff()
183 | {
184 | if (Synth.getNumPressedKeys() == 0) //All keys up
185 | heldKeys.clear(); //Reset array
186 | else
187 | {
188 | local index = heldKeys.indexOf(Message.getNoteNumber()); //Get index of released key
189 | heldKeys.remove(heldKeys[index]); //Remove matching element from the heldKeys array
190 |
191 | //Check for chord based on updated heldKeys array
192 | if (heldKeys.length > 2)
193 | determineChord(heldKeys);
194 | }
195 | }
196 | function onController()
197 | {
198 |
199 | }
200 | function onTimer()
201 | {
202 |
203 | }
204 | function onControl(number, value)
205 | {
206 |
207 | }
208 |
--------------------------------------------------------------------------------
/modules/ColourPicker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: colourPicker.js
3 | * Author: David Healey
4 | * Date: 07/09/2017
5 | * Modified: 07/09/2017
6 | * License: Public Domain
7 | */
8 |
9 | Content.setWidht(650);
10 | Content.setHeight(175);
11 |
12 | reg colour = [];
13 | reg colourString;
14 |
15 | const var alpha = Content.addKnob("Alpha", 0, 0);
16 | alpha.setRange(0, 255, 1);
17 | alpha.set("style", "Vertical");
18 | alpha.set("itemColour", 0xFFAAAAAA);
19 | alpha.set("bgColour", 0xFF000000);
20 | const var red = Content.addKnob("Red", 150, 0);
21 | red.setRange(0, 255, 1);
22 | red.set("style", "Vertical");
23 | red.set("itemColour", 0xFFFF0000);
24 | red.set("bgColour", 0xFF000000);
25 | const var green = Content.addKnob("Green", 300, 0);
26 | green.setRange(0, 255, 1);
27 | green.set("itemColour", 0xFF64FF4E);
28 | green.set("bgColour", 0xFF000000);
29 | green.set("style", "Vertical");
30 | const var blue = Content.addKnob("Blue", 450, 0);
31 | blue.setRange(0, 255, 1);
32 | blue.set("style", "Vertical");
33 | blue.set("itemColour", 0xFF1C00FF);
34 | blue.set("bgColour", 0xFF000000);
35 |
36 | const var code = Content.addLabel("code", 600, 10);
37 | code.set("bgColour", 0xFF000000);
38 |
39 | const var pnlColour = Content.addPanel("pnlColour", 0, 50);
40 | pnlColour.set("width", 700);
41 | pnlColour.set("height", 100);
42 | pnlColour.setPaintRoutine(function(g){g.fillAll(parseInt(colourString));});
43 |
44 | inline function convertToHex(v)
45 | {
46 | reg hexTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
47 | reg d1 = hexTable[Math.floor(v/16)];
48 | reg d2 = hexTable[Math.floor(v % 16)];
49 |
50 | return d1 + d2;
51 | }
52 |
53 | inline function updateCode()
54 | {
55 | code.set("text", "0x" + colour[0] + colour[1] + colour[2] + colour[3]);
56 | colourString = "0x" + colour[0] + colour[1] + colour[2] + colour[3];
57 | }function onNoteOn()
58 | {
59 |
60 | }
61 | function onNoteOff()
62 | {
63 |
64 | }
65 | function onController()
66 | {
67 |
68 | }
69 | function onTimer()
70 | {
71 |
72 | }
73 | function onControl(number, value)
74 | {
75 | switch (number)
76 | {
77 | case alpha:
78 | colour[0] = convertToHex(parseInt(value));
79 | updateCode();
80 | pnlColour.repaintImmediately();
81 | break;
82 |
83 | case red:
84 | colour[1] = convertToHex(parseInt(value));
85 | updateCode();
86 | pnlColour.repaintImmediately();
87 | break;
88 |
89 | case green:
90 | colour[2] = convertToHex(parseInt(value));
91 | updateCode();
92 | pnlColour.repaintImmediately();
93 | break;
94 |
95 | case blue:
96 | colour[3] = convertToHex(parseInt(value));
97 | updateCode();
98 | pnlColour.repaintImmediately();
99 | break;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/modules/ControllerToVelocity.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: controllerToVelocity.js
3 | * Author: David Healey
4 | * Date: 30/06/2017
5 | * Modified: 30/06/2017
6 | * License: Public Domain
7 | */
8 |
9 | Content.setWidth(650);
10 | Content.setHeight(50);
11 |
12 | const var knbCC = Content.addKnob("CC Number", 0, 0);
13 | knbCC.setRange(1, 127, 1);
14 |
15 | reg lastCCValue = 1;
16 |
17 |
18 | function onNoteOn()
19 | {
20 | Message.setVelocity(lastCCValue);
21 | }
22 |
23 | function onNoteOff()
24 | {
25 | }
26 |
27 | function onController()
28 | {
29 | if (Message.getControllerNumber() == knbCC.getValue())
30 | {
31 | lastCCValue = Math.max(1, Message.getControllerValue());
32 | }
33 | }
34 |
35 | function onTimer()
36 | {
37 | }
38 |
39 | function onControl(number, value)
40 | {
41 | }
42 |
--------------------------------------------------------------------------------
/modules/DynamicsXfadeTables.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: dynamicXFadeTables.js
3 | * Author: David Healey
4 | * Date: 07/09/2017
5 | * Modified: 07/09/2017
6 | * License: Public Domain
7 | */
8 |
9 | Content.setWidth(650);
10 | Content.setHeight(50);
11 |
12 | const var tableEnvelopes = Synth.getIdList("Midi Controller");
13 | const var modulators = [];
14 |
15 | for (env in tableEnvelopes)
16 | {
17 | modulators.push(Synth.getTableProcessor(env));
18 | }
19 |
20 | //---GUI
21 | const var cmbType = Content.addComboBox("Type", 0, 0);
22 | cmbType.set("items", ["Equal Power", "S-Shaped"].join("\n"));
23 |
24 | const var cmbCount = Content.addComboBox("Count", 150, 0);
25 | cmbCount.set("items", ["2", "3", "4", "5", "6", "7"].join("\n"));
26 |
27 | const var btnApply = Content.addButton("Apply", 300, 0);
28 |
29 | //FUNCTIONS
30 | inline function equalPower2(tableProcessor, stage)
31 | {
32 | tableProcessor.reset(0);
33 |
34 | switch (stage)
35 | {
36 | case 0:
37 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
38 | tableProcessor.setTablePoint(0, 1, 1, 0, 0.25);
39 | break;
40 |
41 | case 1:
42 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
43 | tableProcessor.setTablePoint(0, 1, 1, 1, 0.75);
44 | break;
45 | }
46 | };
47 |
48 | inline function equalPower3(tableProcessor, stage)
49 | {
50 | tableProcessor.reset(0);
51 |
52 | switch (stage)
53 | {
54 | case 0:
55 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
56 | tableProcessor.addTablePoint(0, 1, 1);
57 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0, 0.25);
58 | tableProcessor.setTablePoint(0, 2, 1, 0, 0);
59 | break;
60 |
61 | case 1:
62 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
63 | tableProcessor.addTablePoint(0, 1, 1);
64 | tableProcessor.setTablePoint(0, 1, 1/2, 1, 0.75);
65 | tableProcessor.setTablePoint(0, 2, 1, 0, 0.25);
66 | break;
67 |
68 | case 2:
69 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
70 | tableProcessor.addTablePoint(0, 1, 1);
71 | tableProcessor.setTablePoint(0, 1, 1/2, 0, 1);
72 | tableProcessor.setTablePoint(0, 2, 1, 1, 0.75);
73 | break;
74 | }
75 | };
76 |
77 | inline function equalPower4(tableProcessor, stage)
78 | {
79 | tableProcessor.reset(0);
80 |
81 | switch (stage)
82 | {
83 | case 0:
84 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
85 | tableProcessor.addTablePoint(0, 1, 1);
86 | tableProcessor.setTablePoint(0, 1, 1.0/3, 0, 0.25);
87 | tableProcessor.setTablePoint(0, 2, 1, 0, 0);
88 | break;
89 |
90 | case 1:
91 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
92 | tableProcessor.addTablePoint(0, 1, 1);
93 | tableProcessor.setTablePoint(0, 1, 1/3, 1, 0.75);
94 | tableProcessor.addTablePoint(0, 1, 1);
95 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 0, 0.25);
96 | tableProcessor.setTablePoint(0, 3, 1, 0, 0.25);
97 | break;
98 |
99 | case 2:
100 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
101 | tableProcessor.addTablePoint(0, 1, 1);
102 | tableProcessor.setTablePoint(0, 1, 1/3, 0, 0.25);
103 | tableProcessor.addTablePoint(0, 1, 1);
104 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 1, 0.75);
105 | tableProcessor.setTablePoint(0, 3, 1, 0, 0.25);
106 | break;
107 |
108 | case 3:
109 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
110 | tableProcessor.addTablePoint(0, 1, 1);
111 | tableProcessor.setTablePoint(0, 1, 1/3+1/3, 0, 0.75);
112 | tableProcessor.addTablePoint(0, 1, 1);
113 | tableProcessor.setTablePoint(0, 2, 1/3+1/3, 0, 0.25);
114 | tableProcessor.setTablePoint(0, 3, 1, 1, 0.75);
115 | break;
116 | }
117 | };
118 |
119 | inline function equalPower5(tableProcessor, stage)
120 | {
121 | tableProcessor.reset(0);
122 |
123 | switch (stage)
124 | {
125 | case 0:
126 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
127 | tableProcessor.addTablePoint(0, 1, 1);
128 | tableProcessor.setTablePoint(0, 1, 1/4, 0, 0.25);
129 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.25);
130 | break;
131 |
132 | case 1:
133 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
134 | tableProcessor.addTablePoint(0, 1, 1);
135 | tableProcessor.setTablePoint(0, 1, 1/4, 1, 0.75);
136 | tableProcessor.addTablePoint(0, 1, 1);
137 | tableProcessor.setTablePoint(0, 2, 1/4+1/4, 0, 0.25);
138 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
139 | break;
140 |
141 | case 2:
142 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
143 | tableProcessor.addTablePoint(0, 1, 1);
144 | tableProcessor.setTablePoint(0, 1, 1/4, 0, 0.75);
145 | tableProcessor.addTablePoint(0, 1, 1);
146 | tableProcessor.setTablePoint(0, 2, 1/4+1/4, 1, 0.75);
147 | tableProcessor.addTablePoint(0, 1, 1);
148 | tableProcessor.setTablePoint(0, 3, (1/4)*3, 0, 0.25);
149 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.25);
150 | break;
151 |
152 | case 3:
153 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
154 | tableProcessor.addTablePoint(0, 1, 1);
155 | tableProcessor.setTablePoint(0, 1, 1/4*2, 0, 0.75);
156 | tableProcessor.addTablePoint(0, 1, 1);
157 | tableProcessor.setTablePoint(0, 2, 1/4+1/4*2, 1, 0.75);
158 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
159 | break;
160 |
161 | case 4:
162 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
163 | tableProcessor.addTablePoint(0, 1, 1);
164 | tableProcessor.setTablePoint(0, 1, 1/4*3, 0, 0.75);
165 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
166 | break;
167 | }
168 | };
169 |
170 | inline function equalPower6(tableProcessor, stage)
171 | {
172 | tableProcessor.reset(0);
173 |
174 | switch (stage)
175 | {
176 | case 0:
177 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
178 | tableProcessor.addTablePoint(0, 1, 1);
179 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.25);
180 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.75);
181 | break;
182 |
183 | case 1:
184 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
185 | tableProcessor.addTablePoint(0, 1, 1);
186 | tableProcessor.setTablePoint(0, 1, 1/5, 1, 0.75);
187 | tableProcessor.addTablePoint(0, 1, 1);
188 | tableProcessor.setTablePoint(0, 2, 1/5*2, 0, 0.25);
189 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
190 | break;
191 |
192 | case 2:
193 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
194 | tableProcessor.addTablePoint(0, 1, 1);
195 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.75);
196 | tableProcessor.addTablePoint(0, 1, 1);
197 | tableProcessor.setTablePoint(0, 2, 1/5*2, 1, 0.75);
198 | tableProcessor.addTablePoint(0, 1, 1);
199 | tableProcessor.setTablePoint(0, 3, 1/5*3, 0, 0.25);
200 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
201 | break;
202 |
203 | case 3:
204 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
205 | tableProcessor.addTablePoint(0, 1, 1);
206 | tableProcessor.setTablePoint(0, 1, 1/5*2, 0, 0.75);
207 | tableProcessor.addTablePoint(0, 1, 1);
208 | tableProcessor.setTablePoint(0, 2, 1/5*3, 1, 0.75);
209 | tableProcessor.addTablePoint(0, 1, 1);
210 | tableProcessor.setTablePoint(0, 3, 1/5*4, 0, 0.25);
211 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
212 | break;
213 |
214 | case 4:
215 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
216 | tableProcessor.addTablePoint(0, 1, 1);
217 | tableProcessor.setTablePoint(0, 1, 1/5*3, 0, 0.75);
218 | tableProcessor.addTablePoint(0, 1, 1);
219 | tableProcessor.setTablePoint(0, 2, 1/5*4, 1, 0.75);
220 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.25);
221 | break;
222 |
223 | case 5:
224 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
225 | tableProcessor.addTablePoint(0, 1, 1);
226 | tableProcessor.setTablePoint(0, 1, 1/5*4, 0, 0.75);
227 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
228 | break;
229 | }
230 | };
231 |
232 | inline function equalPower7(tableProcessor, stage)
233 | {
234 | tableProcessor.reset(0);
235 |
236 | switch (stage)
237 | {
238 | case 0:
239 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
240 | tableProcessor.addTablePoint(0, 1, 1);
241 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0, 0.25);
242 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.25);
243 | break;
244 |
245 | case 1:
246 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
247 | tableProcessor.addTablePoint(0, 1, 1);
248 | tableProcessor.setTablePoint(0, 1, (1.0/4), 1, 0.75);
249 | tableProcessor.addTablePoint(0, 1, 1);
250 | tableProcessor.setTablePoint(0, 2, (1.0/4)*2, 0, 0.25);
251 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
252 | break;
253 |
254 | case 2:
255 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
256 | tableProcessor.addTablePoint(0, 1, 1);
257 | tableProcessor.setTablePoint(0, 1, (1.0/4/2), 0.0, 0.25);
258 | tableProcessor.addTablePoint(0, 1, 1);
259 | tableProcessor.setTablePoint(0, 2, (1.0/4+1/4/2), 1, 0.75);
260 | tableProcessor.addTablePoint(0, 1, 1);
261 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8, 0, 0.25);
262 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
263 | break;
264 |
265 | case 3:
266 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
267 | tableProcessor.addTablePoint(0, 1, 1);
268 | tableProcessor.setTablePoint(0, 1, (1.0/4), 0.0, 0.25);
269 | tableProcessor.addTablePoint(0, 1, 1);
270 | tableProcessor.setTablePoint(0, 2, (1.0/4*2), 1, 0.75);
271 | tableProcessor.addTablePoint(0, 1, 1);
272 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*2, 0, 0.25);
273 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
274 | break;
275 |
276 | case 4:
277 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
278 | tableProcessor.addTablePoint(0, 1, 1);
279 | tableProcessor.setTablePoint(0, 1, (1.0/4+1/4/2), 0.0, 0.25);
280 | tableProcessor.addTablePoint(0, 1, 1);
281 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8, 1, 0.75);
282 | tableProcessor.addTablePoint(0, 1, 1);
283 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*3, 0, 0.25);
284 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
285 | break;
286 |
287 | case 5:
288 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
289 | tableProcessor.addTablePoint(0, 1, 1);
290 | tableProcessor.setTablePoint(0, 1, (1.0/4*2), 0.0, 0.25);
291 | tableProcessor.addTablePoint(0, 1, 1);
292 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8*2, 1, 0.75);
293 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*4, 0, 0.25);
294 | break;
295 |
296 | case 6:
297 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
298 | tableProcessor.addTablePoint(0, 1, 1);
299 | tableProcessor.setTablePoint(0, 1, (1.0/4*2)+1/8*2, 0, 0.75);
300 | tableProcessor.setTablePoint(0, 2, ((1.0/4)*2)+1/8*4, 1, 0.75);
301 | break;
302 | }
303 | };
304 |
305 | inline function sShape2(tableProcessor, stage)
306 | {
307 | tableProcessor.reset(0);
308 |
309 | switch (stage)
310 | {
311 | case 0:
312 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
313 | tableProcessor.addTablePoint(0, 1, 1);
314 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0.5, 0.25);
315 | tableProcessor.setTablePoint(0, 2, 0, 0, 0.75);
316 | break;
317 |
318 | case 1:
319 | tableProcessor.setTablePoint(0, 0, 0, 0, 0.75);
320 | tableProcessor.addTablePoint(0, 1, 1);
321 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0.5, 0.25);
322 | tableProcessor.setTablePoint(0, 2, 0, 1, 0.75);
323 | break;
324 | }
325 | };
326 |
327 | inline function sShape3(tableProcessor, stage)
328 | {
329 | tableProcessor.reset(0);
330 |
331 | switch (stage)
332 | {
333 | case 0:
334 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
335 | tableProcessor.addTablePoint(0, 1, 1);
336 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0.5, 0.25);
337 | tableProcessor.addTablePoint(0, 1, 1);
338 | tableProcessor.setTablePoint(0, 2, 1.0/2, 0, 0.75);
339 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
340 | break;
341 |
342 | case 1:
343 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
344 | tableProcessor.addTablePoint(0, 1, 1);
345 | tableProcessor.setTablePoint(0, 1, 1.0/4, 0.5, 0.25);
346 | tableProcessor.addTablePoint(0, 1, 1);
347 | tableProcessor.setTablePoint(0, 2, 1.0/2, 1, 0.75);
348 | tableProcessor.addTablePoint(0, 1, 1);
349 | tableProcessor.setTablePoint(0, 3, 1.0/4*3, 0.5, 0.25);
350 | tableProcessor.setTablePoint(0, 4, 0, 0, 0.75);
351 | break;
352 |
353 | case 2:
354 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
355 | tableProcessor.addTablePoint(0, 1, 1);
356 | tableProcessor.setTablePoint(0, 1, 1.0/2, 0, 0.75);
357 | tableProcessor.addTablePoint(0, 1, 1);
358 | tableProcessor.setTablePoint(0, 2, 1.0/4*3, 0.5, 0.25);
359 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
360 | break;
361 | }
362 | };
363 |
364 | inline function sShape4(tableProcessor, stage)
365 | {
366 | tableProcessor.reset(0);
367 |
368 | switch (stage)
369 | {
370 | case 0:
371 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
372 | tableProcessor.addTablePoint(0, 1, 1);
373 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
374 | tableProcessor.addTablePoint(0, 1, 1);
375 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0, 0.75);
376 | tableProcessor.setTablePoint(0, 3, 1, 0, 0);
377 | break;
378 |
379 | case 1:
380 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
381 | tableProcessor.addTablePoint(0, 1, 1);
382 | tableProcessor.setTablePoint(0, 1, 1/3/2, 0.5, 0.25);
383 | tableProcessor.addTablePoint(0, 1, 1);
384 | tableProcessor.setTablePoint(0, 2, 1/3, 1, 0.75);
385 | tableProcessor.addTablePoint(0, 1, 1);
386 | tableProcessor.setTablePoint(0, 3, 1/3+1/3/2, 0.5, 0.25);
387 | tableProcessor.addTablePoint(0, 1, 1);
388 | tableProcessor.setTablePoint(0, 4, 1/3+1/3, 0, 0.75);
389 | tableProcessor.setTablePoint(0, 5, 1, 0, 0.25);
390 | break;
391 |
392 | case 2:
393 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
394 | tableProcessor.addTablePoint(0, 1, 1);
395 | tableProcessor.setTablePoint(0, 1, 1/3, 0, 0.25);
396 | tableProcessor.addTablePoint(0, 1, 1);
397 | tableProcessor.setTablePoint(0, 2, 1/3+1/3/2, 0.5, 0.25);
398 | tableProcessor.addTablePoint(0, 1, 1);
399 | tableProcessor.setTablePoint(0, 3, 1/3+1/3, 1, 0.75);
400 | tableProcessor.addTablePoint(0, 1, 1);
401 | tableProcessor.setTablePoint(0, 4, 1/3+1/3+1/6, 0.5, 0.25);
402 | tableProcessor.setTablePoint(0, 5, 1, 0, 0.75);
403 | break;
404 |
405 | case 3:
406 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
407 | tableProcessor.addTablePoint(0, 1, 1);
408 | tableProcessor.setTablePoint(0, 1, 1/3+1/3, 0, 0.75);
409 | tableProcessor.addTablePoint(0, 1, 1);
410 | tableProcessor.setTablePoint(0, 2, 1/3+1/3+1/6, 0.5, 0.25);
411 | tableProcessor.setTablePoint(0, 3, 1, 1, 0.75);
412 | break;
413 | }
414 | };
415 |
416 | inline function sShape5(tableProcessor, stage)
417 | {
418 | tableProcessor.reset(0);
419 |
420 | switch (stage)
421 | {
422 | case 0:
423 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
424 | tableProcessor.addTablePoint(0, 1, 1);
425 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
426 | tableProcessor.addTablePoint(0, 1, 1);
427 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0, 0.75);
428 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
429 | break;
430 |
431 | case 1:
432 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
433 | tableProcessor.addTablePoint(0, 1, 1);
434 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0.5, 0.25);
435 | tableProcessor.addTablePoint(0, 1, 1);
436 | tableProcessor.setTablePoint(0, 2, 1.0/3, 1, 0.75);
437 | tableProcessor.addTablePoint(0, 1, 1);
438 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3/2, 0.5, 0.25);
439 | tableProcessor.addTablePoint(0, 1, 1);
440 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3, 0, 0.75);
441 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
442 | break;
443 |
444 | case 2:
445 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
446 | tableProcessor.addTablePoint(0, 1, 1);
447 | tableProcessor.setTablePoint(0, 1, 1.0/3/2, 0, 0.75);
448 | tableProcessor.addTablePoint(0, 1, 1);
449 | tableProcessor.setTablePoint(0, 2, 1.0/3, 0.5, 0.25);
450 | tableProcessor.addTablePoint(0, 1, 1);
451 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3/2, 1, 0.75);
452 | tableProcessor.addTablePoint(0, 1, 1);
453 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3, 0.5, 0.25);
454 | tableProcessor.addTablePoint(0, 1, 1);
455 | tableProcessor.setTablePoint(0, 5, 1.0/3+1/3+1/6, 0, 0.75);
456 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
457 | break;
458 |
459 | case 3:
460 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
461 | tableProcessor.addTablePoint(0, 1, 1);
462 | tableProcessor.setTablePoint(0, 1, 1.0/3, 0, 0.75);
463 | tableProcessor.addTablePoint(0, 1, 1);
464 | tableProcessor.setTablePoint(0, 2, 1.0/3+1/3/2, 0.5, 0.25);
465 | tableProcessor.addTablePoint(0, 1, 1);
466 | tableProcessor.setTablePoint(0, 3, 1.0/3+1/3, 1, 0.75);
467 | tableProcessor.addTablePoint(0, 1, 1);
468 | tableProcessor.setTablePoint(0, 4, 1.0/3+1/3+1/6, 0.5, 0.25);
469 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
470 | break;
471 |
472 | case 4:
473 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
474 | tableProcessor.addTablePoint(0, 1, 1);
475 | tableProcessor.setTablePoint(0, 1, 1.0/3+1/3, 0, 0.75);
476 | tableProcessor.addTablePoint(0, 1, 1);
477 | tableProcessor.setTablePoint(0, 2, 1.0/3+1/3+1/6, 0.5, 0.25);
478 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
479 | break;
480 | }
481 | };
482 |
483 | inline function sShape6(tableProcessor, stage)
484 | {
485 | tableProcessor.reset(0);
486 |
487 | switch (stage)
488 | {
489 | case 0:
490 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
491 | tableProcessor.addTablePoint(0, 1, 1);
492 | tableProcessor.setTablePoint(0, 1, 1/5/2, 0.5, 0.25);
493 | tableProcessor.addTablePoint(0, 1, 1);
494 | tableProcessor.setTablePoint(0, 2, 1/5, 0, 0.75);
495 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
496 | break;
497 |
498 | case 1:
499 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
500 | tableProcessor.addTablePoint(0, 1, 1);
501 | tableProcessor.setTablePoint(0, 1, 1/5/2, 0.5, 0.25);
502 | tableProcessor.addTablePoint(0, 1, 1);
503 | tableProcessor.setTablePoint(0, 2, 1/5, 1, 0.75);
504 | tableProcessor.addTablePoint(0, 1, 1);
505 | tableProcessor.setTablePoint(0, 3, 1/5+1/5/2, 0.5, 0.25);
506 | tableProcessor.addTablePoint(0, 1, 1);
507 | tableProcessor.setTablePoint(0, 4, 1/5*2, 0, 0.75);
508 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
509 | break;
510 |
511 | case 2:
512 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
513 | tableProcessor.addTablePoint(0, 1, 1);
514 | tableProcessor.setTablePoint(0, 1, 1/5, 0, 0.75);
515 | tableProcessor.addTablePoint(0, 1, 1);
516 | tableProcessor.setTablePoint(0, 2, 1/5+1/5/2, 0.5, 0.25);
517 | tableProcessor.addTablePoint(0, 1, 1);
518 | tableProcessor.setTablePoint(0, 3, 1/5*2, 1, 0.75);
519 | tableProcessor.addTablePoint(0, 1, 1);
520 | tableProcessor.setTablePoint(0, 4, 1/5+1/5+1/10, 0.5, 0.25);
521 | tableProcessor.addTablePoint(0, 1, 1);
522 | tableProcessor.setTablePoint(0, 5, 1/5*3, 0, 0.75);
523 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
524 | break;
525 |
526 | case 3:
527 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
528 | tableProcessor.addTablePoint(0, 1, 1);
529 | tableProcessor.setTablePoint(0, 1, 1/5+1/5, 0, 0.75);
530 | tableProcessor.addTablePoint(0, 1, 1);
531 | tableProcessor.setTablePoint(0, 2, 1/5+1/5+1/5/2, 0.5, 0.25);
532 | tableProcessor.addTablePoint(0, 1, 1);
533 | tableProcessor.setTablePoint(0, 3, 1/5*3, 1, 0.75);
534 | tableProcessor.addTablePoint(0, 1, 1);
535 | tableProcessor.setTablePoint(0, 4, 1/5*3+1/5/2, 0.5, 0.25);
536 | tableProcessor.addTablePoint(0, 1, 1);
537 | tableProcessor.setTablePoint(0, 5, 1/5*4, 0, 0.75);
538 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
539 | break;
540 |
541 | case 4:
542 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
543 | tableProcessor.addTablePoint(0, 1, 1);
544 | tableProcessor.setTablePoint(0, 1, 1/5+1/5+1/5, 0, 0.75);
545 | tableProcessor.addTablePoint(0, 1, 1);
546 | tableProcessor.setTablePoint(0, 2, 1/5+1/5+1/5+1/5/2, 0.5, 0.25);
547 | tableProcessor.addTablePoint(0, 1, 1);
548 | tableProcessor.setTablePoint(0, 3, 1/5*4, 1, 0.75);
549 | tableProcessor.addTablePoint(0, 1, 1);
550 | tableProcessor.setTablePoint(0, 4, 1/5*4+1/5/2, 0.5, 0.25);
551 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
552 | break;
553 |
554 | case 5:
555 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
556 | tableProcessor.addTablePoint(0, 1, 1);
557 | tableProcessor.setTablePoint(0, 1, 1/5*4, 0, 0.75);
558 | tableProcessor.addTablePoint(0, 1, 1);
559 | tableProcessor.setTablePoint(0, 2, 1/5*4+1/5/2, 0.5, 0.25);
560 | tableProcessor.setTablePoint(0, 3, 0, 1, 0.75);
561 | break;
562 | }
563 | };
564 |
565 | inline function sShape7(tableProcessor, stage)
566 | {
567 | tableProcessor.reset(0);
568 |
569 | switch (stage)
570 | {
571 | case 0:
572 | tableProcessor.setTablePoint(0, 0, 0, 1, 1);
573 | tableProcessor.addTablePoint(0, 1, 1);
574 | tableProcessor.setTablePoint(0, 1, 1.0/4/2, 0.5, 0.25);
575 | tableProcessor.addTablePoint(0, 1, 1);
576 | tableProcessor.setTablePoint(0, 2, 1.0/4, 0, 0.75);
577 | tableProcessor.setTablePoint(0, 3, 0, 0, 0.75);
578 | break;
579 |
580 | case 1:
581 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
582 | tableProcessor.addTablePoint(0, 1, 1);
583 | tableProcessor.setTablePoint(0, 1, 1.0/4/2, 0.5, 0.25);
584 | tableProcessor.addTablePoint(0, 1, 1);
585 | tableProcessor.setTablePoint(0, 2, (1.0/4), 1, 0.75);
586 | tableProcessor.addTablePoint(0, 1, 1);
587 | tableProcessor.setTablePoint(0, 3, (1/4+1/4/2), 0.5, 0.25);
588 | tableProcessor.addTablePoint(0, 1, 1);
589 | tableProcessor.setTablePoint(0, 4, (1.0/4)*2, 0, 0.75);
590 | tableProcessor.setTablePoint(0, 5, 0, 0, 0.75);
591 | break;
592 |
593 | case 2:
594 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
595 | tableProcessor.addTablePoint(0, 1, 1);
596 | tableProcessor.setTablePoint(0, 1, (1.0/4/2), 0.0, 0.25);
597 | tableProcessor.addTablePoint(0, 1, 1);
598 | tableProcessor.setTablePoint(0, 2, (1.0/4), 0.5, 0.25);
599 | tableProcessor.addTablePoint(0, 1, 1);
600 | tableProcessor.setTablePoint(0, 3, (1.0/4+1/4/2), 1, 0.75);
601 | tableProcessor.addTablePoint(0, 1, 1);
602 | tableProcessor.setTablePoint(0, 4, ((1.0/4)*2), 0.5, 0.25);
603 | tableProcessor.addTablePoint(0, 1, 1);
604 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8, 0, 0.75);
605 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
606 | break;
607 |
608 | case 3:
609 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
610 | tableProcessor.addTablePoint(0, 1, 1);
611 | tableProcessor.setTablePoint(0, 1, (1.0/4), 0.0, 0.25);
612 | tableProcessor.addTablePoint(0, 1, 1);
613 | tableProcessor.setTablePoint(0, 2, (1.0/4+1/4/2), 0.5, 0.25);
614 | tableProcessor.addTablePoint(0, 1, 1);
615 | tableProcessor.setTablePoint(0, 3, (1.0/4*2), 1, 0.75);
616 | tableProcessor.addTablePoint(0, 1, 1);
617 | tableProcessor.setTablePoint(0, 4, ((1.0/4)*2)+1/8, 0.5, 0.25);
618 | tableProcessor.addTablePoint(0, 1, 1);
619 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*2, 0, 0.75);
620 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
621 | break;
622 |
623 | case 4:
624 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
625 | tableProcessor.addTablePoint(0, 1, 1);
626 | tableProcessor.setTablePoint(0, 1, (1.0/4+1/4/2), 0.0, 0.25);
627 | tableProcessor.addTablePoint(0, 1, 1);
628 | tableProcessor.setTablePoint(0, 2, (1.0/4*2), 0.5, 0.25);
629 | tableProcessor.addTablePoint(0, 1, 1);
630 | tableProcessor.setTablePoint(0, 3, (1.0/4*2)+1/8, 1, 0.75);
631 | tableProcessor.addTablePoint(0, 1, 1);
632 | tableProcessor.setTablePoint(0, 4, (1.0/4*2)+1/8*2, 0.5, 0.25);
633 | tableProcessor.addTablePoint(0, 1, 1);
634 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*3, 0, 0.75);
635 | tableProcessor.setTablePoint(0, 6, 0, 0, 0.75);
636 | break;
637 |
638 | case 5:
639 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
640 | tableProcessor.addTablePoint(0, 1, 1);
641 | tableProcessor.setTablePoint(0, 1, (1.0/4*2), 0.0, 0.25);
642 | tableProcessor.addTablePoint(0, 1, 1);
643 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8, 0.5, 0.25);
644 | tableProcessor.addTablePoint(0, 1, 1);
645 | tableProcessor.setTablePoint(0, 3, (1.0/4*2)+1/8*2, 1, 0.75);
646 | tableProcessor.addTablePoint(0, 1, 1);
647 | tableProcessor.setTablePoint(0, 4, (1.0/4*2)+1/8*3, 0.5, 0.25);
648 | tableProcessor.setTablePoint(0, 5, ((1.0/4)*2)+1/8*4, 0, 0.75);
649 | break;
650 |
651 | case 6:
652 | tableProcessor.setTablePoint(0, 0, 0, 0, 1);
653 | tableProcessor.addTablePoint(0, 1, 1);
654 | tableProcessor.setTablePoint(0, 1, (1.0/4*2)+1/8*2, 0, 0.75);
655 | tableProcessor.addTablePoint(0, 1, 1);
656 | tableProcessor.setTablePoint(0, 2, (1.0/4*2)+1/8*3, 0.5, 0.25);
657 | tableProcessor.setTablePoint(0, 3, ((1.0/4)*2)+1/8*4, 1, 0.75);
658 | break;
659 | }
660 | };
661 |
662 | function onNoteOn()
663 | {
664 |
665 | }
666 | function onNoteOff()
667 | {
668 |
669 | }
670 | function onController()
671 | {
672 |
673 | }
674 | function onTimer()
675 | {
676 |
677 | }
678 | function onControl(number, value)
679 | {
680 | switch (number)
681 | {
682 | case btnApply:
683 |
684 | btnApply.setValue(0);
685 |
686 | for (i = 0; i < modulators.length; i++)
687 | {
688 | if (cmbType.getValue() == 1) //Equal power
689 | {
690 | switch (cmbCount.getValue())
691 | {
692 | case 1: equalPower2(modulators[i], i); break;
693 | case 2: equalPower3(modulators[i], i); break;
694 | case 3: equalPower4(modulators[i], i); break;
695 | case 4: equalPower5(modulators[i], i); break;
696 | case 5: equalPower6(modulators[i], i); break;
697 | case 6: equalPower7(modulators[i], i); break;
698 | }
699 | }
700 | else
701 | {
702 | switch (cmbCount.getValue())
703 | {
704 | case 1: sShape2(modulators[i], i); break;
705 | case 2: sShape3(modulators[i], i); break;
706 | case 3: sShape4(modulators[i], i); break;
707 | case 4: sShape5(modulators[i], i); break;
708 | case 5: sShape6(modulators[i], i); break;
709 | case 6: sShape7(modulators[i], i); break;
710 | }
711 | }
712 | }
713 |
714 | break;
715 | }
716 | }
717 |
--------------------------------------------------------------------------------
/modules/GenericMicMixer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title genericMicMixer
3 | * Author: David Healey
4 | * License: GPLv3 - https://www.gnu.org/licenses/gpl-3.0.en.html
5 | */
6 |
7 | //INIT
8 | Content.setWidth(650);
9 | Content.setHeight(350);
10 |
11 | //Retrieve simple gain FX with "mic" in their ID and store in gainFX array
12 | const var simpleGainIds = Synth.getIdList("Simple Gain"); //Get IDs of simple gain FX - 1 per mic
13 | const var gainFx = [];
14 |
15 | reg soloOn = false; //Flag to indicate if at least one channel is soloed
16 |
17 | for (g in simpleGainIds)
18 | {
19 | if (!Engine.matchesRegex(g, "mic")) continue; //Skip if it doesn't contain "mic" in its ID
20 | gainFx.push(Synth.getEffect(g));
21 | }
22 |
23 | //Retrieve samplers and store in samplers array
24 | const var samplerIds = Synth.getIdList("Sampler"); //Get IDs of samplers
25 | const var samplers = [];
26 |
27 | for (s in samplerIds)
28 | {
29 | samplers.push(Synth.getSampler(s));
30 | }
31 |
32 | //GUI
33 | const var vol = [];
34 | const var delay = [];
35 | const var width = [];
36 | const var pan = [];
37 | const var mute = [];
38 | const var solo = [];
39 | const var purge = [];
40 |
41 | for (i = 0; i < Math.min(8, gainFx.length); i++) //Maximum of 8 channels
42 | {
43 | vol[i] = knobFactory(100*i, 0, "vol"+i, {width:95, mode:"Decibel", max:3, text:"Vol " + (i+1)});
44 | delay[i] = knobFactory(100*i, 50, "delay"+i, {width:95, max:500, text:"Delay " + (i+1)});
45 | width[i] = knobFactory(100*i, 100, "width"+i, {width:95, max:200, text:"Width " + (i+1)});
46 | pan[i] = knobFactory(100*i, 150, "pan"+i, {width:95, min:-100, max:100, text:"Pan " + (i+1)});
47 | mute[i] = buttonFactory(100*i, 200, "mute"+i, {width:95, text:"Mute " + (i+1)});
48 | solo[i] = buttonFactory(100*i, 250, "solo"+i, {width:95, text:"Solo " + (i+1)});
49 | purge[i] = buttonFactory(100*i, 300, "purge"+i, {width:95, text:"Purge " + (i+1)});
50 | }
51 |
52 | //FUNCTIONS
53 | inline function knobFactory(x, y, name, obj)
54 | {
55 | local control = Content.addKnob(name, x, y);
56 |
57 | //Set control properties
58 | for (k in obj) //Each key in object
59 | {
60 | control.set(k, obj[k]);
61 | }
62 |
63 | return control;
64 | };
65 |
66 | inline function buttonFactory(x, y, name, obj)
67 | {
68 | local control = Content.addButton(name, x, y);
69 |
70 | //Set control properties
71 | for (k in obj) //Each key in object
72 | {
73 | control.set(k, obj[k]);
74 | }
75 |
76 | return control;
77 | };
78 |
79 | inline function muteSolo()
80 | {
81 | soloOn = false;
82 | //Determine if any channels are soloed in order to set soloOn flag
83 | for (i = 0; i < solo.length; i++) //Each solo slider
84 | {
85 | if (solo[i].getValue() == 1)
86 | {
87 | soloOn = true; //Flag that at least one channel is soloed
88 | mute[i].setValue(0); //Toggle mute state
89 | }
90 | }
91 |
92 | for (i = 0; i < mute.length; i++)
93 | {
94 | //If a channel is soloed set volume of all non-soloed channels to 0, if no channel is soloed restore volume of non-muted channels
95 | if (mute[i].getValue() == 1) continue; //Ignore muted channels
96 |
97 | if (solo[i].getValue() == 1) //Channel is soloed
98 | {
99 | gainFx[i].setAttribute(0, vol[i].getValue());
100 | mute[i].setValue(0); //Toggle mute state
101 | }
102 | else //Channel is not soloed
103 | {
104 | if (soloOn == true) //If at least one channel is soloed
105 | {
106 | gainFx[i].setAttribute(0, -100); //Mute the channel
107 | }
108 | else //No channels soloed
109 | {
110 | gainFx[i].setAttribute(0, vol[i].getValue()); //Restore channel's volume
111 | }
112 | }
113 | }
114 | }
115 | function onNoteOn()
116 | {
117 |
118 | }
119 | function onNoteOff()
120 | {
121 |
122 | }
123 | function onController()
124 | {
125 |
126 | }
127 | function onTimer()
128 | {
129 |
130 | }
131 | function onControl(number, value)
132 | {
133 | for (i = 0; i < vol.length; i++) //Each mic position
134 | {
135 | if (number == vol[i])
136 | {
137 | if (mute[i].getValue() == 0) //Channel isn't muted
138 | {
139 | //No channels are soloed or this channel is soloed
140 | if (soloOn == false || (soloOn == true && solo[i].getValue()))
141 | {
142 | gainFx[i].setAttribute(gainFx[i].Gain, value);
143 | }
144 | }
145 | }
146 | else if (number == delay[i])
147 | {
148 | gainFx[i].setAttribute(gainFx[i].Delay, value);
149 | }
150 | else if (number == width[i])
151 | {
152 | gainFx[i].setAttribute(gainFx[i].Width, value);
153 | }
154 | else if (number == pan[i])
155 | {
156 | gainFx[i].setAttribute(gainFx[i].Balance, value);
157 | }
158 | else if (number == mute[i])
159 | {
160 | if (value == 1) //Mute is active for channel
161 | {
162 | gainFx[i].setAttribute(gainFx[i].Gain, -100); //Silence channel
163 | solo[i].setValue(0); //Toggle solo state
164 | }
165 | muteSolo();
166 | }
167 | else if (number == solo[i])
168 | {
169 | muteSolo();
170 | }
171 | else if (number == purge[i])
172 | {
173 | for (s in samplers) //Each sampler
174 | {
175 | if (s.getNumMicPositions() > 1 && s.isMicPositionPurged(i) != value) //Only purge or load if it's not already purged or loaded
176 | {
177 | s.purgeMicPosition(s.getMicPositionName(i), value);
178 | }
179 | }
180 | }
181 | }
182 | }
183 |
184 |
--------------------------------------------------------------------------------
/modules/GlobalVariables.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: globalVariables.js
3 | * Author: David Healey
4 | * License: Public Domain
5 | */
6 |
7 | /*Should be placed directly after interface script*/
8 |
9 | global g_realVelocity; //Keeps track of velocity as it comes into the chain
10 | global g_keys = 0; //Keeps track of number of pressed keys - avoids issues with artificial notes
11 | function onNoteOn()
12 | {
13 | g_realVelocity = Message.getVelocity();
14 | g_keys++;
15 | }
16 | function onNoteOff()
17 | {
18 | g_keys--;
19 | }
20 | function onController()
21 | {
22 |
23 | }
24 | function onTimer()
25 | {
26 |
27 | }
28 | function onControl(number, value)
29 | {
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/modules/GuitarChordDictionary.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019, 2020 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | namespace ChordDictionary
19 | {
20 | const var chordTypes = [
21 | [0, 4, 7], //Major
22 | [0, 3, 7], //Minor
23 | [0, 4, 7, 9], //Major 6th
24 | [0, 2, 3, 7, 9], //Minor 6 add9
25 | [0, 4, 7, 10], //Dominant 7th
26 | [0, 4, 7, 11], //Major 7th
27 | [0, 3, 7, 10], //Minor 7th
28 | [0, 3, 7, 11], //mM7
29 | [0, 4, 6, 7, 11], //Major 7#11
30 | [0, 5, 7, 10], //7th sus4
31 | [0, 2, 4, 6, 9, 11], //Major 13th #11
32 | [0, 2, 3, 7, 10], //Minor 9th
33 | [0, 2, 3, 5, 7, 10], //Minor 11th
34 | [0, 2, 3, 5, 7, 9, 10], //Minor 13th
35 | [0, 3, 6], //Diminished
36 | [0, 3, 6, 9], //Diminished 7th
37 | [0, 3, 6, 10], //Half diminished 7th
38 | [0, 3, 6, 11], //Diminished Major 7th
39 | [0, 4, 8], //Augmented
40 | [0, 2, 7] //Sus2
41 | ];
42 |
43 | const var chordNames = [
44 | "Major",
45 | "Minor",
46 | "Major 6th",
47 | "Minor 6 add9",
48 | "Dominant 7th",
49 | "Major 7th",
50 | "Minor 7th",
51 | "mM7",
52 | "Major 7#11",
53 | "7th sus4",
54 | "Major 13th #11",
55 | "Minor 9th",
56 | "Minor 11th",
57 | "Minor 13th",
58 | "Diminished",
59 | "Diminished 7th",
60 | "7b5",
61 | "Dim M7",
62 | "Augmented",
63 | "Sus2"
64 | ];
65 |
66 | inline function getUserChord(tuning, fret, keys)
67 | {
68 | local data = g_userChords; //Get persistent data object by ref
69 |
70 | if (data != undefined && data[tuning.join()] != undefined && data[tuning.join()][fret] != undefined)
71 | {
72 | keys.sort();
73 |
74 | //Search for existing entry
75 | local index = data[tuning.join()][fret].indexes.indexOf(keys.join());
76 |
77 | if (index != -1)
78 | return data[tuning.join()][fret].strings[index];
79 | else
80 | return [];
81 | }
82 | else
83 | return [];
84 | }
85 |
86 | inline function storeUserChord(tuning, fret, keys, strings)
87 | {
88 | local data = PersistentData.get("userChords"); //Get persistent data object
89 |
90 | if (data == undefined) data = {};
91 | if (data[tuning.join()] == undefined) data[tuning.join()] = [];
92 | if (data[tuning.join()][fret] == undefined) data[tuning.join()][fret] = {"indexes":[], "strings":[]};
93 |
94 | keys.sort();
95 |
96 | //Search for existing entry
97 | local index = data[tuning.join()][fret].indexes.indexOf(keys.join());
98 |
99 | if (index != -1)
100 | data[tuning.join()][fret].strings[index] = strings;
101 | else
102 | {
103 | data[tuning.join()][fret].indexes.push(keys.join());
104 | data[tuning.join()][fret].strings.push(strings);
105 | }
106 |
107 | g_userChords = data; //Update global object
108 | PersistentData.set("userChords", data); //Update persistent data object
109 | }
110 |
111 | inline function removeUserChord(tuning, fret, keys)
112 | {
113 | local data = PersistentData.get("userChords"); //Get persistent data object
114 |
115 | if (data != undefined && data[tuning.join()] != undefined && data[tuning.join()][fret] != undefined)
116 | {
117 | keys.sort();
118 |
119 | //Search for existing entry
120 | local index = data[tuning.join()][fret].indexes.indexOf(keys.join());
121 |
122 | if (index != -1)
123 | {
124 | data[tuning.join()][fret].indexes.remove(data[tuning.join()][fret].indexes[index]);
125 | data[tuning.join()][fret].strings.remove(data[tuning.join()][fret].strings[index]);
126 | }
127 | }
128 |
129 | g_userChords = data; //Update global object
130 | PersistentData.set("userChords", data); //Update persistent data object
131 | }
132 |
133 | inline function getChord(notes, fret)
134 | {
135 | local sorted = []; //Holds sorted note data
136 | local search = []; //Holds pattern to search for in the dictionary
137 | local chordRoot;
138 | local index;
139 |
140 | //Sort the note data and remove duplicates
141 | for ( i = 0; i < notes.length; i++)
142 | {
143 | if (notes[i] != undefined && !sorted.contains(notes[i] % 12))
144 | sorted.push(notes[i] % 12);
145 | }
146 |
147 | sorted.sort();
148 |
149 | /*Seach dictionary for match to sorted array
150 | Shuffle array if no match is found until all choices have been tried.*/
151 | for (i = 0; i < sorted.length; i++)
152 | {
153 | for (j = 0; j < sorted.length; j++)
154 | {
155 | chordRoot = sorted[i]; //Try next note as root
156 | search[j] = (sorted[j] + ((j < i) * 12)) - chordRoot;
157 | }
158 |
159 | search.sort();
160 |
161 | //Check if the search pattern is present in the dictionary
162 | index = chordTypes.indexOf(search);
163 |
164 | //Exit loop if search match entry in chord dictionary
165 | if (index != -1)
166 | break;
167 | }
168 |
169 | if (index != -1)
170 | return g_chordMap[chordRoot][index][fretPos];
171 | else
172 | return [];
173 | }
174 |
175 | inline function getChordVoicing(data, fretPos, ref)
176 | {
177 | local notes = []; //Holds sorted data
178 | local search = []; //Holds pattern to search for in the dictionary
179 | local chordRoot;
180 | local index;
181 |
182 | //Sort the note data and remove duplicates
183 | for ( i = 0; i < data.length; i++)
184 | {
185 | if (data[i] != undefined && !notes.contains(data[i] % 12))
186 | notes.push(data[i] % 12);
187 | }
188 |
189 | notes.sort();
190 |
191 | /*Seach dictionary for match to notes array
192 | Shuffle array if no match is found until all choices have been tried.*/
193 | for (i = 0; i < notes.length; i++)
194 | {
195 | for (j = 0; j < notes.length; j++)
196 | {
197 | chordRoot = notes[i]; //Try next note as root
198 | search[j] = (notes[j] + ((j < i) * 12)) - chordRoot;
199 | }
200 |
201 | search.sort();
202 |
203 | //Check if the search pattern is present in the dictionary
204 | index = chordTypes.indexOf(search);
205 |
206 | //Exit loop if search match entry in chord dictionary
207 | if (index != -1)
208 | break;
209 | }
210 |
211 | if (index != -1)
212 | {
213 | if (ref)
214 | return referenceMap[chordRoot][index][fretPos];
215 | else
216 | return g_chordMap[chordRoot][index][fretPos];
217 | }
218 | else
219 | return [];
220 | }
221 |
222 | inline function getType(index)
223 | {
224 | return chordTypes[index];
225 | }
226 |
227 | //Create chord voicing lookup table
228 | inline function generateChordTable(tuning, numFrets)
229 | {
230 | local result = [[], [], [], [], [], [], [], [], [], [], [], []]; //One object per root note
231 | local i;
232 | local j;
233 | local f;
234 |
235 | for (i = 0; i < result.length; i++) //Each root note
236 | {
237 | for (j = 0; j < chordTypes.length; j++) //Each chord type
238 | {
239 | for (f = 0; f < numFrets; f++) //Each fret
240 | {
241 | local voicing = voiceChord(tuning, i, j, f);
242 |
243 | if (result[i][j] == undefined)
244 | result[i][j] = [];
245 |
246 | result[i][j][f] = voicing;
247 | }
248 | }
249 | }
250 | return result;
251 | }
252 |
253 | //Wrapper function
254 | inline function voiceChord(tuning, rootNote, chordType, fretPos)
255 | {
256 | local intervals = chordTypes[chordType];
257 | local options = getOptions(tuning, rootNote, intervals, fretPos);
258 | return filterOptions(options, tuning, rootNote, intervals);
259 | }
260 |
261 | //Gather possible frets for each string
262 | inline function getOptions(tuning, rootNote, intervals, fretPos)
263 | {
264 | local result = [];
265 | local i;
266 | local j;
267 |
268 | for (i = 0; i < tuning.length; i++)
269 | {
270 | local s = (fretPos + tuning[i]) % 12;
271 |
272 | for (j = 0; j < intervals.length; j++)
273 | {
274 | local n = (rootNote + intervals[j]) % 12;
275 |
276 | if (n < s) n = n + 12;
277 |
278 | local f = Math.abs(n-s);
279 |
280 | if (f < 5)
281 | {
282 | if (result[i] == undefined)
283 | result[i] = [];
284 |
285 | result[i].push(f+fretPos);
286 | }
287 | }
288 | }
289 | return result;
290 | }
291 |
292 | //Filter fret options to generated chord voicing
293 | inline function filterOptions(options, tuning, rootNote, intervals)
294 | {
295 | local result = [];
296 | local fretted = 0;
297 | local count = [];
298 | local lowest = 99;
299 | local notes = [];
300 | local third = rootNote + intervals[1];
301 | local i;
302 | local j;
303 |
304 | for (i = 0; i < options.length; i++)
305 | {
306 | result[i] = -1;
307 | local lastFret = 0;
308 | local s = (tuning[i]) % 12;
309 |
310 | if (options[i])
311 | {
312 | for (j = 0; j < options[i].length; j++)
313 | {
314 | local f = options[i][j];
315 | local n = s + f; //Get normalized note number
316 |
317 | if (count[f] == undefined)
318 | count[f] = 0;
319 |
320 | if (fretted < 4 || i < 5 || (f == lowest && count[f] > 1))
321 | {
322 | //Don't double fretted thirds
323 | if (n == third && notes.contains(third) && f != 0)
324 | continue;
325 |
326 | local temp = result[i]; //Copy of fret for this string to compare
327 |
328 | //Go for the lowest fret possible
329 | if (f < result[i] || result[i] == -1)
330 | result[i] = f;
331 |
332 | //Make sure the fret is compatible with the options on the previous and next strings
333 | if (i > 0 && options[i-1] && options[i-1].contains(f) && i < options.length-1 && options[i+1] && !options[i+1].contains(result[i]))
334 | result[i] = f;
335 |
336 | if (typeof options[i+1] == "object" && i < options.length-1 && options[i+1].contains(f))
337 | result[i] = f;
338 |
339 | if (i > 2 && result[i-1] == -1)
340 | result[i] = -1;
341 |
342 | lastFret = f;
343 |
344 | //Count number of frets being played
345 | if (result[i] != 0)
346 | {
347 | fretted++;
348 | count[f]++;
349 | }
350 |
351 | //Compare string with comparison copy and record the note number
352 | if (result[i] != temp && f != 0)
353 | notes[i] = n;
354 | }
355 | }
356 |
357 | //Keep track of lowest fret and fretted notes
358 | if (lastFret != 0 && lastFret < lowest)
359 | {
360 | lowest = lastFret;
361 | }
362 | }
363 | }
364 | return result;
365 | }
366 | }
--------------------------------------------------------------------------------
/modules/GuitarPicker.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019, 2020 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | Content.setWidth(750);
19 | Content.setHeight(200);
20 |
21 | const var eventIds = Engine.createMidiList();
22 |
23 | reg channel = 0;
24 | reg forceString = -1;
25 | reg lastTime = 0;
26 | reg forceString = -1; //Force the use of a particular string
27 | reg direction = 0;
28 | reg lastVelocity;
29 |
30 | //Round robin variables
31 | const var NUM_RR = 2; //Total RRs
32 | reg rr = 0; //Current RR
33 | const var lastRR = Engine.createMidiList();
34 | lastRR.fill(0);
35 |
36 | const var noteString = Engine.createMidiList(); //Map string used for each note when played
37 |
38 | //Polyphonic variables (1 element per string)
39 | const var notes = []; //Current note for each string
40 | const var slideDecays = []; //Volume decay value
41 | const var timerCounters = []; //Timer loop counters
42 | const var timers = []; //Timer objects
43 | const var intervals = []; //Legato/slide interval
44 | const var timerOrigin = [];
45 | const var timerTarget = [];
46 | const var lastTimes = []; //Last time each string was triggered
47 | const var retrigger = []; //Legato retrigger notes
48 |
49 | //Timer object callback
50 | function timerAction()
51 | {
52 | var s = timers.indexOf(this);
53 | var v;
54 | var note;
55 | var bend;
56 | var rate = knbSlideSpeed.getValue();
57 | var newId;
58 | var oldId = eventIds.getValue(timerOrigin[s]);
59 |
60 | //Select bend, either up or down
61 | intervals[s] > 0 ? bend = 100 : bend = -100;
62 |
63 | //Fade out old note
64 | if (oldId > 0)
65 | {
66 | Synth.addPitchFade(oldId, rate, 0, bend); //Pitch fade old note to bend amount
67 | Synth.addVolumeFade(oldId, rate, -100); //Fade out last note
68 | eventIds.setValue(timerOrigin[s], 0);
69 | }
70 |
71 | //Move to next note, either up or down
72 | intervals[s] > 0 ? timerOrigin[s]++ : timerOrigin[s]--;
73 |
74 | note = timerOrigin[s] + knbCapo.getValue();
75 |
76 | v = 1 + (s * 4) + rr; //Get velocity to trigger corrent sample
77 |
78 | //Play new note
79 | newId = Synth.playNoteWithStartOffset(channel, note, v, 20000);
80 |
81 | //Fade in new note
82 | Synth.addVolumeFade(newId, 0, -99); //Set new note's initial volume
83 | Synth.addVolumeFade(newId, rate, Math.min(0, slideDecays[s] * timerCounters[s])); //Fade in new note
84 | Synth.addPitchFade(newId, 0, 0, -bend); //Set new note's initial detuning
85 | Synth.addPitchFade(newId, rate, 0, 0); //Pitch fade new note to 0
86 |
87 | eventIds.setValue(timerOrigin[s], newId);
88 |
89 | timerCounters[s]++;
90 |
91 | if (Synth.getNumPressedKeys() < 1 || timerOrigin[s] == timerTarget[s] || timerCounters[s] >= Math.abs(intervals[s]) || (intervals[s] < 0 && timerOrigin[s] == tuning[s]))
92 | this.stopTimer();
93 | }
94 |
95 | //Make timer object (7 = max strings)
96 | for (i = 0; i < 7; i++)
97 | {
98 | timers[i] = Engine.createTimerObject();
99 | timers[i].setTimerCallback(timerAction);
100 | }
101 |
102 | /*GUI*/
103 | //btnMute
104 | const var btnMute = Content.addButton("btnMute", 10, 10);
105 | btnMute.set("text", "Mute");
106 |
107 | //cmbDirection
108 | const var cmbDirection = Content.addComboBox("cmbDirection", 160, 10);
109 | cmbDirection.set("items", ["Down", "Up", "Alternate"].join("\n"));
110 |
111 | //btnRetrigger
112 | const var btnRetrigger = Content.addButton("btnRetrigger", 10, 60);
113 | btnRetrigger.set("text", "Retrigger");
114 |
115 | //knbCapo
116 | const var knbCapo = Content.addKnob("knbCapo", 160, 50);
117 | knbCapo.set("text", "Capo");
118 | knbCapo.setRange(0, 16, 1);
119 |
120 | //knbLegatoRange
121 | const var knbLegatoRange = Content.addKnob("knbLegatoRange", 310, 50);
122 | knbLegatoRange.set("text", "Legato Range");
123 | knbLegatoRange.setRange(0, 15, 1);
124 |
125 | //btnSlide
126 | const var btnSlide = Content.addButton("btnSlide", 10, 110);
127 | btnSlide.set("text", "Slide");
128 |
129 | //knbSlideDecay
130 | const var knbSlideDecay = Content.addKnob("knbSlideDecay", 160, 100);
131 | knbSlideDecay.set("text", "Slide Decay");
132 | knbSlideDecay.setRange(-50, 0, 1);
133 | knbSlideDecay.set("suffix", " dB");
134 |
135 | //knbSlideSpeed
136 | const var knbSlideSpeed = Content.addKnob("knbSlideSpeed", 310, 100);
137 | knbSlideSpeed.set("text", "Slide Speed");
138 | knbSlideSpeed.set("mode", "Time");
139 | knbSlideSpeed.set("defaultValue", 80);
140 | knbSlideSpeed.setRange(50, 200, 1);
141 |
142 | //Higher velocity = shorter slide time
143 | inline function setSlideRateFromVelocity(v)
144 | {
145 | local t = (knbSlideSpeed.get("max") - knbSlideSpeed.get("min")) / 127 * v;
146 | knbSlideSpeed.setValue(knbSlideSpeed.get("max") - t);
147 | }function onNoteOn()
148 | {
149 | if (!btnMute.getValue())
150 | {
151 | channel = Message.getChannel();
152 | local n = Message.getNoteNumber(); //Record incoming note number
153 | local d = 1 + g_velocityLayer * g_tuning.length * 4; //Dynamic velocity level
154 | local notesPerString = g_patch.notesPerString;
155 |
156 | local capo = knbCapo.getValue();
157 |
158 | if (n >= g_ranges[1][0] + capo && n <= g_ranges[1][1]) //Force string keys
159 | {
160 | forceString = g_ranges[1][1] - n;
161 | }
162 | else if (n >= g_ranges[2][0] && n <= g_ranges[2][1]) //Strum keys
163 | {
164 | for (i = 0; i < notes.length; i++)
165 | {
166 | if (notes[i] && eventIds.getValue(notes[i]) > 0)
167 | {
168 | Synth.noteOffByEventId(eventIds.getValue(notes[i]));
169 | eventIds.setValue(notes[i], 0);
170 | notes[i] = undefined;
171 | }
172 | }
173 | }
174 | else
175 | {
176 | if (n >= g_ranges[0][0] + capo && n <= g_ranges[0][1])
177 | {
178 | //Get default string for note
179 | local s = g_map.getValue(n - capo);
180 |
181 | //Set direction
182 | if (cmbDirection.getValue() == 3)
183 | direction == 0 ? direction = 1 : direction = 0;
184 | else
185 | direction = cmbDirection.getValue()-1;
186 |
187 | //Set round robin - accounting for direction
188 | if (direction || cmbDirection.getValue() != 3)
189 | {
190 | rr = (lastRR.getValue(n) - 1 + Math.randInt(2, NUM_RR)) % NUM_RR;
191 | lastRR.setValue(n, rr);
192 | }
193 |
194 | if (knbLegatoRange.getValue() == 0) //Polyphonic
195 | {
196 | local v = d + (s * 4) + (direction * 2) + rr;
197 | local id = Synth.playNote(n + capo, v);
198 | eventIds.setValue(n, id);
199 | }
200 | else
201 | {
202 | //Forced string
203 | if (forceString != -1 && n >= g_tuning[forceString] && n <= (g_tuning[forceString] + notesPerString-3))
204 | {
205 | s = forceString;
206 | }
207 | else
208 | {
209 | //Check for two notes on same string
210 | local sameString;
211 | if (notes[s])
212 | sameString = (Engine.getUptime() - lastTimes[s]) < 0.025;
213 | else
214 | sameString = false;
215 |
216 | //For two notes on same string at same time, find the next available string
217 | if (sameString && Synth.getNumPressedKeys() > 1)
218 | {
219 | while (s < g_tuning.length-1 && notes[s])
220 | {
221 | s++;
222 | }
223 | }
224 |
225 | //Two notes played together, on any strings
226 | local isChord = (Engine.getUptime() - lastTime) < 0.025;
227 |
228 | //Figure out legato string config
229 | if (!isChord && Synth.getNumPressedKeys() > 1)
230 | {
231 | local lastS = s;
232 |
233 | while (lastS < g_tuning.length-1 && !notes[lastS])
234 | {
235 | lastS++;
236 | }
237 |
238 | if (notes[lastS] && n - notes[lastS] < knbLegatoRange.getValue())
239 | s = lastS;
240 | }
241 | }
242 |
243 | //Calculate velocity, accounting for note, string, rr, and direction
244 | local v = d + (s * 4) + rr + (direction * 2);
245 |
246 | //Reset force string if no force key held
247 | if (forceString != -1 && !Synth.isKeyDown(g_ranges[1][1]-forceString))
248 | forceString = -1;
249 |
250 | if (notes[s] && !sameString && Math.abs(n - notes[s]) <= knbLegatoRange.getValue()) //Phrase note
251 | {
252 | if (btnSlide.getValue() && notes[s] != n) //Slide
253 | {
254 | //Stop string's timer
255 | timers[s].stopTimer();
256 |
257 | //Set interval
258 | intervals[s] = n - notes[s];
259 |
260 | //Calculate slide decay
261 | slideDecays[s] = knbSlideDecay.getValue() / Math.abs(intervals[s]);
262 |
263 | //Set start and end note for timer
264 | timerOrigin[s] = notes[s];
265 | timerTarget[s] = n;
266 |
267 | //Reset fail safe counter
268 | timerCounters[s] = 0;
269 |
270 | //Set slide rate
271 | setSlideRateFromVelocity(g_velocity);
272 | g_velocity = lastVelocity / 2;
273 |
274 | //Start timer
275 | timers[s].startTimer(knbSlideSpeed.getValue());
276 | }
277 | else //Legato
278 | {
279 | local detune;
280 |
281 | //Calculate played interval
282 | intervals[s] = n - notes[s];
283 | intervals[s] > 0 ? detune = -10 : detune = 10;
284 |
285 | //Play and fade in new note
286 | local newId = Synth.playNoteWithStartOffset(channel, n + capo, v, 5000);
287 | Synth.addVolumeFade(newId, 0, -99);
288 | Synth.addVolumeFade(newId, 20, 0);
289 | Synth.addPitchFade(newId, 0, 0, detune); //Set initial deg_tuning
290 | Synth.addPitchFade(newId, 20, 0, 0); //Fade to final pitch
291 |
292 | //Fade out old note
293 | local oldId = eventIds.getValue(notes[s]);
294 |
295 | if (oldId > 0)
296 | {
297 | //Adjust fade time if playing a retriggered note
298 | local fadeTm;
299 | retrigger[s] == n ? fadeTm = 20 : fadeTm = 300;
300 |
301 | Synth.addVolumeFade(oldId, fadeTm, -100);
302 | Synth.addPitchFade(oldId, 250, 0, -detune); //Fade to final pitch
303 | eventIds.setValue(notes[s], 0);
304 |
305 | eventIds.setValue(n, newId);
306 | retrigger[s] = notes[s];
307 | }
308 | }
309 | }
310 | else
311 | {
312 | //Turn off old note
313 | if (notes[s] && eventIds.getValue(notes[s]))
314 | {
315 | Synth.noteOffByEventId(eventIds.getValue(notes[s]));
316 | eventIds.setValue(notes[s], 0);
317 | }
318 |
319 | local id = Synth.playNote(n + capo, v);
320 | eventIds.setValue(n, id);
321 | retrigger[s] = notes[s];
322 | }
323 |
324 | notes[s] = n; //Update current note for string
325 | noteString.setValue(n, s); //Update current string for note
326 | }
327 | }
328 |
329 | if (s >= 0 && s !== undefined)
330 | {
331 | if (n + capo < g_tuning[s] + capo)
332 | g_strings[s] = undefined;
333 | else
334 | g_strings[s] = Math.abs(n - (g_tuning[s] + capo));
335 |
336 | lastTimes[s] = Engine.getUptime(); //Track time per string
337 | }
338 |
339 | lastVelocity = g_velocity;
340 | lastTime = Engine.getUptime(); //Track time all strings
341 | }
342 | }
343 | } function onNoteOff()
344 | {
345 | local n = Message.getNoteNumber();
346 | local s = noteString.getValue(n);
347 | local d = 1 + g_velocityLayer * g_tuning.length * 4; //Dynamic velocity level
348 | local capo = knbCapo.getValue();
349 |
350 | //Force string keys
351 | if (n >= g_ranges[1][0] + capo && n <= g_ranges[1][1])
352 | {
353 | forceString = -1;
354 | }
355 | else
356 | {
357 | if (!eventIds.isEmpty() && n >= g_ranges[0][0] + capo && n <= g_ranges[0][1])
358 | {
359 | if (knbLegatoRange.getValue() > 0)
360 | {
361 | //Turn off released note
362 | if (eventIds.getValue(n) > 0 && !Synth.isSustainPedalDown())
363 | {
364 | Synth.noteOffByEventId(eventIds.getValue(n));
365 | eventIds.setValue(n, 0);
366 | }
367 |
368 | //If retrigger note is released, reset retrigger note
369 | if (n == retrigger[s] && !Synth.isSustainPedalDown())
370 | retrigger[s] = undefined;
371 | else if (Synth.isSustainPedalDown())
372 | retrigger[s] = n;
373 |
374 | //Handle Retrigger
375 | if (retrigger[s] && btnRetrigger.getValue() && n == notes[s])
376 | {
377 | direction = 1-direction;
378 | local v = d + (s * 4) + (direction * 2) + rr;
379 |
380 | //Play retrigger note
381 | local id = Synth.playNoteWithStartOffset(Message.getChannel(), retrigger[s] + capo, v, 5000);
382 | eventIds.setValue(retrigger[s], id);
383 |
384 | //Update variables
385 | notes[s] = retrigger[s];
386 | g_strings[s] = Math.abs(retrigger[s] - g_tuning[s]);
387 | }
388 | else if (notes[s] && eventIds.getValue(notes[s]) <= 0)
389 | {
390 | notes[s] = undefined;
391 | g_strings[s] = undefined;
392 | }
393 |
394 | if (!Synth.getNumPressedKeys())
395 | timers[s].stopTimer();
396 | }
397 | else //Polyphonic
398 | {
399 | if (eventIds.getValue(n) > 0 && (!Synth.isSustainPedalDown() || btnRetrigger.getValue()))
400 | {
401 | local id = eventIds.getValue(n);
402 | Synth.noteOffByEventId(id);
403 | eventIds.setValue(n, 0);
404 | }
405 |
406 | if (btnRetrigger.getValue() && Synth.isSustainPedalDown())
407 | {
408 | s = g_map.getValue(n);
409 | direction = 1-direction;
410 | local v = d + (s * 4) + (direction * 2) + rr;
411 |
412 | Synth.playNote(n, v);
413 | }
414 | }
415 | }
416 |
417 | //All keys up
418 | if (!Synth.getNumPressedKeys() && !Synth.isSustainPedalDown())
419 | {
420 | //Stop all timers
421 | for (i = 0; i < timers.length; i++)
422 | {
423 | timers[i].stopTimer();
424 | }
425 |
426 | //Kill all notes
427 | for (i = 0; i < 128; i++)
428 | {
429 | if (eventIds.getValue(i) > 0)
430 | Synth.noteOffByEventId(eventIds.getValue(i));
431 | }
432 |
433 | eventIds.clear();
434 | notes.clear();
435 | retrigger.clear();
436 |
437 | if (!btnMute.getValue() && g_strings)
438 | g_strings.clear();
439 | }
440 | }
441 | }function onController()
442 | {
443 | if (Message.getControllerNumber() == 64)
444 | {
445 | if (!btnMute.getValue())
446 | Message.ignoreEvent(true);
447 |
448 | if (!Synth.isSustainPedalDown() && !Synth.getNumPressedKeys())
449 | {
450 | eventIds.clear();
451 | notes.clear();
452 |
453 | if (!btnMute.getValue() && g_strings)
454 | g_strings.clear();
455 |
456 | Engine.allNotesOff();
457 | Synth.stopTimer();
458 | }
459 | }
460 | } function onTimer()
461 | {
462 |
463 | }
464 | function onControl(number, value)
465 | {
466 |
467 | }
468 |
--------------------------------------------------------------------------------
/modules/GuitarStrummer.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019, 2020 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | Content.setWidth(750);
19 | Content.setHeight(250);
20 | //include("chordDictionary.js"); //Chord dictionary is required
21 |
22 | Synth.deferCallbacks(true);
23 |
24 | reg channel;
25 | reg direction = 0;
26 | reg stringCounter;
27 | reg startString;
28 | reg chordTones;
29 | reg strings = []; //One element per string
30 | reg timerNote;
31 | reg timerString;
32 | reg capo = 0;
33 | reg dynamic;
34 | const var ids = []; //One element per string
35 |
36 | reg rr = 0;
37 | const var NUM_RR = 2;
38 | const var lastRR = Engine.createMidiList();
39 | lastRR.fill(0);
40 |
41 | /*GUI*/
42 |
43 | //btnMute
44 | const var btnMute = Content.addButton("btnMute", 10, 10);
45 | btnMute.set("text", "Mute");
46 |
47 | //cmbDirection
48 | const var cmbDirection = Content.addComboBox("cmbDirection", 160, 10);
49 | cmbDirection.set("items", ["Silent", "Down", "Up", "Alternate"].join("\n"));
50 |
51 | //knbFret
52 | const var knbFret = Content.addKnob("knbFret", 310, 0);
53 | knbFret.set("text", "Fret Position");
54 | knbFret.setRange(0, 30, 1);
55 |
56 | //knbCapo
57 | const var knbCapo = Content.addKnob("knbCapo", 460, 0);
58 | knbCapo.set("text", "Capo");
59 | knbCapo.setRange(0, 16, 1);
60 |
61 | //btnAutoPos - not implemented
62 | const var btnAutoPos = Content.addButton("btnAutoPos", 10, 60);
63 | btnAutoPos.set("text", "Auto Fret Position");
64 |
65 | //knbStart
66 | const var knbStart = Content.addKnob("knbStart", 160, 50);
67 | knbStart.set("text", "Start String");
68 | knbStart.setRange(0, 3, 1);
69 |
70 | //knbLength
71 | const var knbLength = Content.addKnob("knbLength", 310, 50);
72 | knbLength.set("text", "Strum Length");
73 | knbLength.setRange(2, 6, 1);
74 |
75 | //knbDecay
76 | const var knbDecay = Content.addKnob("knbDecay", 460, 50);
77 | knbDecay.set("text", "Decay");
78 | knbDecay.set("mode", "normalizedPercentage");
79 | knbDecay.set("middlePosition", 0.5);
80 |
81 | //knbSpeed
82 | const var knbSpeed = Content.addKnob("knbSpeed", 610, 50);
83 | knbSpeed.set("text", "Speed");
84 | knbSpeed.setRange(100, 600, 1);
85 | knbSpeed.set("mode", "Time");
86 | knbSpeed.set("middlePosition", 150);
87 |
88 | inline function strum()
89 | {
90 | Synth.stopTimer();
91 |
92 | local start = knbStart.getValue();
93 | local end = start + knbLength.getValue();
94 |
95 | stringCounter = 0;
96 | direction == 0 ? startString = g_tuning.length-1 : startString = start;
97 |
98 | Synth.startTimer(knbSpeed.getValue()/10000);
99 | }function onNoteOn()
100 | {
101 | if (!btnMute.getValue() && !Message.isArtificial())
102 | {
103 | local n = Message.getNoteNumber();
104 | dynamic = 1 + g_velocityLayer * g_tuning.length * 4; //Dynamic velocity level
105 | channel = Message.getChannel();
106 | capo = knbCapo.getValue(); //Set capo variable
107 |
108 | if (n >= g_ranges[2][0] && n <= g_ranges[2][1]) //Strum keys
109 | {
110 | if (strings)
111 | {
112 | //Determine strum direction based on strum key
113 | if (n - g_ranges[2][0] == 0 || n - g_ranges[2][0] == 1)
114 | direction = 0;
115 | else if (n - g_ranges[2][0] == 2 || n - g_ranges[2][0] == 3)
116 | direction = 1;
117 |
118 | //Set round robin
119 | rr = (lastRR.getValue(n) - 1 + Math.randInt(2, NUM_RR)) % NUM_RR;
120 | lastRR.setValue(n, rr);
121 |
122 | //Muted samples only have 1 dynamic
123 | if (n == g_ranges[2][0]+1 || n == g_ranges[2][1])
124 | dynamic = 1;
125 |
126 | g_strings = strings.clone();
127 | strum();
128 | }
129 | }
130 | else //Playable range and picking keys
131 | {
132 | //Determine strum/pick direction
133 | if (cmbDirection.getValue() != 1)
134 | {
135 | if (cmbDirection.getValue() == 4)
136 | direction = 1-direction;
137 | else
138 | direction = cmbDirection.getValue() - 2;
139 | }
140 |
141 | //Set round robin - accounting for direction
142 | if (direction == 1 || cmbDirection.getValue() != 4)
143 | {
144 | rr = (lastRR.getValue(n) - 1 + Math.randInt(2, NUM_RR)) % NUM_RR;
145 | lastRR.setValue(n, rr);
146 | }
147 |
148 | if (n >= g_ranges[0][0] && n <= g_ranges[0][1]) //Playable range
149 | {
150 | if (Synth.getNumPressedKeys() > 2) //At least 3 keys are pressed
151 | {
152 | Synth.stopTimer();
153 |
154 | //Check for userChord
155 | strings = ChordDictionary.getUserChord(g_tuning, knbFret.getValue(), g_heldKeys);
156 |
157 | //If no userChord found check for chord in chordMap
158 | if (strings.length == 0)
159 | strings = ChordDictionary.getChordVoicing(g_heldKeys, knbFret.getValue(), false);
160 |
161 | //Strum the chord if direction is not set to Silent
162 | if (cmbDirection.getValue() != 1)
163 | {
164 | g_strings = strings.clone();
165 | strum();
166 | }
167 | }
168 | }
169 | else if (n >= g_ranges[1][0] && n <= g_ranges[1][1]) //Picking keys
170 | {
171 | local s = g_ranges[1][1] - n;
172 | local stringNote = g_tuning[s] + parseInt(strings[s]);
173 |
174 | //If chord string is unused, use open string
175 | if (stringNote < g_ranges[0][0])
176 | stringNote = g_tuning[s];
177 |
178 | if (ids[s])
179 | {
180 | Synth.noteOffByEventId(ids[s]);
181 | ids[s] = undefined;
182 | }
183 |
184 | if (stringNote - g_ranges[0][0] > -1)
185 | {
186 | local v = dynamic + (s * 4) + (2 * direction) + rr;
187 | ids[s] = Synth.playNote(stringNote + capo, v);
188 | }
189 | }
190 | }
191 | }
192 | }function onNoteOff()
193 | {
194 | local n = Message.getNoteNumber();
195 |
196 | if (!Message.isArtificial() && (!Synth.isSustainPedalDown() || cmbDirection.getValue()))
197 | {
198 | if (n >= g_ranges[1][0] && n <= g_ranges[1][1])
199 | {
200 | local s = g_ranges[1][1] - n;
201 |
202 | if (ids[s])
203 | Synth.noteOffByEventId(ids[s]);
204 |
205 | ids[s] = undefined;
206 | }
207 |
208 | if (!Synth.getNumPressedKeys() && !Synth.isSustainPedalDown())
209 | {
210 | if (!btnMute.getValue())
211 | g_strings = [];
212 |
213 | Synth.stopTimer();
214 |
215 | for (i = 0; i < ids.length; i++)
216 | {
217 | if (ids[i])
218 | {
219 | Synth.noteOffByEventId(ids[i]);
220 | ids[i] = undefined;
221 | }
222 | }
223 | }
224 | }
225 | } function onController()
226 | {
227 | if (!Synth.isSustainPedalDown() && !Synth.getNumPressedKeys())
228 | {
229 | if (!btnMute.getValue())
230 | g_strings = [];
231 |
232 | Synth.stopTimer();
233 |
234 | for (i = 0; i < ids.length; i++)
235 | {
236 | if (ids[i])
237 | {
238 | Synth.noteOffByEventId(ids[i]);
239 | ids[i] = undefined;
240 | }
241 | }
242 | }
243 | }
244 | function onTimer()
245 | {
246 | local s;
247 |
248 | if (stringCounter < g_tuning.length && stringCounter <= knbLength.getValue())
249 | {
250 | if (!direction) //Down
251 | {
252 | s = startString - stringCounter;
253 |
254 | //Skip unplayed strings
255 | if (strings[s] == -1)
256 | {
257 | startString--;
258 | s--;
259 | }
260 | }
261 | else //Up
262 | {
263 | s = startString + stringCounter;
264 |
265 | //Skip unplayed strings
266 | if (strings[s] == -1)
267 | {
268 | startString++;
269 | s++;
270 | }
271 | }
272 |
273 | //Turn off old note
274 | if (ids[stringCounter])
275 | {
276 | Synth.noteOffByEventId(ids[stringCounter]);
277 | ids[stringCounter] = undefined;
278 | }
279 |
280 | if (s > -1 && s < strings.length && strings[s] != -1)
281 | {
282 | //Get note number
283 | local n = g_tuning[s] + strings[s] + capo;
284 |
285 | //Play new note
286 | local v = dynamic + (4 * s) + rr + (2 * direction);
287 | ids[stringCounter] = Synth.playNote(n, v);
288 |
289 | //String decay
290 | local decay = (stringCounter) * knbDecay.getValue() * -10 / (g_tuning.length-1);
291 | Synth.addVolumeFade(ids[stringCounter], 0, decay);
292 | }
293 | }
294 |
295 | stringCounter++; //Count played strings
296 |
297 | if (stringCounter > g_tuning.length)
298 | Synth.stopTimer();
299 | } function onControl(number, value)
300 | {
301 |
302 | }
303 |
--------------------------------------------------------------------------------
/modules/HarpGliss.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 |
18 | Content.setWidth(750);
19 | Content.setHeight(100);
20 |
21 | const var whiteKeys = [0, 2, 4, 5, 7, 9, 11]; //White keys
22 |
23 | reg eventIds = Engine.createMidiList();
24 | reg origin;
25 | reg target;
26 | reg velocity;
27 | reg rate = 50;
28 |
29 | function gliss()
30 | {
31 | var oldId = eventIds.getValue(origin);
32 | var newId;
33 |
34 | //Turn off last gliss note
35 | if (oldId > 0)
36 | {
37 | Synth.noteOffByEventId(oldId);
38 | eventIds.setValue(origin, 0);
39 | }
40 |
41 | //Inc/Dec origin based on interval direction
42 | target > origin ? origin++ : origin--;
43 |
44 | //Snap origin to nearest white note
45 | if (whiteKeys.indexOf(origin % 12) == -1)
46 | target > origin ? origin++ : origin--;
47 |
48 | //Adjust for pedalling
49 | var note = origin + slpPedals.getSliderValueAt(whiteKeys.indexOf(origin % 12));
50 |
51 | //Play new note
52 | newId = Synth.playNote(note, velocity);
53 | eventIds.setValue(note, newId);
54 |
55 | //Stop timer when gliss completed
56 | if (Synth.getNumPressedKeys() < 1 || origin == target)
57 | this.stopTimer();
58 | }
59 |
60 | //Two timers - only one used but potential to add polyphonic gliss
61 | const var timers = [];
62 | for (i = 0; i < 2; i++)
63 | {
64 | timers[i] = Engine.createTimerObject();
65 | timers[i].setTimerCallback(gliss);
66 | }
67 |
68 | /*GUI*/
69 |
70 | //btnMute
71 | const var btnMute = Content.addButton("btnMute", 10, 10);
72 | btnMute.set("text", "Mute");
73 |
74 | //knbRate
75 | const var knbRate = Content.addKnob("knbRate", 160, 0);
76 | knbRate.set("mode", "TempoSync");
77 |
78 | inline function onknbRateControl(component, value)
79 | {
80 | rate = Engine.getMilliSecondsForTempo(value);
81 | };
82 |
83 | Content.getComponent("knbRate").setControlCallback(onknbRateControl);
84 |
85 | //slpPedals
86 | const var slpPedals = Content.addSliderPack("slpPedals", 310, 10);
87 | slpPedals.set("height", 50);
88 | slpPedals.set("sliderAmount", 7);
89 | slpPedals.set("min", -1);
90 | slpPedals.set("max", 1);
91 | slpPedals.set("stepSize", 1);
92 | slpPedals.referToData(g_harpPedals);
93 |
94 | function onNoteOn()
95 | {
96 | if (!btnMute.getValue())
97 | {
98 | local n = Message.getNoteNumber();
99 | local velocity = Message.getVelocity();
100 | if (velocity == 0) velocity = 1;
101 |
102 | local idx = whiteKeys.indexOf(n % 12);
103 |
104 | if (idx != -1) //White key triggered callback
105 | {
106 | if (origin && Synth.isLegatoInterval() && Math.abs(origin - n) > 2)
107 | {
108 | Message.ignoreEvent(true);
109 |
110 | target = n;
111 | timers[0].startTimer(rate);
112 | }
113 | else //First note of gliss
114 | {
115 | local v = slpPedals.getSliderValueAt(idx);
116 | eventIds.setValue(n + v, Message.makeArtificial());
117 | Message.setNoteNumber(n + v);
118 | origin = n + v;
119 | }
120 | }
121 | else
122 | Message.ignoreEvent(true);
123 | }
124 | } function onNoteOff()
125 | {
126 | if (!btnMute.getValue())
127 | {
128 | local idx = whiteKeys.indexOf(Message.getNoteNumber() % 12);
129 |
130 | if (idx != -1)
131 | {
132 | local n = Message.getNoteNumber() + slpPedals.getSliderValueAt(idx);
133 |
134 | if (eventIds.getValue(n) > 0)
135 | {
136 | Synth.noteOffByEventId(eventIds.getValue(n));
137 | eventIds.setValue(n, 0);
138 | }
139 | }
140 |
141 | if (!Synth.getNumPressedKeys())
142 | origin = undefined;
143 | }
144 | }
145 | function onController()
146 | {
147 |
148 | }
149 | function onTimer()
150 | {
151 |
152 | }
153 | function onControl(number, value)
154 | {
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/modules/Humaniser.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018, 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with This file. If not, see .
16 | */
17 |
18 | Content.setWidth(600);
19 | Content.setHeight(100);
20 |
21 | reg gain;
22 | reg pitch;
23 | reg velocity;
24 | reg offset;
25 |
26 | const var noteOnDelays = Engine.createMidiList();
27 | noteOnDelays.fill(0);
28 |
29 | const knbGain = Content.addKnob("knbGain", 0, 0);
30 | knbGain.set("text", "Gain");
31 | knbGain.setRange(0, 6, 1);
32 | knbGain.set("suffix", "dB");
33 |
34 | const knbPitch = Content.addKnob("knbPitch", 150, 0);
35 | knbPitch.set("text", "Pitch");
36 | knbPitch.setRange(0, 50, 1);
37 | knbPitch.set("suffix", "ct");
38 |
39 | const knbVel = Content.addKnob("knbVel", 300, 0);
40 | knbVel.set("text", "Velocity");
41 | knbVel.setRange(0, 25, 1);
42 |
43 | const knbOffset = Content.addKnob("knbOffset", 450, 0);
44 | knbOffset.set("text", "Offset");
45 | knbOffset.setRange(0, 500, 1);
46 | knbOffset.set("suffix", "ms");
47 |
48 | const knbNoteOn = Content.addKnob("knbNoteOn", 0, 50);
49 | knbNoteOn.set("text", "Note On");
50 | knbNoteOn.setRange(0, 100, 1);
51 | knbNoteOn.set("suffix", "ms");
52 |
53 | const knbNoteOff = Content.addKnob("knbNoteOff", 150, 50);
54 | knbNoteOff.set("text", "Note Off");
55 | knbNoteOff.setRange(0, 100, 1);
56 | knbNoteOff.set("suffix", "ms");
57 |
58 | function onNoteOn()
59 | {
60 | gain = 0;
61 | pitch = 0;
62 | velocity = 0;
63 | offset = 0;
64 |
65 | //Generate random values
66 | if (knbGain.getValue() != 0) gain = Math.randInt(-knbGain.getValue(), knbGain.getValue()+1); //Gain
67 | if (knbPitch.getValue() != 0) pitch = Math.randInt(-knbPitch.getValue(), knbPitch.getValue()+1); //Pitch
68 | if (knbVel.getValue != 0) velocity = Math.randInt(-knbVel.getValue(), knbVel.getValue()+1); //Velocity
69 | if (knbOffset.getValue() != 0) offset = Engine.getSamplesForMilliSeconds(Math.random()*knbOffset.getValue()); //Start offset
70 |
71 | Message.setGain(Message.getGain() + gain);
72 | Message.setFineDetune(Message.getFineDetune() + pitch);
73 | Message.setVelocity(Math.range(Message.getVelocity() + velocity, 1, 127);
74 | Message.setStartOffset(offset);
75 |
76 | //Note on delay
77 | if (knbNoteOn.getValue() > 0)
78 | {
79 | noteOnDelays.setValue(Message.getNoteNumber(), Math.random() * knbNoteOn.getValue());
80 | Message.delayEvent(Engine.getSamplesForMilliSeconds(noteOnDelays.getValue(Message.getNoteNumber())));
81 | }
82 | }
83 | function onNoteOff()
84 | {
85 | Message.setGain(Message.getGain() + gain);
86 | Message.setFineDetune(Message.getFineDetune() + pitch);
87 | Message.setVelocity(Math.range(Message.getVelocity() + velocity, 1, 127);
88 | Message.setStartOffset(offset);
89 |
90 | //Note delay
91 | if (knbNoteOn.getValue() != 0 || knbNoteOff.getValue() != 0)
92 | {
93 | local delay = 0; //Reset
94 |
95 | //Compensate for note on delay
96 | if (knbNoteOn.getValue() != 0)
97 | {
98 | delay = noteOnDelays.getValue(Message.getNoteNumber());
99 | }
100 |
101 | //Note Off
102 | if (knbNoteOff.getValue() > 0)
103 | {
104 | delay += Math.random() * knbNoteOff.getValue(); //Note off delay
105 | }
106 |
107 | Message.delayEvent(Engine.getSamplesForMilliSeconds(delay));
108 | }
109 | }
110 | function onController()
111 | {
112 |
113 | }
114 | function onTimer()
115 | {
116 |
117 | }
118 | function onControl(number, value)
119 | {
120 |
121 | }
122 |
123 |
--------------------------------------------------------------------------------
/modules/KeyRangeColour.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: keyRangeColour.js
3 | * Author: David Healey
4 | * Date: 30/06/2017
5 | * Modified: 30/06/2017
6 | * License: GPLv3 - https://www.gnu.org/licenses/gpl-3.0.en.html
7 | */
8 |
9 | //Init
10 | Content.setWidth(650);
11 | Content.setHeight(50);
12 |
13 | const var colours = [Colours.blue, Colours.cyan, Colours.yellow, Colours.green, Colours.red, Colours.purple, Colours.black, Colours.white, Colours.orange, Colours.pink];
14 | var noteRange = [];
15 |
16 | //GUI
17 | const var knbLow = Content.addKnob("knbLow", 0, 0);
18 | knbLow.set("text", "Low Note");
19 | knbLow.setRange(0, 127, 1);
20 | knbLow.set("defaultValue", 0);
21 |
22 | const var knbHigh = Content.addKnob("knbHigh", 150, 0);
23 | knbHigh.set("text", "High Note");
24 | knbHigh.setRange(0, 127, 1);
25 | knbHigh.set("defaultValue", 1);
26 |
27 | const var cmbColour = Content.addComboBox("cmbColour", 300, 10);
28 | cmbColour.set("width", 100);
29 | cmbColour.set("text", "Colour");
30 | cmbColour.set("items", ["Blue", "Cyan", "Yellow", "Green", "Red", "Purple", "Black", "White", "Orange", "Pink"].join("\n"));
31 |
32 | const var btnSkipBlack = Content.addButton("btnSkipBlack", 410, 10);
33 | btnSkipBlack.set("width", 100);
34 | btnSkipBlack.set("text", "Skip Black");
35 |
36 | const var btnSkipWhite = Content.addButton("btnSkipWhite", 520, 10);
37 | btnSkipWhite.set("width", 100);
38 | btnSkipWhite.set("text", "Skip White");
39 |
40 | const var btnIgnore = Content.addButton("btnIgnore", 630, 10);
41 | btnIgnore.set("width", 100);
42 | btnIgnore.set("text", "Ignore Events");
43 |
44 | //Functions
45 | inline function inRange(value, min, max)
46 | {
47 | return value >= min && value <= max;
48 | }
49 |
50 | inline function isBlackKey(n)
51 | {
52 | var blackKeys = [1, 3, 6, 8, 10]; //Black keys in one octave assuming C = 0
53 |
54 | if (blackKeys.indexOf(parseInt(n) % 12) != -1)
55 | {
56 | return true;
57 | }
58 | return false;
59 | }
60 |
61 | inline function updateKeyboardColours()
62 | {
63 | local i;
64 |
65 | //Reset old range key colours
66 | for (i = noteRange[0]; i <= noteRange[1]; i++)
67 | {
68 | Engine.setKeyColour(i, Colours.withAlpha(Colours.white, 0.0));
69 | }
70 |
71 | //Set new range key colours
72 | for (i = knbLow.getValue(); i <= knbHigh.getValue(); i++)
73 | {
74 | if (btnSkipBlack.getValue() == 1 && isBlackKey(i)) continue;
75 | if (btnSkipWhite.getValue() == 1 && !isBlackKey(i)) continue;
76 |
77 | Engine.setKeyColour(i, Colours.withAlpha(colours[cmbColour.getValue()-1], 0.38));
78 | }
79 | }
80 |
81 | //Callbacks
82 | function onNoteOn()
83 | {
84 | if (btnIgnore.getValue())
85 | {
86 | if (!inRange(Message.getNoteNumber(), noteRange[0], noteRange[1]) || (btnSkipBlack.getValue() == 1 && isBlackKey(Message.getNoteNumber())) || (btnSkipWhite.getValue() == 1 && !isBlackKey(Message.getNoteNumber())))
87 | {
88 | Message.ignoreEvent(true);
89 | }
90 | }
91 | }
92 |
93 | function onNoteOff()
94 | {
95 | if (btnIgnore.getValue())
96 | {
97 | if (!inRange(Message.getNoteNumber(), noteRange[0], noteRange[1]) || (btnSkipBlack.getValue() == 1 && isBlackKey(Message.getNoteNumber())) || (btnSkipWhite.getValue() == 1 && !isBlackKey(Message.getNoteNumber())))
98 | {
99 | Message.ignoreEvent(true);
100 | }
101 | }
102 | }
103 |
104 | function onController()
105 | {
106 | }
107 |
108 | function onTimer()
109 | {
110 | }
111 |
112 | function onControl(number, value)
113 | {
114 | switch (number)
115 | {
116 | case knbLow:
117 | updateKeyboardColours();
118 | noteRange[0] = value;
119 | if (value > knbHigh.getValue()) knbHigh.setValue(value);
120 | break;
121 |
122 | case knbHigh:
123 | updateKeyboardColours();
124 | noteRange[1] = value;
125 | if (value < knbLow.getValue()) knbLow.setValue(value);
126 | break;
127 |
128 | case cmbColour:
129 | updateKeyboardColours();
130 | break;
131 |
132 | case btnSkipBlack:
133 | btnSkipWhite.setValue(0);
134 | updateKeyboardColours();
135 | break;
136 |
137 | case btnSkipWhite:
138 | btnSkipBlack.setValue(0);
139 | updateKeyboardColours();
140 | break;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/modules/MappingOffset.js:
--------------------------------------------------------------------------------
1 | // knbVelocity
2 | const knbVelocity = Content.addKnob("Velocity", 0, 0);
3 | knbVelocity.set("text", "Velocity");
4 | knbVelocity.setRange(0, 127, 1);
5 |
6 | // knbNoteOffset
7 | const knbNoteOffset = Content.addKnob("NoteOffset", 150, 0);
8 | knbNoteOffset.set("text", "Note Offset");
9 | knbNoteOffset.setRange(-127, 127, 1);function onNoteOn()
10 | {
11 | local n = Message.getNoteNumber();
12 |
13 | if (knbVelocity.getValue() > 0)
14 | Message.setVelocity(knbVelocity.getValue());
15 |
16 | if (knbNoteOffset.getValue() != 0)
17 | Message.setNoteNumber(n + knbNoteOffset.getValue());
18 | }
19 | function onNoteOff()
20 | {
21 |
22 | }
23 | function onController()
24 | {
25 |
26 | }
27 | function onTimer()
28 | {
29 |
30 | }
31 | function onControl(number, value)
32 | {
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/modules/MonoMode.js:
--------------------------------------------------------------------------------
1 | /*
2 | The MIT License (MIT)
3 |
4 | Copyright © 2017, 2018, 2019, 2020, 2021, 2022 David Healey
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this file (the “Software”), to deal in the Software without
8 | restriction, including without limitation the rights to use, copy,
9 | modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished
11 | to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included
14 | in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | Content.setWidth(650);
25 | Content.setHeight(50);
26 |
27 | reg lastNote = -1;
28 | reg retrigger = -1;
29 | reg eventId;
30 | reg lastTuning = 0;
31 |
32 | //GUI
33 | const var bypass = Content.addButton("Bypass", 10, 10);
34 |
35 | const var time = Content.addKnob("Time", 160, 0);
36 | time.setRange(0, 2000, 0.01);
37 |
38 | function onNoteOn()
39 | {
40 | if (!bypass.getValue())
41 | {
42 | if (lastNote == -1)
43 | {
44 | lastNote = Message.getNoteNumber();
45 | eventId = Message.makeArtificial();
46 | }
47 | else
48 | {
49 | if (time.getValue() > 0 && eventId != -1)
50 | {
51 | Message.ignoreEvent(true);
52 | Synth.addPitchFade(eventId, time.getValue(), lastTuning + Message.getNoteNumber()-lastNote, 0);
53 | lastTuning = lastTuning + Message.getNoteNumber()-lastNote;
54 | }
55 | else
56 | {
57 | if (eventId != -1) Synth.noteOffByEventId(eventId);
58 |
59 | eventId = Message.makeArtificial();
60 | }
61 | retrigger = lastNote;
62 | lastNote = Message.getNoteNumber();
63 | }
64 | }
65 | }
66 |
67 | function onNoteOff()
68 | {
69 | if (!bypass.getValue())
70 | {
71 | Message.ignoreEvent(true);
72 |
73 | if (eventId != -1 && Message.getNoteNumber() == lastNote)
74 | {
75 | if (Synth.isKeyDown(retrigger))
76 | {
77 | Synth.addPitchFade(eventId, time.getValue(), 0, 0);
78 | lastTuning = 0;
79 | lastNote = retrigger;
80 | retrigger = -1;
81 | }
82 | else
83 | {
84 | Synth.noteOffByEventId(eventId);
85 | eventId = -1;
86 | }
87 | }
88 |
89 | if (!Synth.getNumPressedKeys())
90 | {
91 | lastNote = -1;
92 | lastTuning = 0;
93 | }
94 | }
95 | else if (eventId != -1 && eventId != undefined)
96 | {
97 | Synth.noteOffByEventId(eventId);
98 | eventId = -1;
99 | lastNote = -1;
100 | lastTuning = 0;
101 | }
102 | }
103 |
104 | function onController()
105 | {
106 |
107 | }
108 | function onTimer()
109 | {
110 |
111 | }
112 | function onControl(number, value)
113 | {
114 |
115 | }
--------------------------------------------------------------------------------
/modules/MultiCCMapper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: multiCCMapper.js
3 | * Author: David Healey
4 | * Date: 30/06/2017
5 | * Modified: 30/06/2017
6 | * License: Public Domain
7 | */
8 | Content.setWidth(650);
9 | Content.setHeight(275);
10 |
11 | const var COUNT = 15; //The number of CCs you want to remap
12 |
13 | const var origin = Content.addSliderPack("sliOrigin", 0, 0);
14 | const var target = Content.addSliderPack("sliTarget", 260, 0);
15 | const var ccValue = Content.addSliderPack("sliValue", 520, 0);
16 |
17 | origin.set("sliderAmount", COUNT);
18 | target.set("sliderAmount", COUNT);
19 | ccValue.set("sliderAmount", COUNT);
20 | origin.set("min", 0);
21 | target.set("min", 0);
22 | ccValue.set("min", 0);
23 | origin.set("max", 127);
24 | target.set("max", 127);
25 | ccValue.set("max", 127);
26 | origin.set("stepSize", 1);
27 | target.set("stepSize", 1);
28 | ccValue.set("stepSize", 1);
29 | origin.set("width", 250);
30 | target.set("width", 250);
31 | ccValue.set("width", 250);
32 | origin.set("height", 200);
33 | target.set("height", 200);
34 | ccValue.set("height", 200);
35 | origin.set("flashActive", false);
36 | target.set("flashActive", false);
37 | ccValue.set("flashActive", false);
38 |
39 | const var lblOrigin = Content.addLabel("Origin", 105, 225);
40 | const var lblTarget = Content.addLabel("Target", 365, 225);
41 | const var lblValue = Content.addLabel("Value", 625, 225);
42 |
43 | function onNoteOn()
44 | {
45 | }
46 |
47 | function onNoteOff()
48 | {
49 | }
50 |
51 | function onController()
52 | {
53 | for (i = 0; i < origin.getNumSliders(); i++)
54 | {
55 | if (origin.getSliderValueAt(i) == 0) continue; //0 = not used
56 |
57 | if (Message.getControllerNumber() == origin.getSliderValueAt(i))
58 | {
59 | Message.ignoreEvent(true);
60 | if (target.getSliderValueAt(i) > 0)
61 | {
62 | Synth.sendController(target.getSliderValueAt(i), Message.getControllerValue());
63 | ccValue.setSliderAtIndex(i, Message.getControllerValue());
64 | }
65 | }
66 | }
67 | }
68 |
69 | function onTimer()
70 | {
71 | }
72 |
73 | function onControl(number, value)
74 | {
75 | if (number == ccValue)
76 | {
77 | if (target.getSliderValueAt(value) > 0)
78 | {
79 | Synth.sendController(target.getSliderValueAt(value), ccValue.getSliderValueAt(value));
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/modules/OctaveDoubler.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2019 David Healey
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 | */
22 |
23 | const var ids = Engine.createMidiList();
24 |
25 | ids.fill(false);function onNoteOn()
26 | {
27 | if (Message.getNoteNumber()+12 < 128)
28 | {
29 | if (ids.getValue(Message.getNoteNumber()) != false)
30 | {
31 | Synth.noteOffByEventId(ids.getValue(Message.getNoteNumber()));
32 | ids.setValue(Message.getNoteNumber(), false);
33 | }
34 |
35 | if (!Synth.isKeyDown(Message.getNoteNumber()+12))
36 | ids.setValue(Message.getNoteNumber()+12, Synth.playNote(Message.getNoteNumber()+12, Message.getVelocity()));
37 | }
38 | }
39 | function onNoteOff()
40 | {
41 | if (Message.getNoteNumber()+12 < 128)
42 | {
43 | if (ids.getValue(Message.getNoteNumber()+12) != false)
44 | {
45 | Synth.noteOffByEventId(ids.getValue(Message.getNoteNumber()+12));
46 | ids.setValue(Message.getNoteNumber()+12, false);
47 | }
48 | }
49 | }
50 | function onController()
51 | {
52 |
53 | }
54 | function onTimer()
55 | {
56 |
57 | }
58 | function onControl(number, value)
59 | {
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/modules/PortGlide.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: portGlide.js
3 | * Author: David Healey
4 | * Date: 30/06/2017
5 | * Modified: 03/12/2017
6 | * License: GPLv3 - https://www.gnu.org/licenses/gpl-3.0.en.html
7 | */
8 |
9 | reg note = -1;
10 | reg noteId = -1;
11 | reg origin = -1;
12 | reg target = -1; //Last note of the run
13 | reg velocity; //Velocity of the target note
14 | reg rate; //Tempo - taken from the host - to play the glide at.
15 | reg interval;
16 |
17 | //Get all child sample start constant modulators
18 | const var modulatorNames = Synth.getIdList("Constant"); //Get child constant modulator names
19 | const var startModulators = []; //For offsetting sample start position
20 |
21 | for (modName in modulatorNames)
22 | {
23 | if (Engine.matchesRegex(modName, "(?=.*ample)(?=.*tart)"))
24 | {
25 | startModulators.push(Synth.getModulator(modName));
26 | }
27 | }
28 |
29 | //GUI
30 | Content.setWidth(650);
31 | Content.setHeight(100);
32 |
33 | const var btnMute = Content.addButton("btnMute", 0, 10);
34 | btnMute.set("text", "Mute");
35 |
36 | const var knbOffset = Content.addKnob("knbOffset", 150, 0);
37 | knbOffset.setRange(0, 1, 0.01);
38 | knbOffset.set("text", "Offset");
39 |
40 | const var knbRate = Content.addKnob("knbRate", 300, 0);
41 | knbRate.set("mode", "TempoSync");
42 | knbRate.set("text", "Rate");
43 | knbRate.set("max", 11);
44 |
45 | const var btnWholeStep = Content.addButton("btnWholeStep", 450, 10);
46 | btnWholeStep.set("text", "Whole Step");
47 |
48 | const var knbMinInt = Content.addKnob("knbMinInt", 0, 50);
49 | knbMinInt.setRange(1, 24, 1);
50 | knbMinInt.set("text", "Min Interval");
51 | knbMinInt.set("suffix", "st");
52 |
53 | const var knbMaxInt = Content.addKnob("knbMaxInt", 150, 50);
54 | knbMaxInt.setRange(1, 36, 1);
55 | knbMaxInt.set("text", "Max Interval");
56 | knbMaxInt.set("suffix", "st");
57 |
58 | const var knbBendAmount = Content.addKnob("knbBendAmount", 300, 50);
59 | knbBendAmount.setRange(0, 100, 1);
60 | knbBendAmount.set("text", "Bend Amount");
61 | knbBendAmount.set("suffix", "Ct");
62 |
63 | inline function getRate(range, velocity)
64 | {
65 | reg rate = knbRate.getValue(); //Get rate knob value
66 |
67 | //If rate knob is set to the maximum then the actual rate will be determined by velocity
68 | if (rate == knbRate.get("max"))
69 | {
70 | rate = Math.floor((velocity / (knbRate.get("max") - 1)));
71 |
72 | if (rate > knbRate.get("max")) rate = max-1; //Cap rate at max rate
73 | }
74 |
75 | rate = (Engine.getMilliSecondsForTempo(rate) / 1000) / range; //Get rate based on host tempo and selected knob value
76 |
77 | if (rate < 0.04) rate = 0.04; //Cap lowest rate at timer's minimum
78 |
79 | return rate;
80 | }
81 |
82 | inline function setOffsetIntensity(value)
83 | {
84 | if (startModulators.length > 0)
85 | {
86 | for (mod in startModulators)
87 | {
88 | mod.setIntensity(value);
89 | }
90 | }
91 | }
92 |
93 | function onNoteOn()
94 | {
95 | if (!btnMute.getValue())
96 | {
97 | Synth.stopTimer();
98 |
99 | Message.ignoreEvent(true);
100 |
101 | if (origin != -1) interval = Math.abs(origin - Message.getNoteNumber()); //Calculate interval
102 |
103 | //In interval range or no note played yet
104 | if ((interval >= knbMinInt.getValue() && interval <= knbMaxInt.getValue()) || noteId == -1)
105 | {
106 | if (noteId != -1 && origin != -1)
107 | {
108 | setOffsetIntensity(knbOffset.getValue());
109 |
110 | note = origin;
111 | target = Message.getNoteNumber();
112 | velocity = Message.getVelocity();
113 |
114 | rate = getRate(Math.abs(origin - target), velocity);
115 |
116 | Synth.startTimer(rate);
117 | }
118 | else if (origin == -1) //Origin has not yet been played
119 | {
120 | setOffsetIntensity(1);
121 | origin = Message.getNoteNumber();
122 | noteId = Synth.playNote(origin, Message.getVelocity()); //Play first note
123 | }
124 | }
125 | }
126 | }
127 |
128 | function onNoteOff()
129 | {
130 | if (!btnMute.getValue())
131 | {
132 | if (Message.getNoteNumber() == origin || Message.getNoteNumber() == target)
133 | {
134 | Message.ignoreEvent(true);
135 |
136 | if (Message.getNoteNumber() == target) //Target released
137 | {
138 | Synth.stopTimer();
139 | if (noteId != -1) Synth.noteOffByEventId(noteId);
140 | origin = -1;
141 | noteId = -1;
142 | target = -1;
143 | }
144 |
145 | if (!Synth.isKeyDown(origin) && !Synth.isKeyDown(target) && noteId != -1) //Both keys have been lifted
146 | {
147 | Synth.stopTimer();
148 | Synth.noteOffByEventId(noteId);
149 | noteId = -1;
150 | origin = -1;
151 | target = -1;
152 | }
153 | }
154 | }
155 | else
156 | {
157 | //Turn off hanging note, if any
158 | if (noteId != -1)
159 | {
160 | Synth.noteOffByEventId(noteId);
161 | noteId = -1;
162 | origin = -1;
163 | target = -1;
164 | setOffsetIntensity(1); //Reset sample start offset
165 | }
166 | }
167 | }
168 |
169 | function onController()
170 | {
171 | }
172 |
173 | function onTimer()
174 | {
175 | if (!btnMute.getValue())
176 | {
177 | target > origin ? note++ : note--; //Increment/decrement the note number by 1 (a half step)
178 |
179 | //If the whole step button is enabled then increment/decrement the note by another half step
180 | if (btnWholeStep.getValue())
181 | {
182 | target > origin ? note++ : note--;
183 | }
184 |
185 | if (noteId != -1 && origin != -1 && ((target > origin && note <= target) || (target < origin && note >= target)))
186 | {
187 | bendAmount = knbBendAmount.getValue();
188 |
189 | if (origin > target) bendAmount = -bendAmount;
190 |
191 | Synth.addVolumeFade(noteId, rate*1000, -100); //Fade out old note
192 | Synth.addPitchFade(noteId, rate*1000, 0, bendAmount); //Pitch fade old note to bend amount
193 |
194 | noteId = Synth.playNote(note, velocity); //Play new note and store its ID
195 |
196 | Synth.addVolumeFade(noteId, 0, -99); //Set new note's initial volume
197 | Synth.addVolumeFade(noteId, rate*1000, 0); //Fade in new note
198 | Synth.addPitchFade(noteId, 0, 0, -bendAmount); //Set new note's initial detuning
199 | Synth.addPitchFade(noteId, rate*1000, 0, 0); //Pitch fade new note to 0
200 |
201 | }
202 | else
203 | {
204 | origin = target;
205 | note = target;
206 | Synth.stopTimer();
207 | }
208 | }
209 | }
210 |
211 | function onControl(number, value)
212 | {
213 | switch (number)
214 | {
215 | case btnMute:
216 |
217 | origin = -1;
218 | target = -1;
219 |
220 | Synth.stopTimer();
221 | setOffsetIntensity(1);
222 | break;
223 |
224 | case knbRate:
225 | //If timer's already started then update its Rate
226 | if (Synth.isTimerRunning() == 1)
227 | {
228 | rate = getRate(Math.abs(origin - target), velocity);
229 | Synth.startTimer(rate);
230 | }
231 |
232 | knbRate.set("text", "Rate"); //Default
233 | if (knbRate.getValue() == knbRate.get("max"))
234 | {
235 | knbRate.set("text", "Velocity");
236 | }
237 | break;
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/modules/RoundRobinBypassHandler.js:
--------------------------------------------------------------------------------
1 | const var scriptIds = Synth.getIdList("Script Processor");
2 | const var rrScripts = [];
3 |
4 | for (id in scriptIds)
5 | {
6 | if (id.indexOf("roundRobin") != -1)
7 | rrScripts.push(Synth.getMidiProcessor(id));
8 | }
9 |
10 | const var btnBypass = Content.addButton("btnBypass", 10, 10);
11 | btnBypass.set("text", "Bypass");
12 | btnBypass.setControlCallback(onbtnBypassControl);
13 |
14 | inline function onbtnBypassControl(component, value)
15 | {
16 | for (i = 0; i < rrScripts.length; i++)
17 | {
18 | rrScripts[i].setAttribute(rrScripts[i].btnBypass, 1-value);
19 | }
20 | }function onNoteOn()
21 | {
22 |
23 | }
24 | function onNoteOff()
25 | {
26 |
27 | }
28 | function onController()
29 | {
30 |
31 | }
32 | function onTimer()
33 | {
34 |
35 | }
36 | function onControl(number, value)
37 | {
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/modules/SamplerActivator.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 David Healey
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
5 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
6 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
13 | IN THE SOFTWARE.
14 | */
15 | Content.setHeight(100);
16 |
17 | const var UACC = 32;
18 |
19 | const var btnActive = Content.addButton("btnActive", 10, 10);
20 | btnActive.set("text", "Active");
21 | btnActive.set("defaultValue", 1);
22 |
23 | const var knbKs = Content.addKnob("knbKs", 160, 0);
24 | knbKs.setRange(-1, 127, 1);
25 | knbKs.set("text", "KS");
26 |
27 | const var knbProgram = Content.addKnob("knbProgram", 310, 0);
28 | knbProgram.setRange(-1, 127, 1);
29 | knbProgram.set("text", "Program");
30 |
31 | const var btnToggle = Content.addButton("btnToggle", 460, 10);
32 | btnToggle.set("text", "Toggle");
33 |
34 | const var knbLoKs = Content.addKnob("knbLoKs", 0, 45);
35 | knbLoKs.setRange(0, 127, 1);
36 | knbLoKs.set("text", "Lowest Ks");
37 |
38 | const var knbHiKs = Content.addKnob("knbHiKs", 160, 45);
39 | knbHiKs.setRange(0, 127, 1);
40 | knbHiKs.set("text", "Highest Ks");function onNoteOn()
41 | {
42 | if (!btnActive.getValue())
43 | Message.ignoreEvent(true);
44 |
45 | if (knbKs.getValue() != -1 && Message.getNoteNumber() == knbKs.getValue())
46 | {
47 | if (!btnToggle.getValue() || Message.getVelocity() > 64)
48 | btnActive.setValue(1);
49 | else
50 | btnActive.setValue(0);
51 | }
52 | else if (Message.getNoteNumber() >= knbLoKs.getValue() && Message.getNoteNumber() <= knbHiKs.getValue())
53 | btnActive.setValue(0);
54 | }
55 | function onNoteOff()
56 | {
57 |
58 | }
59 | function onController()
60 | {
61 | if (knbProgram.getValue() != -1 && (Message.getControllerNumber() == UACC || Message.isProgramChange()))
62 | {
63 | local v;
64 |
65 | Message.getControllerNumber() == UACC ? v = Message.getControllerValue() : v = Message.getProgramChangeNumber();
66 |
67 | if (v == knbProgram.getValue())
68 | btnActive.setValue(1);
69 | else
70 | btnActive.setValue(0);
71 | }
72 | }
73 | function onTimer()
74 | {
75 |
76 | }
77 | function onControl(number, value)
78 | {
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/modules/SimpleKeyswitcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: Simple Keyswitcher.js
3 | * Author: David Healey
4 | * Date: 11/10/2020
5 | * License: Public Domain (CC0)
6 | */
7 |
8 | //Get first sampler
9 | const var synthIds = Synth.getIdList("Sampler");
10 | const var sampler = Synth.getSampler(synthIds[0])
11 |
12 | //Disable default round robin
13 | sampler.enableRoundRobin(false);
14 |
15 | reg ks = [];
16 | reg currentGroup = 1;
17 |
18 | //GUI
19 | const var Groups = Content.addKnob("Groups", 0, 0);
20 | Groups.setRange(0, 50, 1);
21 | Groups.set("text", "Num Groups");
22 | Groups.setControlCallback(update);
23 |
24 | const var FirstKs = Content.addKnob("FirstKs", 150, 0);
25 | FirstKs.setRange(0, 127, 1);
26 | FirstKs.set("defaultValue", 24);
27 | FirstKs.set("text", "First KS");
28 | FirstKs.setControlCallback(update);
29 |
30 | inline function update(component, value)
31 | {
32 | //Clear old keyswitches
33 | for (i = 0; i < ks.length; i++)
34 | Engine.setKeyColour(ks[i], Colours.transparentWhite);
35 |
36 | //Get new keyswitches
37 | ks = [];
38 | for (i = 0; i < Groups.getValue(); i++)
39 | {
40 | ks[i] = FirstKs.getValue() + i;
41 | Engine.setKeyColour(ks[i], Colours.withAlpha(Colours.red, 0.3));
42 | }
43 | };
44 | function onNoteOn()
45 | {
46 | local n = Message.getNoteNumber();
47 |
48 | if (ks.contains(n))
49 | currentGroup = ks.indexOf(n) + 1;
50 |
51 | sampler.setActiveGroup(currentGroup);
52 | }
53 | function onNoteOff()
54 | {
55 |
56 | }
57 | function onController()
58 | {
59 |
60 | }
61 | function onTimer()
62 | {
63 |
64 | }
65 | function onControl(number, value)
66 | {
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/modules/SyntheticRelease.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: syntheticRelease.js
3 | * Author: David Healey
4 | * Date: 07/09/2017
5 | * Modified: 07/09/2017
6 | * License: Public Domain
7 | */
8 |
9 | //INIT
10 | Content.setWidth(650);
11 | Content.setHeight(50);
12 |
13 | const var envelopeNames = Synth.getIdList("Table Envelope"); //Get all child table envelopes
14 | reg pitchEnvelope;
15 |
16 | //Get pitch envelope
17 | for (envelope in envelopeNames)
18 | {
19 | if (Engine.matchesRegex(envelope, "(?=.*itch)(?=.*elease)") == true) //Pitch Release envelope
20 | {
21 | pitchEnvelope = Synth.getModulator(envelope);
22 | }
23 | }
24 |
25 | const var knbPitch = Content.addKnob("Pitch", 0, 0);
26 | knbPitch.setRange(-1, 1, 0.01);
27 |
28 | const var knbReleaseTime = Content.addKnob("Release Time", 150, 0);
29 | knbReleaseTime.setRange(1, 50, 0.01);
30 |
31 | const var btnLegato = Content.addButton("Legato", 300, 10);
32 |
33 | //FUNCTIONS
34 |
35 | //CALLBACKS
36 | function onNoteOn()
37 | {
38 | }
39 |
40 | function onNoteOff()
41 | {
42 | if (btnLegato.getValue() == 1)
43 | {
44 | if (Globals.keyCount == 0) //All keys up
45 | {
46 | pitchEnvelope.setIntensity(knbPitch.getValue()); //Set pitch adjustment
47 | }
48 | else
49 | {
50 | pitchEnvelope.setIntensity(0); //No pitch adjustment
51 | }
52 | }
53 | }
54 |
55 | function onController()
56 | {
57 | }
58 |
59 | function onTimer()
60 | {
61 | }
62 |
63 | function onControl(number, value)
64 | {
65 | switch (number)
66 | {
67 | case knbPitch:
68 | pitchEnvelope.setIntensity(value);
69 | break;
70 |
71 | case knbReleaseTime:
72 | pitchEnvelope.setAttribute(1, value);
73 | break;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/modules/TrillMaker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: trillMaker.js
3 | * Author: David Healey
4 | * Date: 07/09/2017
5 | * Modified: 07/09/2017
6 | * License: Public Domain
7 | */
8 |
9 | reg velocity; //Velocity of the notes[1] note
10 | reg count; //Note number to be played - in the range of the and notes[1]
11 | reg rate; //Tempo - taken from the host - to play the glide at.
12 | reg interval;
13 | reg notes = [-1, -1];
14 | reg noteId = -1;
15 | reg stopTimerFlag = 0;
16 | reg microTuning;
17 |
18 | //Get all child sample start constant modulators
19 | const var modulatorNames = Synth.getIdList("Constant"); //Get child constant modulator names
20 | const var startModulators = []; //For offsetting sample start position
21 |
22 | for (modName in modulatorNames)
23 | {
24 | if (Engine.matchesRegex(modName, "(?=.*ample)(?=.*tart)"))
25 | {
26 | startModulators.push(Synth.getModulator(modName));
27 | }
28 | }
29 |
30 | //GUI
31 | Content.setWidth(650);
32 | Content.setHeight(100);
33 |
34 | const var btnMute = Content.addButton("btnMute", 0, 10);
35 | btnMute.set("text", "Mute");
36 |
37 | const var knbOffset = Content.addKnob("knbOffset", 150, 0);
38 | knbOffset.setRange(0, 1, 0.01);
39 | knbOffset.set("text", "Offset");
40 |
41 | const var knbRate = Content.addKnob("knbRate", 300, 0);
42 | knbRate.set("mode", "TempoSync");
43 | knbRate.set("text", "Rate");
44 |
45 | const var knbMinInt = Content.addKnob("knbMinInt", 0, 50);
46 | knbMinInt.setRange(1, 11, 1);
47 | knbMinInt.set("text", "Min Interval");
48 | knbMinInt.set("suffix", "st");
49 |
50 | const var knbMaxInt = Content.addKnob("knbMaxInt", 150, 50);
51 | knbMaxInt.setRange(1, 12, 1);
52 | knbMaxInt.set("text", "Max Interval");
53 | knbMaxInt.set("suffix", "st");
54 |
55 | const var knbMinBendAmount = Content.addKnob("knbMinBendAmount", 300, 50);
56 | knbMinBendAmount.setRange(0, 100, 1);
57 | knbMinBendAmount.set("text", "Min Bend");
58 | knbMinBendAmount.set("suffix", "Ct");
59 |
60 | const var knbMaxBendAmount = Content.addKnob("knbMaxBendAmount", 450, 50);
61 | knbMaxBendAmount.setRange(1, 100, 1);
62 | knbMaxBendAmount.set("text", "Max Bend");
63 | knbMaxBendAmount.set("suffix", "Ct");
64 |
65 | //Microtuning knob
66 | const var knbMicroTuning = Content.addKnob("Microtuning", 600, 50);
67 | knbMicroTuning.setRange(0, 100, 0.1);
68 |
69 | inline function getRate()
70 | {
71 | rate = Engine.getMilliSecondsForTempo(knbRate.getValue()) / 1000;
72 | if (rate < 0.04) rate = 0.04;
73 | return rate;
74 | }
75 |
76 | inline function calculateBendAmount(lastNote, noteNumber)
77 | {
78 | local bendAmount;
79 | reg interval = Math.abs(lastNote - noteNumber);
80 |
81 | bendAmount = (((interval - 1) * (knbMaxBendAmount.getValue() - knbMinBendAmount.getValue())) / (12 - 1)) + knbMinBendAmount.getValue();
82 |
83 | if (lastNote > noteNumber) bendAmount = -bendAmount; //Down interval - invert the bend amount
84 |
85 | return bendAmount;
86 | }
87 |
88 | inline function setOffsetIntensity(value)
89 | {
90 | if (startModulators.length > 0)
91 | {
92 | for (mod in startModulators)
93 | {
94 | mod.setIntensity(value);
95 | }
96 | }
97 | }
98 |
99 | function onNoteOn()
100 | {
101 | if (!btnMute.getValue())
102 | {
103 | Synth.stopTimer();
104 |
105 | Message.ignoreEvent(true);
106 |
107 | if (notes[0] != -1) interval = Math.abs(notes[0] - Message.getNoteNumber()); //Calculate interval
108 |
109 | //In interval range or no note played yet
110 | if ((interval >= knbMinInt.getValue() && interval <= knbMaxInt.getValue()) || noteId == -1)
111 | {
112 | if (noteId != -1 && notes[0] != -1)
113 | {
114 | setOffsetIntensity(knbOffset.getValue());
115 |
116 | count = 0;
117 | notes[1] = Message.getNoteNumber();
118 | velocity = Message.getVelocity();
119 |
120 | bendAmount = calculateBendAmount(notes[0], notes[1]);
121 | rate = getRate();
122 |
123 | Synth.startTimer(rate);
124 | }
125 | else if (notes[0] == -1) //notes[0] has not yet been played - first note
126 | {
127 | stopTimerFlag = 0;
128 | setOffsetIntensity(1);
129 | notes[0] = Message.getNoteNumber();
130 | noteId = Synth.playNote(notes[0], Message.getVelocity()); //Play first note
131 | }
132 | }
133 | }
134 | }
135 |
136 | function onNoteOff()
137 | {
138 | if (!btnMute.getValue())
139 | {
140 | if (Message.getNoteNumber() == notes[0] || Message.getNoteNumber() == notes[1])
141 | {
142 | Message.ignoreEvent(true);
143 |
144 | if (Message.getNoteNumber() == notes[0]) //notes[0] released
145 | {
146 | notes[0] = -1;
147 |
148 | if (Synth.isKeyDown(notes[1])) //notes[1] is still held
149 | {
150 | if (count == 0)
151 | {
152 | stopTimerFlag = 1; //Timer will be stopped after next cycle so that notes[1] is sounding note
153 | }
154 | else
155 | {
156 | Synth.stopTimer();
157 | }
158 |
159 | notes[0] = notes[1]; //notes[1] becomes the new notes[0]
160 | }
161 | }
162 |
163 | if (Message.getNoteNumber() == notes[1]) //notes[1] released
164 | {
165 | notes[1] = -1;
166 |
167 | if (Synth.isKeyDown(notes[0])) //notes[0] is still held
168 | {
169 | if (count == 1)
170 | {
171 | stopTimerFlag = 1; //Timer will be stopped after next cycle so that notes[0] is sounding note
172 | }
173 | else
174 | {
175 | Synth.stopTimer();
176 | }
177 | }
178 | }
179 |
180 | if (!Synth.isKeyDown(notes[0]) && !Synth.isKeyDown(notes[1]) && noteId != -1) //Both keys have been lifted
181 | {
182 | Synth.stopTimer();
183 | Synth.noteOffByEventId(noteId);
184 | notes = [-1, -1];
185 | noteId = -1;
186 | setOffsetIntensity(1); //Reset sample start offset
187 | }
188 | }
189 | }
190 | else
191 | {
192 | //Turn off hanging note, if any
193 | if (noteId != -1)
194 | {
195 | Synth.noteOffByEventId(noteId);
196 | notes = [-1, -1];
197 | noteId = -1;
198 | setOffsetIntensity(1); //Reset sample start offset
199 | }
200 | }
201 | }
202 |
203 | function onController()
204 | {
205 | }
206 |
207 | function onTimer()
208 | {
209 | if (!btnMute.getValue())
210 | {
211 | count = 1-count; //Toggle count - to switch between origin and target notes
212 |
213 | //Get random microtuning, if any
214 | microTuning = 0;
215 | if (knbMicroTuning.getValue() > 0)
216 | {
217 | microTuning = (Math.random() * (knbMicroTuning.getValue() - -knbMicroTuning.getValue() + 1) + -knbMicroTuning.getValue());
218 | }
219 |
220 | Synth.addPitchFade(noteId, rate*1000, 0, bendAmount + microTuning); //Pitch fade old note to bend amount
221 | Synth.addVolumeFade(noteId, rate*1000, -100); //Fade out last note
222 |
223 | noteId = Synth.playNote(notes[count], velocity); //Play new note
224 |
225 | Synth.addVolumeFade(noteId, 0, -99); //Set new note's initial volume
226 | Synth.addVolumeFade(noteId, rate*1000, 0); //Fade in new note
227 | Synth.addPitchFade(noteId, 0, 0, -bendAmount + microTuning); //Set new note's initial detuning
228 | Synth.addPitchFade(noteId, rate*1000, 0, microTuning); //Pitch fade new note to 0
229 |
230 | if (stopTimerFlag == 1 || noteId == -1)
231 | {
232 | Synth.stopTimer();
233 | stopTimerFlag = 0;
234 | }
235 | }
236 | }
237 |
238 | function onControl(number, value)
239 | {
240 | switch (number)
241 | {
242 | case btnMute:
243 | notes = [-1, -1];
244 | Synth.stopTimer();
245 | setOffsetIntensity(1);
246 | break;
247 |
248 | case knbRate:
249 | //If timer's already started then update its Rate
250 | if (Synth.isTimerRunning())
251 | {
252 | rate = getRate();
253 | Synth.startTimer(rate);
254 | }
255 |
256 | knbRate.set("text", "Rate"); //Default
257 | if (knbRate.getValue() == knbRate.get("max"))
258 | {
259 | knbRate.set("text", "Velocity");
260 | }
261 | break;
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/modules/VelocityFilter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: velocityFilter.js
3 | * Author: David Healey
4 | * Date: 07/09/2017
5 | * License: Public Domain
6 | */
7 |
8 | Content.setWidth(650);
9 | Content.setHeight(50);
10 |
11 | const var min = Content.addKnob("Min", 0, 0);
12 | min.setRange(1, 127, 1);
13 | min.set("defaultValue", 1);
14 |
15 | const var max = Content.addKnob("Max", 150, 0);
16 | max.setRange(1, 127, 1);
17 | max.set("defaultValue", 127);function onNoteOn()
18 | {
19 | if (Message.getVelocity() < min.getValue() || Message.getVelocity() > max.getValue())
20 | {
21 | Message.ignoreEvent(true);
22 | }
23 | }
24 | function onNoteOff()
25 | {
26 |
27 | }
28 | function onController()
29 | {
30 |
31 | }
32 | function onTimer()
33 | {
34 |
35 | }
36 | function onControl(number, value)
37 | {
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/modules/VelocityMappedRoundRobin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: velocityMappedRoundRobin.js
3 | * Author: David Healey
4 | * Date: 31/10/2017
5 | * Modified: 31/10/2017
6 | * License: Public Domain
7 | */
8 |
9 | /*Instructions:
10 | Map each round robin sample to a single velocity range i.e 0-1, 2-3, 4-5.
11 | Always start with 0-1 for RR1 and work up from there.
12 | */
13 |
14 | reg rrStep = -1;
15 |
16 | Content.setWidth(650);
17 | Content.setHeight(50);
18 |
19 | //Knob to lock to a specific RR - essentially disabling the round robin functionality - useful for finding duff samples
20 | const var knbRRLock = Content.addKnob("knbRRLock", 0, 0);
21 | knbRRLock.set("text", "Lock to RR");
22 | knbRRLock.setRange(0, 50, 1);
23 |
24 | const var btnRandom = Content.addButton("btnRandom", 150, 10);
25 | btnRandom.set("text", "Random");
26 |
27 | const var knbNumRR = Content.addKnob("knbNumRR", 300, 0);
28 | knbNumRR.set("text", "Num RR");
29 | knbNumRR.setRange(1, 50, 1);
30 |
31 | function onNoteOn()
32 | {
33 | if (knbRRLock.getValue() == 0) //RR not locked
34 | {
35 | if (!btnRandom.getValue())
36 | {
37 | rrStep = rrStep + 2; //Each RR covers a velocity range of 2 (0-1, 2-3, 4-5, etc.)
38 | if (rrStep > knbNumRR.getValue()*2-1) rrStep = 1;
39 | }
40 | else
41 | {
42 | rrStep = Math.randInt(1, knbNumRR.getValue()+1);
43 | rrStep = rrStep * 2 - 1;
44 | }
45 | }
46 | else
47 | {
48 | rrStep = knbRRLock.getValue() * 2 - 1;
49 | }
50 |
51 | Message.setVelocity(rrStep);
52 | }
53 | function onNoteOff()
54 | {
55 |
56 | }
57 | function onController()
58 | {
59 |
60 | }
61 | function onTimer()
62 | {
63 |
64 | }
65 | function onControl(number, value)
66 | {
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/modules/VelocityScaler.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018, 2023 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | Content.setWidth(650);
19 | Content.setHeight(200);
20 |
21 | const var tblVelocity = Content.addTable("Velocity", 10, 10);
22 | tblVelocity.set("width", 180);
23 | tblVelocity.set("height", 180);function onNoteOn()
24 | {
25 | local oldVelocity = Message.getVelocity();
26 | local newVelocity = 1 + Math.floor(126 * tblVelocity.getTableValue(oldVelocity / 127));
27 |
28 | Message.setVelocity(newVelocity);
29 | }
30 | function onNoteOff()
31 | {
32 |
33 | }
34 | function onController()
35 | {
36 |
37 | }
38 | function onTimer()
39 | {
40 |
41 | }
42 | function onControl(number, value)
43 | {
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/modules/VelocityShaper.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 David Healey
3 |
4 | This file is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This file is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this file. If not, see .
16 | */
17 |
18 | Content.setWidth(600);
19 | Content.setHeight(100);
20 |
21 | const var dynamicsCC = Synth.getModulator("dynamicsCC");
22 |
23 | const var btnDynamics = Content.addButton("btnDynamics", 0, 10);
24 | btnDynamics.set("text", "Vel = Dynamics");
25 |
26 | const tblVel = Content.addTable("tblVel", 150, 0);
27 | tblVel.set("height", 95);
28 | tblVel.set("width", 200);
29 | tblVel.connectToOtherTable("Velocity Modulator", 0);function onNoteOn()
30 | {
31 | Message.setVelocity(Math.max(1, 127 * tblVel.getTableValue(Message.getVelocity()))); //Scale velocity using table
32 |
33 | //Send velocity to dynamics CC if button enabled
34 | if (btnDynamics.getValue())
35 | {
36 | dynamicsCC.setAttribute(dynamicsCC.DefaultValue, Message.getVelocity());
37 | }
38 | }
39 | function onNoteOff()
40 | {
41 |
42 | }
43 | function onController()
44 | {
45 |
46 | }
47 | function onTimer()
48 | {
49 |
50 | }
51 | function onControl(number, value)
52 | {
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/modules/VelocityToCC.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Title: velocityToCC.js
3 | * Author: David Healey
4 | * Date: 15/03/2018
5 | * Modified: 15/03/2018
6 | * License: Public Domain
7 | */
8 |
9 | Content.setWidth(650);
10 | Content.setHeight(50);
11 |
12 | const var btnBypass = Content.addButton("btnBypass", 0, 10);
13 | btnBypass.set("text", "Bypass");
14 |
15 | const var knbCC = Content.addKnob("KnbCC", 160, 0);
16 | knbCC.set("text", "CC Number");
17 | knbCC.setRange(1, 127, 1);function onNoteOn()
18 | {
19 | if (btnBypass.getValue() == 0)
20 | {
21 | Synth.sendController(knbCC.getValue(), Message.getVelocity());
22 | }
23 | }
24 |
25 | function onNoteOff()
26 | {
27 |
28 | }
29 | function onController()
30 | {
31 |
32 | }
33 | function onTimer()
34 | {
35 |
36 | }
37 | function onControl(number, value)
38 | {
39 |
40 | }
41 |
--------------------------------------------------------------------------------