-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathOVLP.ino
457 lines (399 loc) · 16.9 KB
/
OVLP.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* OVLP: Online Video Looper Pedal
* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* AUTHOR Andreas Brett
* URL https://github.com/andreasbrett/OVLP
* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* - for tweaking
* - the pins that you used on your Arduino Micro
* - the keyboard shortcuts the pedal will send via USB
* - go to section "define buttons (pins and shortcuts)"
* - for tweaking the timings for a short vs long press go to section "global variables"
* - please consider not modifying the rest of this script unless you know what you're doing
* --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
/* ----------------------------------------------------------------------------------------------
* include keyboard and MIDI library
* ----------------------------------------------------------------------------------------------
*/
#include <Keyboard.h>
#include <USB-MIDI.h>
USBMIDI_CREATE_DEFAULT_INSTANCE();
/* ----------------------------------------------------------------------------------------------
* global variables
* ----------------------------------------------------------------------------------------------
*/
unsigned long shortPressTime = 30; // how long will a switch need to be pressed to be considered a short press? [in milliseconds]
unsigned long longPressTime = 450; // how long will a switch need to be pressed to be considered a long press? [in milliseconds]
bool logMessages = false; // show log messages via serial connection?
byte midiChannel = 16; // MIDI channel to use
/* ----------------------------------------------------------------------------------------------
* log message via serial connection (for debugging)
* ----------------------------------------------------------------------------------------------
*/
void logMessage(String s, bool bEOL = true) {
if (logMessages == true) {
Serial.print(s);
if (bEOL == true) {
Serial.println("");
}
}
}
/* ----------------------------------------------------------------------------------------------
* CLASS: KbdShortcut
* ----------------------------------------------------------------------------------------------
*/
class KbdShortcut {
private:
String shortcut = "<undef>"; // initial shortcut
bool ctrlKey = false; // press Ctrl key?
bool shiftKey = false; // press Shift key?
bool altKey = false; // press Alt key?
byte midiType = 255; // type of MIDI message (1 = Note / 2 = CC / 3 = PC)
byte midiNumber = 255; // MIDI number to send
byte midiValue = 255; // MIDI value to send
char character = ' '; // character to press
byte delay1 = 5; // delay in msec before pressing character
byte delay2 = 80; // delay in msec before releasing character
byte delay3 = 40; // delay in msec before releasing modifiers
byte delay4 = 100; // delay in msec after sending a shortcut
public:
/* ----------------------------------------------------------------------------------------------
* constructor
* ----------------------------------------------------------------------------------------------
*/
void init(String IN_shortcut) {
shortcut = IN_shortcut;
String s = "";
char pos1 = 0;
char pos2 = -1;
// ---------------------------------------------------------
// MIDI message (Note / CC / PC)
// ---------------------------------------------------------
if (shortcut.startsWith("MIDI")) {
pos2 = shortcut.indexOf("#");
s = shortcut.substring(pos1, pos2);
s.toLowerCase();
// MIDI-PC
if (s == "midi-pc") {
midiType = 3;
pos1 = pos2+1;
pos2 = shortcut.indexOf("#", pos1);
s = shortcut.substring(pos1, pos2);
midiNumber = s.toInt();
} else {
// MIDI-Note
if (s == "midi-note") {
midiType = 1;
}
// MIDI-CC
else if (s == "midi-cc") {
midiType = 2;
}
// fetch note/command
pos1 = pos2+1;
pos2 = shortcut.indexOf("#", pos1);
s = shortcut.substring(pos1, pos2);
midiNumber = s.toInt();
// fetch note/command value
pos1 = pos2+1;
pos2 = shortcut.indexOf("#", pos1);
s = shortcut.substring(pos1, pos2);
midiValue = s.toInt();
}
}
// ---------------------------------------------------------
// keyboard shortcut
// ---------------------------------------------------------
else {
// go through each key in shortcut (delimiter: "#")
do {
pos2 = shortcut.indexOf("#", pos1);
if (pos2 == -1) {
pos2 = shortcut.length();
}
s = shortcut.substring(pos1, pos2);
s.toLowerCase();
if (s == "ctrl") {
ctrlKey = true;
} else if (s == "shift") {
shiftKey = true;
} else if (s == "alt") {
altKey = true;
} else {
character = s.charAt(0);
}
pos1 = pos2+1;
} while(pos1 <= shortcut.length());
// remove delay1 and delay3 if no modifiers are set
if (ctrlKey == false && shiftKey == false && altKey == false) {
delay1 = 0;
delay3 = 0;
}
}
}
/* ----------------------------------------------------------------------------------------------
* return string representation of keyboard shortcut
* ----------------------------------------------------------------------------------------------
*/
String getShortcut() {
return shortcut;
}
/* ----------------------------------------------------------------------------------------------
* send keyboard shortcut
* ----------------------------------------------------------------------------------------------
*/
void send() {
// ---------------------------------------------------------
// MIDI message (Note / CC / PC)
// ---------------------------------------------------------
if (midiType < 255) {
// MIDI Note
if (midiType == 1) {
MIDI.sendNoteOn(midiNumber, midiValue, midiChannel);
delay(delay2);
MIDI.sendNoteOff(midiNumber, 0, midiChannel);
}
// MIDI Control Change
else if (midiType == 2) {
MIDI.sendControlChange(midiNumber, midiValue, midiChannel);
delay(delay2);
MIDI.sendControlChange(midiNumber, 0, midiChannel);
}
// MIDI Program Change
else if (midiType == 3) {
MIDI.sendProgramChange(midiNumber, midiChannel);
}
delay(delay4);
}
// ---------------------------------------------------------
// keyboard shortcut
// ---------------------------------------------------------
else {
// check if character is set
if (character != ' ') {
// press Ctrl if necessary
if (ctrlKey) {
Keyboard.press(KEY_LEFT_CTRL);
}
// press Shift if necessary
if (shiftKey) {
Keyboard.press(KEY_LEFT_SHIFT);
}
// press Alt if necessary
if (altKey) {
Keyboard.press(KEY_LEFT_ALT);
}
// press character
delay(delay1);
Keyboard.press(character);
// release character
delay(delay2);
Keyboard.release(character);
// release modifiers
delay(delay3);
Keyboard.releaseAll();
delay(delay4);
}
}
}
};
/* ----------------------------------------------------------------------------------------------
* CLASS: Button
* ----------------------------------------------------------------------------------------------
*/
class Button {
private:
byte pin_button; // button's assigned pin
byte pin_bank; // bank switch' assigned pin
byte currentState = 0; // button's current state
byte previousState = 0; // button's previous state
unsigned long startPressed = 0; // point of time button has been pressed
unsigned long endPressed = 0; // point of time button has been released
unsigned long holdTime = 0; // time button has been pressed for
KbdShortcut shortcut1_short; // keyboard shortcut for short press (bank #1)
KbdShortcut shortcut1_long; // keyboard shortcut for long press (bank #1)
KbdShortcut shortcut2_short; // keyboard shortcut for short press (bank #2)
KbdShortcut shortcut2_long; // keyboard shortcut for long press (bank #2)
public:
/* ----------------------------------------------------------------------------------------------
* constructor
* ----------------------------------------------------------------------------------------------
*/
void init(int IN_pin_button, int IN_pin_bank, String IN_short1, String IN_long1, String IN_short2, String IN_long2) {
// set pin mode
pinMode(IN_pin_button, INPUT);
pinMode(IN_pin_bank, INPUT);
// define shortcuts
shortcut1_short.init(IN_short1);
shortcut1_long.init(IN_long1);
shortcut2_short.init(IN_short2);
shortcut2_long.init(IN_long2);
// store pins
pin_button = IN_pin_button;
pin_bank = IN_pin_bank;
logMessage("Initialized button on pin ", false);
logMessage(String(pin_button));
}
/* ----------------------------------------------------------------------------------------------
* check if button changed state
* ----------------------------------------------------------------------------------------------
*/
void check() {
currentState = digitalRead(pin_button);
if (currentState != previousState) {
react();
}
previousState = currentState;
}
/* ----------------------------------------------------------------------------------------------
* react upon short/long press
* ----------------------------------------------------------------------------------------------
*/
void react() {
// button pressed
if (currentState == HIGH) {
startPressed = millis();
logMessage("");
logMessage("Button press detected...");
// button released
} else {
endPressed = millis();
holdTime = endPressed - startPressed;
logMessage(" - hold time = ", false);
logMessage(String(holdTime), false);
logMessage("ms");
// bounced
if (holdTime < shortPressTime) {
logMessage(" -> bounced. ignoring.");
}
// short-press
if (holdTime >= shortPressTime && holdTime < longPressTime) {
if (digitalRead(pin_bank) == LOW) {
logMessage(" - bank = 1");
logMessage(" - type = short");
logMessage(" - sending ", false);
logMessage(shortcut1_short.getShortcut());
shortcut1_short.send();
} else {
logMessage(" - bank = 2");
logMessage(" - type = short");
logMessage(" - sending ", false);
logMessage(shortcut2_short.getShortcut());
shortcut2_short.send();
}
}
// long-press
if (holdTime >= longPressTime) {
if (digitalRead(pin_bank) == LOW) {
logMessage(" - bank = 1");
logMessage(" - type = long");
logMessage(" - sending ", false);
logMessage(shortcut1_long.getShortcut());
shortcut1_long.send();
} else {
logMessage(" - bank = 2");
logMessage(" - type = long");
logMessage(" - sending ", false);
logMessage(shortcut2_long.getShortcut());
shortcut2_long.send();
}
}
}
}
};
// button objects
Button btnPlayPause;
Button btnLoop;
Button btnBack;
Button btnFullscreen;
Button btnForward;
Button btnSpeed;
/* ----------------------------------------------------------------------------------------------
* setup
* ----------------------------------------------------------------------------------------------
*/
void setup() {
// initialize serial communication (wait for serial stream to open)
if (logMessages == true) {
Serial.begin(9600);
while (!Serial) ;
logMessage("---------------------------------------------");
logMessage("Serial interface initialized.");
}
// start USB HID interface
logMessage("Initializing USB HID interface...");
Keyboard.begin();
// start MIDI interface
logMessage("Initializing USB MIDI interface...");
MIDI.begin(midiChannel);
/* ----------------------------------------------------------------------------------------------
* define buttons (pins and shortcuts)
* ----------------------------------------------------------------------------------------------
* PARAM 1) Arduino pin that is mapped to the button
* PARAM 2) Arduino pin that is mapped to the bank switch
* PARAM 3) bank #1: shortcut to send when short-pressed
* PARAM 4) bank #1: shortcut to send when long-pressed
* PARAM 5) bank #2: shortcut to send when short-pressed
* PARAM 6) bank #2: shortcut to send when long-pressed
*
* ----------------------------------------------------------------------------------------------
* shortcut format => concatenate MODIFIER KEYS (ctrl, shift, alt) and NORMAL KEY using "#"
* ----------------------------------------------------------------------------------------------
*
* examples:
* - "Shift#Alt#E"
* - "Ctrl#K"
* - "Alt#X"
*
* for SPECIAL KEYS (e.g. page up/down, up/down/left/right, escape, enter, and so on) check:
* https://www.arduino.cc/reference/en/language/functions/usb/keyboard/keyboardmodifiers/
*
* examples:
* - "Shift#Alt#" + KEY_F4
* - "Shift#Alt#" + KEY_TAB
* - "Shift#Alt#" + KEY_DOWN_ARROW
*
* examples for MIDI Note commands
* PARAM 1) note number: see https://newt.phys.unsw.edu.au/jw/notes.html
* PARAM 2) velocity: 0-127
* - "MIDI-Note#0#127"
* - "MIDI-Note#23#100"
* - "MIDI-Note#127#43"
*
* examples for MIDI Control Change (CC) commands
* PARAM 1) control number: 0-127
* PARAM 2) control value: 0-127
* - "MIDI-CC#0#127"
* - "MIDI-CC#55#54"
* - "MIDI-CC#127#3"
*
* examples for MIDI Program Change (PC) commands
* PARAM 1) program number: 0-127
* - "MIDI-PC#0"
* - "MIDI-PC#55"
* - "MIDI-PC#127"
*/
logMessage("Initializing buttons...");
btnPlayPause.init(5, 16, "Shift#Alt#S", "Shift#Alt#Q", "MIDI-PC#5", "MIDI-PC#55");
btnLoop.init(6, 16, "Shift#Alt#W", "Shift#Alt#T", "MIDI-PC#2", "MIDI-PC#22");
btnBack.init(7, 16, "Shift#Alt#A", "Shift#Alt#R", "MIDI-PC#4", "MIDI-PC#44");
btnFullscreen.init(8, 16, "Shift#Alt#F", "Shift#Alt#Z", "MIDI-PC#3", "MIDI-PC#33");
btnForward.init(9, 16, "Shift#Alt#D", "Shift#Alt#H", "MIDI-PC#6", "MIDI-PC#66");
btnSpeed.init(10, 16, "Shift#Alt#E", "Shift#Alt#G", "MIDI-PC#1", "MIDI-PC#11");
logMessage("Initialization done. Running loop now.");
logMessage("---------------------------------------------");
}
/* ----------------------------------------------------------------------------------------------
* loop
* ----------------------------------------------------------------------------------------------
*/
void loop() {
// check each button for short and long presses
btnPlayPause.check();
btnLoop.check();
btnBack.check();
btnFullscreen.check();
btnForward.check();
btnSpeed.check();
}