From 4c20ccec003beb48ccc182d349e20ab5eed3216e Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 31 Oct 2018 17:21:02 +0100 Subject: [PATCH 01/30] Update cvkeyboard.ino --- cvkeyboard.ino | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index ea04489..c0f4f96 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -53,38 +53,40 @@ void setup() void loop() { scan(); + send(); +} + +void send() { + for (int c = 48; c >= 0; c--) { + if (flip[c] == HIGH) { + flip[c] = LOW; + noteBuffer = c + noteOffset; + if (status[c] == HIGH) { + MIDI.sendNoteOn(noteBuffer, velocity, 1); + } + else if (status[c] == LOW) { + MIDI.sendNoteOff(noteBuffer, velocity, 1); + } + } + } } void scan() { for (int cOctave = 0; cOctave < 4; cOctave++) { - octBuffer = 12 * cOctave; + /*octBuffer = 12 * cOctave;*/ digitalWrite(octave[cOctave], HIGH); for (int cNote = 0; cNote < 12; cNote++) { - if (noteCounter[cNote + octBuffer] > 0) { - noteCounter[cNote + octBuffer]--; - } - else { - noteCounter[cNote + octBuffer] = offCounter; - buffer = digitalRead(note[cNote]); if (buffer ^ status[cNote + octBuffer]) { status[cNote + octBuffer] = buffer; flip[cNote + octBuffer] = HIGH; - noteBuffer = cNote + octBuffer + noteOffset; - if (buffer == HIGH) { - MIDI.sendNoteOn(noteBuffer, velocity, 1); - } - if (buffer == LOW) { - MIDI.sendNoteOff(noteBuffer, velocity, 1); - } } else { flip[cNote + octBuffer] = LOW; } - } } digitalWrite(octave[cOctave], LOW); From 27cbbe5c81f0a13203cd2f78441465ef42a20a4e Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 31 Oct 2018 17:26:11 +0100 Subject: [PATCH 02/30] First commit from VS --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ace3aed --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/.vs/CVkeyboard/v15/.suo From f5322a4bee9021c74d95b2f2d19dfa4ce3ca9517 Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 31 Oct 2018 17:28:14 +0100 Subject: [PATCH 03/30] Removed old code --- .gitignore | 2 ++ cvkeyboard.ino | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ace3aed..40f76f1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ ################################################################################ /.vs/CVkeyboard/v15/.suo +/.vs +/CppProperties.json diff --git a/cvkeyboard.ino b/cvkeyboard.ino index c0f4f96..870b4ff 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -73,7 +73,6 @@ void send() { void scan() { for (int cOctave = 0; cOctave < 4; cOctave++) { - /*octBuffer = 12 * cOctave;*/ digitalWrite(octave[cOctave], HIGH); From 54fc6f26ab7b6f9b01d5159b51c3fb95c988b0d5 Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 31 Oct 2018 17:28:53 +0100 Subject: [PATCH 04/30] Update .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 40f76f1..01d8aed 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,5 @@ # This .gitignore file was automatically created by Microsoft(R) Visual Studio. ################################################################################ -/.vs/CVkeyboard/v15/.suo /.vs /CppProperties.json From 3e477f4ca8ed0d81b6d6620c0b3c52a441625d24 Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 31 Oct 2018 17:44:56 +0100 Subject: [PATCH 05/30] Added off counter but with value 0 --- cvkeyboard.ino | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 870b4ff..9b37096 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -18,7 +18,7 @@ #define Oct4 10 #define noteOffset 36 -#define offCounter 100 +#define offCounter 0 #include #include @@ -60,12 +60,18 @@ void send() { for (int c = 48; c >= 0; c--) { if (flip[c] == HIGH) { flip[c] = LOW; - noteBuffer = c + noteOffset; - if (status[c] == HIGH) { - MIDI.sendNoteOn(noteBuffer, velocity, 1); + if (noteCounter[c] > 0) { + noteCounter[c]--; } - else if (status[c] == LOW) { - MIDI.sendNoteOff(noteBuffer, velocity, 1); + else { + noteCounter[c] = offCounter; + noteBuffer = c + noteOffset; + if (status[c] == HIGH) { + MIDI.sendNoteOn(noteBuffer, velocity, 1); + } + else if (status[c] == LOW) { + MIDI.sendNoteOff(noteBuffer, velocity, 1); + } } } } @@ -73,6 +79,7 @@ void send() { void scan() { for (int cOctave = 0; cOctave < 4; cOctave++) { + octBuffer = 12 * cOctave; digitalWrite(octave[cOctave], HIGH); From 84c9296956df90d60b1cf263fffc57639279b7bd Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 11 Nov 2018 01:11:49 +0100 Subject: [PATCH 06/30] Introduced arpeggiator. Testing --- cvkeyboard.ino | 83 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 9b37096..baba5fa 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -19,23 +19,34 @@ #define noteOffset 36 #define offCounter 0 +#define MINUTE 60000 #include #include MIDI_CREATE_DEFAULT_INSTANCE(); int note[12] = { - C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Pin delle note : 0 -> C , 11 -> B + C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above int octave[4] = { - Oct1, Oct2, Oct3, Oct4 }; // Pin delle ottave : 0 -> 2 , 3 -> 5 + Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above + int noteCounter[49] = { 0 }; -boolean status[49] = { LOW }; // Array di stato, aggiornato durante ogni ciclo. 0 -> C2 , 11 -> C3 , 23 -> C4 -> , 35 -> C5 , 48 -> C6 +boolean status[49] = { LOW }; boolean flip[49] = { LOW }; -boolean buffer = LOW; // Usato come buffer per lo stato di ogni pin, per non chiamare una lettura ogni volta. -int octBuffer; // Usato per non ripetere l'aritmetica ad ogni accesso all'array di stato. +boolean buffer = LOW; + +int octBuffer; byte noteBuffer; -byte velocity = 100; // Placeholder. -int channel = 7; // Placeholder. + + +byte velocity = 100; // Placeholder. Will need something to change it +int channel = 7; // Placeholder. Will need something to change it +int bpm = 120; // Placeholder. Will need something to change it +int gate = 25; // Placeholder. Will need something to change it + +int nextBeat = 0; +int step = 0; + void setup() @@ -48,12 +59,14 @@ void setup() } MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); + nextBeat = millis() + (MINUTE / bpm); } void loop() { + if (millis() < nextBeat) return; scan(); - send(); + arp(); } void send() { @@ -67,16 +80,40 @@ void send() { noteCounter[c] = offCounter; noteBuffer = c + noteOffset; if (status[c] == HIGH) { - MIDI.sendNoteOn(noteBuffer, velocity, 1); + MIDI.sendNoteOn(noteBuffer, velocity, channel); } else if (status[c] == LOW) { - MIDI.sendNoteOff(noteBuffer, velocity, 1); + MIDI.sendNoteOff(noteBuffer, velocity, channel); } } } } } +void playNote(int c, boolean status) { + if (status == HIGH) { + MIDI.sendNoteOn(c + noteOffset, velocity, channel); + } + else if (status == LOW) { + MIDI.sendNoteOff(c + noteOffset, velocity, channel); + } +} + +void arp() { + while (step < 49 && status[step] == LOW) { + step++; + } + if (step == 49) { + step = 0; + } + else { + playNote(step, HIGH); + if (gate < millis() - nextBeat) delay(gate - 5); // 5 ms arbitrarily for the check. Need something more sofisticate + playNote(step, LOW); + } + return; +} + void scan() { for (int cOctave = 0; cOctave < 4; cOctave++) { octBuffer = 12 * cOctave; @@ -84,17 +121,27 @@ void scan() { for (int cNote = 0; cNote < 12; cNote++) { - buffer = digitalRead(note[cNote]); + buffer = digitalRead(note[cNote]); - if (buffer ^ status[cNote + octBuffer]) { - status[cNote + octBuffer] = buffer; - flip[cNote + octBuffer] = HIGH; - } - else { - flip[cNote + octBuffer] = LOW; - } + if (buffer ^ status[cNote + octBuffer]) { + status[cNote + octBuffer] = buffer; + flip[cNote + octBuffer] = HIGH; + } + else { + flip[cNote + octBuffer] = LOW; + } } digitalWrite(octave[cOctave], LOW); } + +} + +int nPressed() { + int c, n = 0; + for (c = 0; c < 49; c++) { + if (status[c] == HIGH) { + n++; + } + } } From d8e545a50c883b914510786131f9387af87f024e Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 11 Nov 2018 02:00:08 +0100 Subject: [PATCH 07/30] Fixed some basic stuff and tried working around dirty signal --- cvkeyboard.ino | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index baba5fa..af1360b 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -42,10 +42,12 @@ byte noteBuffer; byte velocity = 100; // Placeholder. Will need something to change it int channel = 7; // Placeholder. Will need something to change it int bpm = 120; // Placeholder. Will need something to change it -int gate = 25; // Placeholder. Will need something to change it +int gate = 300; // Placeholder. Will need something to change it -int nextBeat = 0; +unsigned long nextBeat = 0; int step = 0; +int lastStep = 0; +boolean notePlayed = LOW; @@ -65,8 +67,33 @@ void setup() void loop() { if (millis() < nextBeat) return; + notePlayed = LOW; + while (notePlayed == LOW) { + cleanScan(); + arp(); + } + nextBeat += (MINUTE / bpm); +} + +void cleanScan() { + int c; + for (c = 0; c < 49; c++) noteCounter[c] = 0; scan(); - arp(); + for (c = 0; c < 49; c++) { + if (status[c] == HIGH) noteCounter[c]++; + } + scan(); + for (c = 0; c < 49; c++) { + if (status[c] == HIGH) noteCounter[c]++; + } + scan(); + for (c = 0; c < 49; c++) { + if (status[c] == HIGH) noteCounter[c]++; + } + for (c = 0; c < 49; c++) { + if (noteCounter[c] == 3) status[c] = HIGH; + else status[c] = LOW; + } } void send() { @@ -100,6 +127,7 @@ void playNote(int c, boolean status) { } void arp() { + step++; while (step < 49 && status[step] == LOW) { step++; } @@ -107,9 +135,10 @@ void arp() { step = 0; } else { + playNote(lastStep, LOW); playNote(step, HIGH); - if (gate < millis() - nextBeat) delay(gate - 5); // 5 ms arbitrarily for the check. Need something more sofisticate - playNote(step, LOW); + lastStep = step; + notePlayed = HIGH; } return; } From 55ab943ecd31854bea939a6bd81e1a54b6509b5c Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 4 Mar 2019 18:54:22 +0100 Subject: [PATCH 08/30] Rewriting my code from ground up. Now readings are made per octave with a struct. No MIDI is implemented yet, still debugging readings --- cvkeyboard.ino | 221 +++++++++++++++++++++---------------------------- 1 file changed, 95 insertions(+), 126 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index af1360b..c911d53 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -18,159 +18,128 @@ #define Oct4 10 #define noteOffset 36 -#define offCounter 0 -#define MINUTE 60000 -#include -#include -MIDI_CREATE_DEFAULT_INSTANCE(); +//#include +//#include +//MIDI_CREATE_DEFAULT_INSTANCE(); + +typedef struct OctaveStatus { + bool stat[12]; + int nOct; +} octst; int note[12] = { C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above int octave[4] = { Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above +int ledPins[12]{ + 19, 18, 17, 16, 15, 14, 2, 3, 4, 5, 6, 0 }; -int noteCounter[49] = { 0 }; -boolean status[49] = { LOW }; -boolean flip[49] = { LOW }; -boolean buffer = LOW; +int clock = 0; +octst buff; -int octBuffer; -byte noteBuffer; - - -byte velocity = 100; // Placeholder. Will need something to change it -int channel = 7; // Placeholder. Will need something to change it -int bpm = 120; // Placeholder. Will need something to change it -int gate = 300; // Placeholder. Will need something to change it - -unsigned long nextBeat = 0; -int step = 0; -int lastStep = 0; -boolean notePlayed = LOW; - - - -void setup() -{ +void setup() { for (int cOctave = 0; cOctave < 4; cOctave++) { pinMode(octave[cOctave], OUTPUT); } for (int cNote = 0; cNote < 12; cNote++) { pinMode(note[cNote], INPUT); } - MIDI.begin(MIDI_CHANNEL_OFF); + for (int cLed = 0; cLed < 12; cLed++) { + pinMode(ledPins[cLed], OUTPUT); + } + // MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - nextBeat = millis() + (MINUTE / bpm); + // nextBeat = millis() + (MINUTE / bpm); + for (int cLed = 0; cLed < 12; cLed++) { + digitalWrite(ledPins[cLed], HIGH); + delay(100); + } + for (int cLed = 0; cLed < 12; cLed++) { + digitalWrite(ledPins[cLed], LOW); + delay(100); + } + pinMode(50, OUTPUT); } - void loop() { - if (millis() < nextBeat) return; - notePlayed = LOW; - while (notePlayed == LOW) { - cleanScan(); - arp(); + for (clock = 0; clock < 4; clock++) { + digitalWrite(octave[clock], HIGH); + buff = scan(clock); + digitalWrite(octave[clock], LOW); + //clean = clearOct(buff[0], buff[1], buff[2], buff[3], buff[4]); + debug(buff); + serialDebug(buff); } - nextBeat += (MINUTE / bpm); } -void cleanScan() { +bool debouncedRead(int pin) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + return HIGH; + } + } + } + } + } + return LOW; +} +octst scan(int nOct) { int c; - for (c = 0; c < 49; c++) noteCounter[c] = 0; - scan(); - for (c = 0; c < 49; c++) { - if (status[c] == HIGH) noteCounter[c]++; + octst output; + + output.nOct = nOct; + + for (c = 0; c < 12; c++) { + output.stat[c] = digitalRead(note[c]); + // delay(50); } - scan(); - for (c = 0; c < 49; c++) { - if (status[c] == HIGH) noteCounter[c]++; + return output; +} + +/*octst clearOct(octst o1, octst o2, octst o3, octst o4, octst o5) { + octst output; + + output.nOct = o1.nOct; + for (int c = 0; c < 12; +c++) { + if (o1.stat[c] && o2.stat[c] && o3.stat[c] && o4.stat[c] && o5.stat[c]) output.stat[c] = HIGH; + else output.stat[c] = LOW; + } + return output; +}*/ + +void debug(octst input) { + int c; + for (c = 0; c < 12; c++) { + digitalWrite(ledPins[c], input.stat[c]); } - scan(); - for (c = 0; c < 49; c++) { - if (status[c] == HIGH) noteCounter[c]++; - } - for (c = 0; c < 49; c++) { - if (noteCounter[c] == 3) status[c] = HIGH; - else status[c] = LOW; + delay(5); + for (c = 0; c < 12; c++) { + digitalWrite(ledPins[c], LOW); } } -void send() { - for (int c = 48; c >= 0; c--) { - if (flip[c] == HIGH) { - flip[c] = LOW; - if (noteCounter[c] > 0) { - noteCounter[c]--; - } - else { - noteCounter[c] = offCounter; - noteBuffer = c + noteOffset; - if (status[c] == HIGH) { - MIDI.sendNoteOn(noteBuffer, velocity, channel); - } - else if (status[c] == LOW) { - MIDI.sendNoteOff(noteBuffer, velocity, channel); - } - } - } +void serialDebug(octst input) { + for (int c = 0; c < 12; c++) { + Serial.print(input.stat[c]); } + Serial.println(""); } -void playNote(int c, boolean status) { - if (status == HIGH) { - MIDI.sendNoteOn(c + noteOffset, velocity, channel); - } - else if (status == LOW) { - MIDI.sendNoteOff(c + noteOffset, velocity, channel); - } -} - -void arp() { - step++; - while (step < 49 && status[step] == LOW) { - step++; - } - if (step == 49) { - step = 0; - } - else { - playNote(lastStep, LOW); - playNote(step, HIGH); - lastStep = step; - notePlayed = HIGH; - } - return; -} - -void scan() { - for (int cOctave = 0; cOctave < 4; cOctave++) { - octBuffer = 12 * cOctave; - digitalWrite(octave[cOctave], HIGH); - - - for (int cNote = 0; cNote < 12; cNote++) { - buffer = digitalRead(note[cNote]); - - if (buffer ^ status[cNote + octBuffer]) { - status[cNote + octBuffer] = buffer; - flip[cNote + octBuffer] = HIGH; - } - else { - flip[cNote + octBuffer] = LOW; - } - - } - digitalWrite(octave[cOctave], LOW); - } - -} - -int nPressed() { - int c, n = 0; - for (c = 0; c < 49; c++) { - if (status[c] == HIGH) { - n++; - } - } -} +/* debugLed(int c) { + switch (c) { + case 0: digitalWrite(2, HIGH); + break; + case 1: digitalWrite(3, HIGH); + break; + case 2: digitalWrite(4, HIGH); + break; + case 3: digitalWrite(2, LOW); + digitalWrite(3, LOW); + digitalWrite(4, LOW); + break; + } +}*/ \ No newline at end of file From 198bbaf69e2ed4466f5a289b06396f7a6236ec12 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 4 Mar 2019 18:56:20 +0100 Subject: [PATCH 09/30] Reordered code --- cvkeyboard.ino | 63 +++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index c911d53..66b8d09 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -67,26 +67,12 @@ void loop() { digitalWrite(octave[clock], HIGH); buff = scan(clock); digitalWrite(octave[clock], LOW); - //clean = clearOct(buff[0], buff[1], buff[2], buff[3], buff[4]); debug(buff); serialDebug(buff); } } -bool debouncedRead(int pin) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - return HIGH; - } - } - } - } - } - return LOW; -} + octst scan(int nOct) { int c; octst output; @@ -95,22 +81,10 @@ octst scan(int nOct) { for (c = 0; c < 12; c++) { output.stat[c] = digitalRead(note[c]); - // delay(50); } return output; } -/*octst clearOct(octst o1, octst o2, octst o3, octst o4, octst o5) { - octst output; - - output.nOct = o1.nOct; - for (int c = 0; c < 12; +c++) { - if (o1.stat[c] && o2.stat[c] && o3.stat[c] && o4.stat[c] && o5.stat[c]) output.stat[c] = HIGH; - else output.stat[c] = LOW; - } - return output; -}*/ - void debug(octst input) { int c; for (c = 0; c < 12; c++) { @@ -129,17 +103,28 @@ void serialDebug(octst input) { Serial.println(""); } -/* debugLed(int c) { - switch (c) { - case 0: digitalWrite(2, HIGH); - break; - case 1: digitalWrite(3, HIGH); - break; - case 2: digitalWrite(4, HIGH); - break; - case 3: digitalWrite(2, LOW); - digitalWrite(3, LOW); - digitalWrite(4, LOW); - break; +bool debouncedRead(int pin) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + if (digitalRead(pin) == HIGH) { + return HIGH; + } + } + } + } + } + return LOW; +} + +/*octst clearOct(octst o1, octst o2, octst o3, octst o4, octst o5) { + octst output; + + output.nOct = o1.nOct; + for (int c = 0; c < 12; +c++) { + if (o1.stat[c] && o2.stat[c] && o3.stat[c] && o4.stat[c] && o5.stat[c]) output.stat[c] = HIGH; + else output.stat[c] = LOW; } + return output; }*/ \ No newline at end of file From eadbbbf9eba37f30e372e322c29a609e21d76ca7 Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 4 Mar 2019 19:04:13 +0100 Subject: [PATCH 10/30] Added comments --- cvkeyboard.ino | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 66b8d09..b74783f 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -29,13 +29,13 @@ typedef struct OctaveStatus { } octst; int note[12] = { - C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above + C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above int octave[4] = { - Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above + Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above int ledPins[12]{ 19, 18, 17, 16, 15, 14, 2, 3, 4, 5, 6, 0 }; -int clock = 0; +int clock = 0; // Keeps track of current octave octst buff; void setup() { @@ -73,8 +73,8 @@ void loop() { } -octst scan(int nOct) { - int c; +octst scan(int nOct) { // This function reads the 12 note pins and returns a struct + int c; // with 1 bool for each note octst output; output.nOct = nOct; @@ -85,7 +85,7 @@ octst scan(int nOct) { return output; } -void debug(octst input) { +void debug(octst input) { // Lights up 12 LEDs used to control the readings int c; for (c = 0; c < 12; c++) { digitalWrite(ledPins[c], input.stat[c]); @@ -96,14 +96,14 @@ void debug(octst input) { } } -void serialDebug(octst input) { +void serialDebug(octst input) { // Prints on the Serial Monitor the 12 bits just read for (int c = 0; c < 12; c++) { Serial.print(input.stat[c]); } Serial.println(""); } -bool debouncedRead(int pin) { +bool debouncedRead(int pin) { // Should clear readings from false positives but doesn't work if (digitalRead(pin) == HIGH) { if (digitalRead(pin) == HIGH) { if (digitalRead(pin) == HIGH) { From 7c7b0cf11607945264cd5c54a2ee23a5900b23d9 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 8 Mar 2019 21:10:15 +0100 Subject: [PATCH 11/30] Implemented an arpeggiator and then broke it (should wait for MIDI clock). Added a basic implementation of 3 capacitive buttons (for drum pads) --- cvkeyboard.ino | 205 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 145 insertions(+), 60 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index b74783f..7de3dbb 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -18,10 +18,13 @@ #define Oct4 10 #define noteOffset 36 +#define MINUTE 60000 -//#include -//#include -//MIDI_CREATE_DEFAULT_INSTANCE(); +#include +#include +#include + +MIDI_CREATE_DEFAULT_INSTANCE(); typedef struct OctaveStatus { bool stat[12]; @@ -29,14 +32,27 @@ typedef struct OctaveStatus { } octst; int note[12] = { - C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above + C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above int octave[4] = { - Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above -int ledPins[12]{ - 19, 18, 17, 16, 15, 14, 2, 3, 4, 5, 6, 0 }; + Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above -int clock = 0; // Keeps track of current octave +int clock = 0; // Used if arp to cycle through notes octst buff; +bool kboard[49]; +bool raw; // Global Settings. RAW = signal is sent when key is detected +byte velocity = 100; +byte channel = 1; +byte midi_clock = 0xf8; +byte dataIn; +int bpm = 360; +unsigned long nextBeat = 0; +unsigned long gate = 50; //ms of keypress if arpeggiator +int npressed; +bool bu1, bu2, bu3; + +CapacitiveSensor b1 = CapacitiveSensor(5, 6); +CapacitiveSensor b2 = CapacitiveSensor(4, 3); +CapacitiveSensor b3 = CapacitiveSensor(16, 17); void setup() { for (int cOctave = 0; cOctave < 4; cOctave++) { @@ -45,36 +61,49 @@ void setup() { for (int cNote = 0; cNote < 12; cNote++) { pinMode(note[cNote], INPUT); } - for (int cLed = 0; cLed < 12; cLed++) { - pinMode(ledPins[cLed], OUTPUT); - } - // MIDI.begin(MIDI_CHANNEL_OFF); + MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - // nextBeat = millis() + (MINUTE / bpm); - for (int cLed = 0; cLed < 12; cLed++) { - digitalWrite(ledPins[cLed], HIGH); - delay(100); - } - for (int cLed = 0; cLed < 12; cLed++) { - digitalWrite(ledPins[cLed], LOW); - delay(100); - } - pinMode(50, OUTPUT); + nextBeat = millis() + (MINUTE / bpm); + pinMode(2, INPUT_PULLUP); + for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; + nextBeat = 0; + + b1.set_CS_AutocaL_Millis(0xFFFFFFFF); + b2.set_CS_AutocaL_Millis(0xFFFFFFFF); + b3.set_CS_AutocaL_Millis(0xFFFFFFFF); + bu1 = LOW; + bu2 = LOW; + bu3 = LOW; } void loop() { - for (clock = 0; clock < 4; clock++) { - digitalWrite(octave[clock], HIGH); - buff = scan(clock); - digitalWrite(octave[clock], LOW); - debug(buff); - serialDebug(buff); + scanButtons(); + + npressed = 0; + raw = digitalRead(2); + for (int cOctave = 0; cOctave < 4; cOctave++) { + digitalWrite(octave[cOctave], HIGH); + npressed += eval(scan(cOctave)); + digitalWrite(octave[cOctave], LOW); + } + if (raw) return; + if (npressed < 1) return; + dataIn = Serial.read(); + if (dataIn == midi_clock) { + clock++; + while (kboard[clock] == LOW) { + clock++; + if (clock == 49) clock = 0; + } + playNote(clock, HIGH); + delay(gate); + playNote(clock, LOW); } } -octst scan(int nOct) { // This function reads the 12 note pins and returns a struct - int c; // with 1 bool for each note +octst scan(int nOct) { // This function reads the 12 note pins and returns a struct + int c; // with 1 bool for each note octst output; output.nOct = nOct; @@ -85,46 +114,102 @@ octst scan(int nOct) { // This function reads the 12 note pins and returns a return output; } -void debug(octst input) { // Lights up 12 LEDs used to control the readings - int c; - for (c = 0; c < 12; c++) { - digitalWrite(ledPins[c], input.stat[c]); - } - delay(5); - for (c = 0; c < 12; c++) { - digitalWrite(ledPins[c], LOW); +int eval(octst input) { + int pressed = 0; + int snote = input.nOct * 12; + + for (int c = 0; c < 12; c++) { + if (input.stat[c] ^ kboard[c + snote]) { + if (raw) playNote(c + snote, input.stat[c]); + kboard[c + snote] = input.stat[c]; + } + if (kboard[c + snote] == HIGH) pressed++; } + return pressed; } -void serialDebug(octst input) { // Prints on the Serial Monitor the 12 bits just read +void serialDebug(octst input) { // Prints on the Serial Monitor the 12 bits just read for (int c = 0; c < 12; c++) { Serial.print(input.stat[c]); } Serial.println(""); } -bool debouncedRead(int pin) { // Should clear readings from false positives but doesn't work - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - if (digitalRead(pin) == HIGH) { - return HIGH; - } - } - } - } +void playNote(int c, bool status) { + byte n = c + noteOffset; + if (status == HIGH) { + MIDI.sendNoteOn(n, velocity, channel); + } + else if (status == LOW) { + MIDI.sendNoteOff(n, velocity, channel); } - return LOW; } -/*octst clearOct(octst o1, octst o2, octst o3, octst o4, octst o5) { - octst output; +void scanButtons() { + long sensor1 = b1.capacitiveSensor(1); + long sensor2 = b2.capacitiveSensor(1); + long sensor3 = b3.capacitiveSensor(1); - output.nOct = o1.nOct; - for (int c = 0; c < 12; +c++) { - if (o1.stat[c] && o2.stat[c] && o3.stat[c] && o4.stat[c] && o5.stat[c]) output.stat[c] = HIGH; - else output.stat[c] = LOW; - } - return output; -}*/ \ No newline at end of file + if (sensor1 > 10) { + if (!bu1) { + MIDI.sendNoteOn(95, velocity, 7); + bu1 = HIGH; + } + } + else { + if (bu1) { + MIDI.sendNoteOff(95, velocity, 7); + bu1 = LOW; + } + } + + + if (sensor2 > 10) { + if (!bu2) { + MIDI.sendNoteOn(97, velocity, 7); + bu2 = HIGH; + } + } + else { + if (bu2) { + MIDI.sendNoteOff(97, velocity, 7); + bu2 = LOW; + } + } + + + if (sensor3 > 10) { + if (!bu3) { + MIDI.sendNoteOn(99, velocity, 7); + bu3 = HIGH; + } + } + else { + if (bu3) { + MIDI.sendNoteOff(99, velocity, 7); + bu3 = LOW; + } + } + /*bu1 = evalButton(b1, bu1, 95); + bu2 = evalButton(b2, bu2, 97); + bu3 = evalButton(b3, bu3, 99);*/ +} + +bool evalButton(CapacitiveSensor b, bool value, int note) { + long sensor = b.capacitiveSensor(1); + + if (sensor > 15) { + if (value) return HIGH; + else { + MIDI.sendNoteOn(note, velocity, 7); + return HIGH; + } + } + else { + if (!value) return LOW; + else { + MIDI.sendNoteOff(note, velocity, 7); + return LOW; + } + } +} \ No newline at end of file From 96a4142c0fdd0141f2eaba5d6dcf9fd2b93006bf Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 9 Mar 2019 15:23:18 +0100 Subject: [PATCH 12/30] Cleaned code. Capacitive Buttons have been reimplemented and are now easily scalable --- cvkeyboard.ino | 163 +++++++++++++++---------------------------------- 1 file changed, 48 insertions(+), 115 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 7de3dbb..33b52e8 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -1,24 +1,7 @@ -#define C 22 -#define Db 24 -#define D 26 -#define Eb 28 -#define E 30 -#define F 32 -#define Gb 34 -#define G 36 -#define Ab 38 -#define A 40 -#define Bb 42 -#define B 44 -#define testLed 13 - -#define Oct1 12 -#define Oct2 9 -#define Oct3 8 -#define Oct4 10 - #define noteOffset 36 +#define DRUMNOTE 60 #define MINUTE 60000 +#define MIDICLOCK 0xf8 #include #include @@ -26,33 +9,39 @@ MIDI_CREATE_DEFAULT_INSTANCE(); -typedef struct OctaveStatus { +typedef struct OctaveStatus { // This struct is for an octave status. Each bool is for 1 note bool stat[12]; int nOct; } octst; -int note[12] = { - C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B }; // Note Pins above -int octave[4] = { - Oct1, Oct2, Oct3, Oct4 }; // Octave Pins above -int clock = 0; // Used if arp to cycle through notes -octst buff; -bool kboard[49]; -bool raw; // Global Settings. RAW = signal is sent when key is detected -byte velocity = 100; -byte channel = 1; -byte midi_clock = 0xf8; -byte dataIn; -int bpm = 360; -unsigned long nextBeat = 0; -unsigned long gate = 50; //ms of keypress if arpeggiator -int npressed; -bool bu1, bu2, bu3; + // PIN DECLARATIONS +int note[12] = { // Pins used to read each note (C is 0, B is 11) + 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 }; +int octave[4] = { // Pins associated to each octave's contact bar + 12, 9, 8, 10 }; +int sendPin[3] = { // Pins used as sender for capacitive touch buttons + 5, 4, 16 }; +int receivePin[3] = { // Pins used as receiver for capacitive touch buttons + 6, 3, 17 }; + + // GLOBAL SETTINGS +bool raw; // Signal is sent when key is detected + + // PLACEHOLDERS +byte velocity = 100; // +byte channel = 1; // +int bpm = 360; // +unsigned long gate = 50; // ms of keypress if arpeggiator +unsigned long nextBeat = 0; // Used to keep track of beats. Useless if receiving MIDI clock. + + // SYSTEM VARIABLES +int clock = 0; // Used if arp to cycle through notes +int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed +bool kboard[49]; // Last status of keyboard +bool bCapStat[3]; // Last status of Capacitive Buttons +CapacitiveSensor* bCap[3]; -CapacitiveSensor b1 = CapacitiveSensor(5, 6); -CapacitiveSensor b2 = CapacitiveSensor(4, 3); -CapacitiveSensor b3 = CapacitiveSensor(16, 17); void setup() { for (int cOctave = 0; cOctave < 4; cOctave++) { @@ -61,23 +50,25 @@ void setup() { for (int cNote = 0; cNote < 12; cNote++) { pinMode(note[cNote], INPUT); } + for (int cButton = 0; cButton < 3; cButton++) { // Capacitive Buttons configuration + bCap[cButton] = new CapacitiveSensor(sendPin[cButton], receivePin[cButton]); // Initialized + bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration + bCap[cButton]->set_CS_Timeout_Millis(200); // Timeout set to 200ms (instead of 2s) + bCapStat[cButton] = LOW; // Button starts LOW + } + + for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW + MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - nextBeat = millis() + (MINUTE / bpm); - pinMode(2, INPUT_PULLUP); - for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; - nextBeat = 0; - b1.set_CS_AutocaL_Millis(0xFFFFFFFF); - b2.set_CS_AutocaL_Millis(0xFFFFFFFF); - b3.set_CS_AutocaL_Millis(0xFFFFFFFF); - bu1 = LOW; - bu2 = LOW; - bu3 = LOW; + pinMode(2, INPUT_PULLUP); // Used for RAW switch } void loop() { - scanButtons(); + for (int cButton = 0; cButton < 3; cButton++) { + bCapStat[cButton] = evalButton(bCap[cButton], bCapStat[cButton], DRUMNOTE + cButton); + } npressed = 0; raw = digitalRead(2); @@ -88,8 +79,7 @@ void loop() { } if (raw) return; if (npressed < 1) return; - dataIn = Serial.read(); - if (dataIn == midi_clock) { + if (Serial.read() == MIDICLOCK) { clock++; while (kboard[clock] == LOW) { clock++; @@ -103,7 +93,7 @@ void loop() { octst scan(int nOct) { // This function reads the 12 note pins and returns a struct - int c; // with 1 bool for each note + int c; // with 1 bool for each note octst output; output.nOct = nOct; @@ -128,13 +118,6 @@ int eval(octst input) { return pressed; } -void serialDebug(octst input) { // Prints on the Serial Monitor the 12 bits just read - for (int c = 0; c < 12; c++) { - Serial.print(input.stat[c]); - } - Serial.println(""); -} - void playNote(int c, bool status) { byte n = c + noteOffset; if (status == HIGH) { @@ -145,70 +128,20 @@ void playNote(int c, bool status) { } } -void scanButtons() { - long sensor1 = b1.capacitiveSensor(1); - long sensor2 = b2.capacitiveSensor(1); - long sensor3 = b3.capacitiveSensor(1); - - if (sensor1 > 10) { - if (!bu1) { - MIDI.sendNoteOn(95, velocity, 7); - bu1 = HIGH; - } - } - else { - if (bu1) { - MIDI.sendNoteOff(95, velocity, 7); - bu1 = LOW; - } - } - - - if (sensor2 > 10) { - if (!bu2) { - MIDI.sendNoteOn(97, velocity, 7); - bu2 = HIGH; - } - } - else { - if (bu2) { - MIDI.sendNoteOff(97, velocity, 7); - bu2 = LOW; - } - } - - - if (sensor3 > 10) { - if (!bu3) { - MIDI.sendNoteOn(99, velocity, 7); - bu3 = HIGH; - } - } - else { - if (bu3) { - MIDI.sendNoteOff(99, velocity, 7); - bu3 = LOW; - } - } - /*bu1 = evalButton(b1, bu1, 95); - bu2 = evalButton(b2, bu2, 97); - bu3 = evalButton(b3, bu3, 99);*/ -} - -bool evalButton(CapacitiveSensor b, bool value, int note) { - long sensor = b.capacitiveSensor(1); +bool evalButton(CapacitiveSensor* b, bool value, byte note) { + long sensor = b->capacitiveSensor(1); if (sensor > 15) { if (value) return HIGH; else { - MIDI.sendNoteOn(note, velocity, 7); + MIDI.sendNoteOn(note, velocity, (byte)7); return HIGH; } } else { if (!value) return LOW; else { - MIDI.sendNoteOff(note, velocity, 7); + MIDI.sendNoteOff(note, velocity, (byte)7); return LOW; } } From 5bd20dab8cf3969cd170ea70e29300dc5cc7004d Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 9 Mar 2019 19:14:18 +0100 Subject: [PATCH 13/30] Now keyboard waits for MIDI clock and plays 2 notes per beat when in arpeggiator --- cvkeyboard.ino | 91 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 33b52e8..7bd968b 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -9,37 +9,40 @@ MIDI_CREATE_DEFAULT_INSTANCE(); -typedef struct OctaveStatus { // This struct is for an octave status. Each bool is for 1 note +typedef struct OctaveStatus { // This struct is for an octave status. Each bool is for 1 note bool stat[12]; int nOct; } octst; - // PIN DECLARATIONS -int note[12] = { // Pins used to read each note (C is 0, B is 11) +// PIN DECLARATIONS +int note[12] = { // Pins used to read each note (C is 0, B is 11) 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 }; -int octave[4] = { // Pins associated to each octave's contact bar +int octave[4] = { // Pins associated to each octave's contact bar 12, 9, 8, 10 }; -int sendPin[3] = { // Pins used as sender for capacitive touch buttons - 5, 4, 16 }; -int receivePin[3] = { // Pins used as receiver for capacitive touch buttons - 6, 3, 17 }; +int sendPin[3] = { // Pins used as sender for capacitive touch buttons + 5, 4, 16 }; +int receivePin[3] = { // Pins used as receiver for capacitive touch buttons + 6, 3, 17 }; - // GLOBAL SETTINGS -bool raw; // Signal is sent when key is detected +// GLOBAL SETTINGS +bool raw; // Signal is sent when key is detected - // PLACEHOLDERS -byte velocity = 100; // -byte channel = 1; // -int bpm = 360; // -unsigned long gate = 50; // ms of keypress if arpeggiator -unsigned long nextBeat = 0; // Used to keep track of beats. Useless if receiving MIDI clock. + // PLACEHOLDERS +byte velocity = 100; // +byte channel = 1; // +int bpm = 360; // +unsigned long gate = 50; // ms of keypress if arpeggiator +unsigned long nextBeat = 0; // Used to keep track of beats. Useless if receiving MIDI clock. - // SYSTEM VARIABLES -int clock = 0; // Used if arp to cycle through notes -int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed -bool kboard[49]; // Last status of keyboard -bool bCapStat[3]; // Last status of Capacitive Buttons + // SYSTEM VARIABLES +int arp = 0; // Keeps track of last played note if arpeggiating +int midiclock = 0; // Used to sync with MIDI clock +int semA = 0; // Basic semaphore implementation with global counter +int semB = 0; +int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed +bool kboard[49]; // Last status of keyboard +bool bCapStat[3]; // Last status of Capacitive Buttons CapacitiveSensor* bCap[3]; @@ -50,26 +53,27 @@ void setup() { for (int cNote = 0; cNote < 12; cNote++) { pinMode(note[cNote], INPUT); } - for (int cButton = 0; cButton < 3; cButton++) { // Capacitive Buttons configuration - bCap[cButton] = new CapacitiveSensor(sendPin[cButton], receivePin[cButton]); // Initialized - bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration - bCap[cButton]->set_CS_Timeout_Millis(200); // Timeout set to 200ms (instead of 2s) - bCapStat[cButton] = LOW; // Button starts LOW + for (int cButton = 0; cButton < 3; cButton++) { // Capacitive Buttons configuration + bCap[cButton] = new CapacitiveSensor(sendPin[cButton], receivePin[cButton]); // Initialized + bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration + bCap[cButton]->set_CS_Timeout_Millis(200); // Timeout set to 200ms (instead of 2s) + bCapStat[cButton] = LOW; // Button starts LOW } - for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW + for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - pinMode(2, INPUT_PULLUP); // Used for RAW switch + pinMode(2, INPUT_PULLUP); // Used for RAW switch } void loop() { + sync(); + for (int cButton = 0; cButton < 3; cButton++) { bCapStat[cButton] = evalButton(bCap[cButton], bCapStat[cButton], DRUMNOTE + cButton); } - npressed = 0; raw = digitalRead(2); for (int cOctave = 0; cOctave < 4; cOctave++) { @@ -79,15 +83,19 @@ void loop() { } if (raw) return; if (npressed < 1) return; - if (Serial.read() == MIDICLOCK) { - clock++; - while (kboard[clock] == LOW) { - clock++; - if (clock == 49) clock = 0; + + if (semA > 0) { + semA--; + arp++; + while (kboard[arp] == LOW) { + arp++; + if (arp == 49) arp = 0; } - playNote(clock, HIGH); - delay(gate); - playNote(clock, LOW); + playNote(arp, HIGH); + } + if (semB > 0) { + semB--; + playNote(arp, LOW); } } @@ -145,4 +153,13 @@ bool evalButton(CapacitiveSensor* b, bool value, byte note) { return LOW; } } +} + +void sync() { + if (Serial.available() && Serial.read() == MIDICLOCK) { + midiclock++; + if (midiclock == 11 && semA == 0) semA++; + else if (midiclock == 5 && semB == 0) semB++; + else if (midiclock == 12) midiclock = 0; + } } \ No newline at end of file From 2727b2deef5e894cdf65d92ea208604b132806ca Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 10 Mar 2019 02:33:59 +0100 Subject: [PATCH 14/30] Implemented a basic sequencer in place of arpeggiator --- cvkeyboard.ino | 103 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 7bd968b..ddd57d4 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -9,11 +9,17 @@ MIDI_CREATE_DEFAULT_INSTANCE(); +typedef struct SequencerStep* link; + typedef struct OctaveStatus { // This struct is for an octave status. Each bool is for 1 note bool stat[12]; int nOct; } octst; +typedef struct SequencerStep { + int note; + link next; +} step; // PIN DECLARATIONS int note[12] = { // Pins used to read each note (C is 0, B is 11) @@ -25,17 +31,20 @@ int sendPin[3] = { // Pins used as sender for capacitive touch button int receivePin[3] = { // Pins used as receiver for capacitive touch buttons 6, 3, 17 }; -// GLOBAL SETTINGS + // GLOBAL SETTINGS bool raw; // Signal is sent when key is detected - // PLACEHOLDERS + // PLACEHOLDERS byte velocity = 100; // byte channel = 1; // int bpm = 360; // unsigned long gate = 50; // ms of keypress if arpeggiator -unsigned long nextBeat = 0; // Used to keep track of beats. Useless if receiving MIDI clock. - // SYSTEM VARIABLES + // SEQUENCER POINTERS +link head, tail, current; + + // SYSTEM VARIABLES +int nstep = 0; // Keeps track of the sequencer steps int arp = 0; // Keeps track of last played note if arpeggiating int midiclock = 0; // Used to sync with MIDI clock int semA = 0; // Basic semaphore implementation with global counter @@ -82,20 +91,26 @@ void loop() { digitalWrite(octave[cOctave], LOW); } if (raw) return; - if (npressed < 1) return; if (semA > 0) { semA--; - arp++; - while (kboard[arp] == LOW) { - arp++; - if (arp == 49) arp = 0; + + if (bCapStat[1]) { + checkInsert(); } - playNote(arp, HIGH); + else if (bCapStat[2] && npressed > 0) { + checkReplace(); + } + + if (current != NULL && current->note != -1) playNote(current->note, HIGH); } if (semB > 0) { semB--; - playNote(arp, LOW); + if (bCapStat[0] && bCapStat[2]) { + deleteStep(); + } + if (current != NULL && current->note != -1) playNote(current->note, LOW); + nextStep(); } } @@ -162,4 +177,70 @@ void sync() { else if (midiclock == 5 && semB == 0) semB++; else if (midiclock == 12) midiclock = 0; } +} + +link newStep() { + return (link)malloc(sizeof(struct SequencerStep)); +} + +bool insertStep(int note) { + link newS = newStep(); + if (newS == NULL) { + free(newS); + return LOW; + } + + newS->note = note; + if (nstep == 0) { + newS->next = newS; + current = newS; + head = newS; + } + else { + newS->next = current->next; + current->next = newS; + } + nstep++; + return HIGH; +} + +void nextStep() { + current = current->next; +} + +bool deleteStep() { + if (nstep < 1) return LOW; + + if (nstep == 1) { + free(current); + head = NULL; + current = NULL; + } + else { + link buffer = current->next->next; + free(current->next); + current->next = buffer; + } + nstep--; + return HIGH; +} + +void checkInsert() { + if (npressed < 1) insertStep(-1); + else { + arp++; + while (!kboard[arp]) { + arp++; + if (arp == 49) arp = 0; + } + insertStep(arp); + } +} +void checkReplace() { + arp++; + while (!kboard[arp]) { + arp++; + if (arp == 49) arp = 0; + } + current->note = arp; } \ No newline at end of file From 0923297c099385973980a16e9577d95b65f6d942 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 27 Jun 2019 18:58:37 +0200 Subject: [PATCH 15/30] Reworked sequencer. It misses a way to add steps --- cvkeyboard.ino | 232 ++++++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 110 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index ddd57d4..769cdfb 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -1,7 +1,9 @@ -#define noteOffset 36 -#define DRUMNOTE 60 +#define NOTEOffset 36 +#define drumOffset 60 #define MINUTE 60000 #define MIDICLOCK 0xf8 +#define MAXKEYS 48 +#define MAXDPAD 3 #include #include @@ -11,186 +13,216 @@ MIDI_CREATE_DEFAULT_INSTANCE(); typedef struct SequencerStep* link; -typedef struct OctaveStatus { // This struct is for an octave status. Each bool is for 1 note +typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each bool is for 1 NOTE bool stat[12]; int nOct; } octst; typedef struct SequencerStep { - int note; + int NOTE; + bool kboard_s[MAXKEYS] + bool dpad_s[MAXDPAD] link next; } step; // PIN DECLARATIONS -int note[12] = { // Pins used to read each note (C is 0, B is 11) +int NOTE[12] = { // Pins used to read each note (C is 0, B is 11) 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 }; -int octave[4] = { // Pins associated to each octave's contact bar +int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar 12, 9, 8, 10 }; -int sendPin[3] = { // Pins used as sender for capacitive touch buttons +int SEND[3] = { // Pins used as sender for capacitive touch buttons 5, 4, 16 }; -int receivePin[3] = { // Pins used as receiver for capacitive touch buttons +int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons 6, 3, 17 }; +int OW = 2; // Pin used for overwrite switch +int DEL = -1; // Pin used for delete button // GLOBAL SETTINGS -bool raw; // Signal is sent when key is detected +bool overwrite; // Step content is overwritten with pressed keys // PLACEHOLDERS byte velocity = 100; // byte channel = 1; // int bpm = 360; // -unsigned long gate = 50; // ms of keypress if arpeggiator + // SEQUENCER POINTERS link head, tail, current; // SYSTEM VARIABLES int nstep = 0; // Keeps track of the sequencer steps -int arp = 0; // Keeps track of last played note if arpeggiating +int arp = 0; // Keeps track of last played NOTE if arpeggiating int midiclock = 0; // Used to sync with MIDI clock -int semA = 0; // Basic semaphore implementation with global counter -int semB = 0; +int sem_beat = 0; // Basic semaphore used to sync with MIDI beat +int sem_gate = 0; // Basic semaphore used for gate timing +unsigned long last_gate = 0; // Gate start time for last sequencer step +unsigned long gate_length = 200; // ms of keypress if arpeggiator +bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed -bool kboard[49]; // Last status of keyboard -bool bCapStat[3]; // Last status of Capacitive Buttons -CapacitiveSensor* bCap[3]; +bool kboard[MAXKEYS]; // Last status of keyboard +bool dpad[MAXDPAD]; // Last status of Capacitive Buttons +CapacitiveSensor* bCap[MAXDPAD]; void setup() { - for (int cOctave = 0; cOctave < 4; cOctave++) { - pinMode(octave[cOctave], OUTPUT); + for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) { + pinMode(OCTAVE[cOCTAVE], OUTPUT); } - for (int cNote = 0; cNote < 12; cNote++) { - pinMode(note[cNote], INPUT); + for (int cNOTE = 0; cNOTE < 12; cNOTE++) { + pinMode(NOTE[cNOTE], INPUT); } - for (int cButton = 0; cButton < 3; cButton++) { // Capacitive Buttons configuration - bCap[cButton] = new CapacitiveSensor(sendPin[cButton], receivePin[cButton]); // Initialized + for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration + bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration bCap[cButton]->set_CS_Timeout_Millis(200); // Timeout set to 200ms (instead of 2s) - bCapStat[cButton] = LOW; // Button starts LOW + dpad[cButton] = LOW; // Button starts LOW } - for (int cStat = 0; cStat < 49; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW + for (int cStat = 0; cStat < MAXKEYS; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - pinMode(2, INPUT_PULLUP); // Used for RAW switch + pinMode(2, INPUT_PULLUP); // Used for overwrite switch } void loop() { sync(); - - for (int cButton = 0; cButton < 3; cButton++) { - bCapStat[cButton] = evalButton(bCap[cButton], bCapStat[cButton], DRUMNOTE + cButton); - } - npressed = 0; - raw = digitalRead(2); - for (int cOctave = 0; cOctave < 4; cOctave++) { - digitalWrite(octave[cOctave], HIGH); - npressed += eval(scan(cOctave)); - digitalWrite(octave[cOctave], LOW); - } - if (raw) return; - - if (semA > 0) { - semA--; - - if (bCapStat[1]) { - checkInsert(); + if (sem_beat > 0) { + sem_beat--; + if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step + sem_gate--; + for (i=0; ikboard_s[c]) playNOTE(i, !current->kboard_s[c]); + for (i=0; idpad_s[c]) playDrum(i, !current->dpad_s[c]); } - else if (bCapStat[2] && npressed > 0) { - checkReplace(); - } - - if (current != NULL && current->note != -1) playNote(current->note, HIGH); - } - if (semB > 0) { - semB--; - if (bCapStat[0] && bCapStat[2]) { - deleteStep(); - } - if (current != NULL && current->note != -1) playNote(current->note, LOW); nextStep(); + if (current != NULL) { // Play all step notes and begin counting for gate + for (i=0; ikboard_s[c]) playNOTE(i, current->kboard_s[c]); + for (i=0; idpad_s[c]) playDrum(i, current->dpad_s[c]); + last_gate = millis(); + sem_gate++; + } + } + if (sem_gate > 0 && (millis() - last_gate) > gate_length) { + sem_gate--; + for (i=0; ikboard_s[c]) playNOTE(i, !current->kboard_s[c]); + for (i=0; idpad_s[c]) playDrum(i, !current->dpad_s[c]); + } + dpadhit = LOW; + for (int cButton = 0; cButton < MAXDPAD; cButton++) { + dpad[cButton] = evalButton(bCap[cButton], dpad[cButton], cButton); + dpadhit = (dpad[cButton] || dpadhit) + } + + npressed = 0; + for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) { + digitalWrite(OCTAVE[cOCTAVE], HIGH); + npressed += eval(scan(cOCTAVE)); + digitalWrite(OCTAVE[cOCTAVE], LOW); + } + + overwrite = digitalRead(OW); + if (overwrite) { + if (npressed > 0) current->kboard_s = kboard + if (dpadhit) current->dpad_s = dpad } } +// Hardware specific functions -octst scan(int nOct) { // This function reads the 12 note pins and returns a struct - int c; // with 1 bool for each note +octst scan(int nOct) { // This function reads the 12 NOTE pins and returns a struct + int c; // with 1 bool for each NOTE octst output; output.nOct = nOct; for (c = 0; c < 12; c++) { - output.stat[c] = digitalRead(note[c]); + output.stat[c] = digitalRead(NOTE[c]); } return output; } -int eval(octst input) { - int pressed = 0; - int snote = input.nOct * 12; - - for (int c = 0; c < 12; c++) { - if (input.stat[c] ^ kboard[c + snote]) { - if (raw) playNote(c + snote, input.stat[c]); - kboard[c + snote] = input.stat[c]; - } - if (kboard[c + snote] == HIGH) pressed++; - } - return pressed; -} - -void playNote(int c, bool status) { - byte n = c + noteOffset; - if (status == HIGH) { - MIDI.sendNoteOn(n, velocity, channel); - } - else if (status == LOW) { - MIDI.sendNoteOff(n, velocity, channel); - } -} - -bool evalButton(CapacitiveSensor* b, bool value, byte note) { +bool evalButton(CapacitiveSensor* b, bool value, int note_number) { long sensor = b->capacitiveSensor(1); if (sensor > 15) { if (value) return HIGH; else { - MIDI.sendNoteOn(note, velocity, (byte)7); + playDrum(note_number, HIGH); return HIGH; } } else { if (!value) return LOW; else { - MIDI.sendNoteOff(note, velocity, (byte)7); + playDrum(note_number, LOW); return LOW; } } } +// NOTE Functions + +int eval(octst input) { + int pressed = 0; + int sNOTE = input.nOct * 12; + + for (int c = 0; c < 12; c++) { + if (input.stat[c] ^ kboard[c + sNOTE]) { + playNOTE(c + sNOTE, input.stat[c]); + kboard[c + sNOTE] = input.stat[c]; + } + if (kboard[c + sNOTE] == HIGH) pressed++; + } + return pressed; +} + +void playNOTE(int c, bool status) { + byte n = c + NOTEOffset; + if (status == HIGH) { + MIDI.sendNOTEOn(n, velocity, channel); + } + else if (status == LOW) { + MIDI.sendNOTEOff(n, velocity, channel); + } +} + +void playDrum(int c, bool status) { + byte n = c + drumOffset; + if (status == HIGH) { + MIDI.sendNOTEOn(n, velocity, (byte)7); + } + else if (status == LOW) { + MIDI.sendNOTEOff(n, velocity, (byte)7); + } +} + +// Sync functions + void sync() { if (Serial.available() && Serial.read() == MIDICLOCK) { midiclock++; - if (midiclock == 11 && semA == 0) semA++; - else if (midiclock == 5 && semB == 0) semB++; - else if (midiclock == 12) midiclock = 0; + if (midiclock == 0 && sem_beat == 0) sem_beat++; + else if (midiclock == 24) midiclock = 0; } } +// List management functions + link newStep() { return (link)malloc(sizeof(struct SequencerStep)); } -bool insertStep(int note) { +bool insertStep() { link newS = newStep(); if (newS == NULL) { free(newS); return LOW; } - newS->note = note; + newS->kboard_s = kboard; + newS->dpad_s = dpad + if (nstep == 0) { newS->next = newS; current = newS; @@ -223,24 +255,4 @@ bool deleteStep() { } nstep--; return HIGH; -} - -void checkInsert() { - if (npressed < 1) insertStep(-1); - else { - arp++; - while (!kboard[arp]) { - arp++; - if (arp == 49) arp = 0; - } - insertStep(arp); - } -} -void checkReplace() { - arp++; - while (!kboard[arp]) { - arp++; - if (arp == 49) arp = 0; - } - current->note = arp; } \ No newline at end of file From d1c6f9f8e952afeca92dbb1590cea2af6b7361e1 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 28 Jun 2019 00:14:44 +0200 Subject: [PATCH 16/30] Fixed insertion and deletion --- cvkeyboard.ino | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 769cdfb..eb7e8bc 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -36,9 +36,10 @@ int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons 6, 3, 17 }; int OW = 2; // Pin used for overwrite switch int DEL = -1; // Pin used for delete button +int ADD = 14; // Pin used for add button // GLOBAL SETTINGS -bool overwrite; // Step content is overwritten with pressed keys +//bool overwrite; // Step content is overwritten with pressed keys, could not be needed // PLACEHOLDERS byte velocity = 100; // @@ -95,6 +96,8 @@ void loop() { for (i=0; ikboard_s[c]) playNOTE(i, !current->kboard_s[c]); for (i=0; idpad_s[c]) playDrum(i, !current->dpad_s[c]); } + if (digitalRead(ADD) && digitalRead(OW)) insertStep(); + if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button nextStep(); if (current != NULL) { // Play all step notes and begin counting for gate for (i=0; ikboard_s[c]) playNOTE(i, current->kboard_s[c]); @@ -120,9 +123,8 @@ void loop() { npressed += eval(scan(cOCTAVE)); digitalWrite(OCTAVE[cOCTAVE], LOW); } - - overwrite = digitalRead(OW); - if (overwrite) { + + if (digitalRead(OW)) { if (npressed > 0) current->kboard_s = kboard if (dpadhit) current->dpad_s = dpad } From 42f1b65f37e17a4d47fe62bff947705a08374cb3 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 28 Jun 2019 01:03:17 +0200 Subject: [PATCH 17/30] Updated gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 01d8aed..39b0eb5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,10 @@ /.vs /CppProperties.json +.vscode/arduino.json +.vscode/c_cpp_properties.json +.vscode/settings.json +.vscode/ipch/f3a41dbca76912c5/mmap_address.bin +.vscode/ipch/f3a41dbca76912c5/WIRING_DIGITAL.ipch +.vscode/ipch/a05e84ed92aede25/mmap_address.bin +.vscode/ipch/2a8fc4e60d41b99f/mmap_address.bin From b7ddcb2a9781303958193434a606fb9d41ce565d Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 28 Jun 2019 01:03:40 +0200 Subject: [PATCH 18/30] Many syntax fixes --- cvkeyboard.ino | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index eb7e8bc..94556cc 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -1,3 +1,7 @@ +#include +#include +#include + #define NOTEOffset 36 #define drumOffset 60 #define MINUTE 60000 @@ -5,12 +9,8 @@ #define MAXKEYS 48 #define MAXDPAD 3 -#include -#include -#include - MIDI_CREATE_DEFAULT_INSTANCE(); - + typedef struct SequencerStep* link; typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each bool is for 1 NOTE @@ -19,9 +19,8 @@ typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each } octst; typedef struct SequencerStep { - int NOTE; - bool kboard_s[MAXKEYS] - bool dpad_s[MAXDPAD] + bool kboard_s[MAXKEYS]; + bool dpad_s[MAXDPAD]; link next; } step; @@ -84,7 +83,8 @@ void setup() { MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); - pinMode(2, INPUT_PULLUP); // Used for overwrite switch + pinMode(OW, INPUT_PULLUP); // Used for overwrite switch + pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch } void loop() { @@ -93,28 +93,28 @@ void loop() { sem_beat--; if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step sem_gate--; - for (i=0; ikboard_s[c]) playNOTE(i, !current->kboard_s[c]); - for (i=0; idpad_s[c]) playDrum(i, !current->dpad_s[c]); + for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); + for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); } if (digitalRead(ADD) && digitalRead(OW)) insertStep(); if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button nextStep(); if (current != NULL) { // Play all step notes and begin counting for gate - for (i=0; ikboard_s[c]) playNOTE(i, current->kboard_s[c]); - for (i=0; idpad_s[c]) playDrum(i, current->dpad_s[c]); + for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, current->kboard_s[i]); + for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, current->dpad_s[i]); last_gate = millis(); sem_gate++; } } if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; - for (i=0; ikboard_s[c]) playNOTE(i, !current->kboard_s[c]); - for (i=0; idpad_s[c]) playDrum(i, !current->dpad_s[c]); + for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); + for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); } dpadhit = LOW; for (int cButton = 0; cButton < MAXDPAD; cButton++) { dpad[cButton] = evalButton(bCap[cButton], dpad[cButton], cButton); - dpadhit = (dpad[cButton] || dpadhit) + dpadhit = (dpad[cButton] || dpadhit); } npressed = 0; @@ -125,8 +125,8 @@ void loop() { } if (digitalRead(OW)) { - if (npressed > 0) current->kboard_s = kboard - if (dpadhit) current->dpad_s = dpad + if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current->kboard_s[i] = kboard[i]; + if (dpadhit) for (int i = 0; i < MAXDPAD; i++) current->dpad_s[i] = dpad[i]; } } @@ -182,20 +182,20 @@ int eval(octst input) { void playNOTE(int c, bool status) { byte n = c + NOTEOffset; if (status == HIGH) { - MIDI.sendNOTEOn(n, velocity, channel); + MIDI.sendNoteOn(n, velocity, channel); } else if (status == LOW) { - MIDI.sendNOTEOff(n, velocity, channel); + MIDI.sendNoteOff(n, velocity, channel); } } void playDrum(int c, bool status) { byte n = c + drumOffset; if (status == HIGH) { - MIDI.sendNOTEOn(n, velocity, (byte)7); + MIDI.sendNoteOn(n, velocity, (byte)7); } else if (status == LOW) { - MIDI.sendNOTEOff(n, velocity, (byte)7); + MIDI.sendNoteOff(n, velocity, (byte)7); } } @@ -222,8 +222,8 @@ bool insertStep() { return LOW; } - newS->kboard_s = kboard; - newS->dpad_s = dpad + for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = kboard[i]; + for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = dpad[i]; if (nstep == 0) { newS->next = newS; From 35950fd11b856578c262de86e52acdc80282854e Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 28 Jun 2019 01:48:24 +0200 Subject: [PATCH 19/30] Various fixes that didn't fix much --- cvkeyboard.ino | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 94556cc..30f672d 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -33,6 +33,8 @@ int SEND[3] = { // Pins used as sender for capacitive touch buttons 5, 4, 16 }; int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons 6, 3, 17 }; +int LEDS[4] = { // Pins used for leds + 21, 20, 19, 18 }; int OW = 2; // Pin used for overwrite switch int DEL = -1; // Pin used for delete button int ADD = 14; // Pin used for add button @@ -65,12 +67,9 @@ CapacitiveSensor* bCap[MAXDPAD]; void setup() { - for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) { - pinMode(OCTAVE[cOCTAVE], OUTPUT); - } - for (int cNOTE = 0; cNOTE < 12; cNOTE++) { - pinMode(NOTE[cNOTE], INPUT); - } + for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); + for (int cNOTE = 0; cNOTE < 12; cNOTE++) pinMode(NOTE[cNOTE], INPUT); + for (int cLED = 0; cLED < 4; cLED++) pinMode(LEDS[cLED], OUTPUT); for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration @@ -89,6 +88,10 @@ void setup() { void loop() { sync(); + if (current == head) nstep = 0; + else nstep++; + display(nstep); + if (sem_beat > 0) { sem_beat--; if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step @@ -96,8 +99,8 @@ void loop() { for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); } - if (digitalRead(ADD) && digitalRead(OW)) insertStep(); - if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button + if (digitalRead(ADD)) insertStep(); + //if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button nextStep(); if (current != NULL) { // Play all step notes and begin counting for gate for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, current->kboard_s[i]); @@ -144,6 +147,13 @@ octst scan(int nOct) { // This function reads the 12 NOTE pins and retu return output; } +void display(int number){ + for(int i = 0; i < 4; i++) { + digitalWrite(LEDS[i], number & 1); + number >> 1; + } +} + bool evalButton(CapacitiveSensor* b, bool value, int note_number) { long sensor = b->capacitiveSensor(1); @@ -203,9 +213,12 @@ void playDrum(int c, bool status) { void sync() { if (Serial.available() && Serial.read() == MIDICLOCK) { + //sem_beat++; midiclock++; - if (midiclock == 0 && sem_beat == 0) sem_beat++; - else if (midiclock == 24) midiclock = 0; + if (midiclock == 24){ + midiclock = 0; + sem_beat++; + } } } From 68d2631f335722b5f738d97cfa12e2ab85a737fc Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 29 Jun 2019 02:42:38 +0200 Subject: [PATCH 20/30] Various improvements and debug leftovers --- cvkeyboard.ino | 95 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 30f672d..bd6b90c 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -2,12 +2,15 @@ #include #include +#define BPQN 24 // Ableton sends 24, VCV rack only one, by standard should be 24? + #define NOTEOffset 36 #define drumOffset 60 #define MINUTE 60000 #define MIDICLOCK 0xf8 #define MAXKEYS 48 #define MAXDPAD 3 +#define MAXSTEP 16 MIDI_CREATE_DEFAULT_INSTANCE(); @@ -21,6 +24,7 @@ typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each typedef struct SequencerStep { bool kboard_s[MAXKEYS]; bool dpad_s[MAXDPAD]; + unsigned short stepnumber; link next; } step; @@ -49,16 +53,18 @@ int bpm = 360; // // SEQUENCER POINTERS -link head, tail, current; +link head = NULL; +link current = NULL; // SYSTEM VARIABLES -int nstep = 0; // Keeps track of the sequencer steps +unsigned short nstep = 0; // Keeps track of the sequencer steps int arp = 0; // Keeps track of last played NOTE if arpeggiating int midiclock = 0; // Used to sync with MIDI clock +bool add_step = LOW; // This is used to remember the addition of a step int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing unsigned long last_gate = 0; // Gate start time for last sequencer step -unsigned long gate_length = 200; // ms of keypress if arpeggiator +unsigned long gate_length = 500; // ms of keypress if arpeggiator bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed bool kboard[MAXKEYS]; // Last status of keyboard @@ -73,7 +79,7 @@ void setup() { for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration - bCap[cButton]->set_CS_Timeout_Millis(200); // Timeout set to 200ms (instead of 2s) + bCap[cButton]->set_CS_Timeout_Millis(10); // Timeout set to 20ms (instead of 2s) dpad[cButton] = LOW; // Button starts LOW } @@ -84,36 +90,63 @@ void setup() { pinMode(OW, INPUT_PULLUP); // Used for overwrite switch pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch + + for (int i = 0; i < 16; i++) { // Boot up fancyness! + display(i); + delay(200); + } + + display(nstep); } void loop() { + // Serial.println(midiclock); + // Serial.print("Start | "); + // Serial.print(millis()); + // Serial.print('\n'); sync(); - if (current == head) nstep = 0; - else nstep++; - display(nstep); + add_step = (add_step || !digitalRead(ADD)); + // Serial.print("SPEPS DONE | "); + // Serial.print(millis()); + // Serial.print('\n'); if (sem_beat > 0) { sem_beat--; + if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step sem_gate--; for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); } - if (digitalRead(ADD)) insertStep(); + if (add_step) { + add_step = LOW; + if (nstep < MAXSTEP) insertStep(); + } //if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button + nextStep(); + display(current->stepnumber); + if (current != NULL) { // Play all step notes and begin counting for gate for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, current->kboard_s[i]); for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, current->dpad_s[i]); last_gate = millis(); sem_gate++; } + // Serial.print("BEAT ELABORATED AND PLAYED | "); + // Serial.print(millis()); + // Serial.print('\n'); } + if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); + // Serial.print("GATE FINISHED | "); + // Serial.print(millis()); + // Serial.print('\n'); } + dpadhit = LOW; for (int cButton = 0; cButton < MAXDPAD; cButton++) { dpad[cButton] = evalButton(bCap[cButton], dpad[cButton], cButton); @@ -126,10 +159,16 @@ void loop() { npressed += eval(scan(cOCTAVE)); digitalWrite(OCTAVE[cOCTAVE], LOW); } + // Serial.print("READ KEYBOARD | "); + // Serial.print(millis()); + // Serial.print('\n'); if (digitalRead(OW)) { if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current->kboard_s[i] = kboard[i]; if (dpadhit) for (int i = 0; i < MAXDPAD; i++) current->dpad_s[i] = dpad[i]; + // Serial.print("OVERWRITTEN STUFF | "); + // Serial.print(millis()); + // Serial.print('\n'); } } @@ -149,13 +188,14 @@ octst scan(int nOct) { // This function reads the 12 NOTE pins and retu void display(int number){ for(int i = 0; i < 4; i++) { - digitalWrite(LEDS[i], number & 1); - number >> 1; - } + digitalWrite(LEDS[i], number & (unsigned short) 1); + number = number >> 1; + } } bool evalButton(CapacitiveSensor* b, bool value, int note_number) { long sensor = b->capacitiveSensor(1); + // Serial.println(sensor); if (sensor > 15) { if (value) return HIGH; @@ -212,12 +252,14 @@ void playDrum(int c, bool status) { // Sync functions void sync() { - if (Serial.available() && Serial.read() == MIDICLOCK) { - //sem_beat++; - midiclock++; - if (midiclock == 24){ - midiclock = 0; - sem_beat++; + if (Serial.available()) { + if (Serial.read() == MIDICLOCK) { + //sem_beat++; + midiclock++; + if (midiclock == BPQN){ + midiclock = 0; + sem_beat++; + } } } } @@ -230,28 +272,35 @@ link newStep() { bool insertStep() { link newS = newStep(); + link buffer; if (newS == NULL) { free(newS); return LOW; } - for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = kboard[i]; - for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = dpad[i]; + for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = LOW; + for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = LOW; - if (nstep == 0) { + if (current == NULL) { newS->next = newS; + newS->stepnumber = (unsigned short) 0; current = newS; head = newS; + nstep = 1; } else { - newS->next = current->next; - current->next = newS; + newS->stepnumber = nstep; + buffer = current; + while (buffer->next != head) buffer = buffer->next; + buffer->next = newS; + newS->next = head; + nstep++; } - nstep++; return HIGH; } void nextStep() { + if (current == NULL) return; current = current->next; } From 6b2faa76a7c2d432bdeed56e231c38a6fa8afaaf Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 30 Jun 2019 00:39:01 +0200 Subject: [PATCH 21/30] Initial channel implementation --- cvkeyboard.ino | 203 +++++++++++++++++++++++++++++++------------------ 1 file changed, 129 insertions(+), 74 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index bd6b90c..210b92d 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -5,6 +5,7 @@ #define BPQN 24 // Ableton sends 24, VCV rack only one, by standard should be 24? #define NOTEOffset 36 +#define DRUMSHIFT 6 #define drumOffset 60 #define MINUTE 60000 #define MIDICLOCK 0xf8 @@ -22,6 +23,7 @@ typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each } octst; typedef struct SequencerStep { + bool clean = LOW; bool kboard_s[MAXKEYS]; bool dpad_s[MAXDPAD]; unsigned short stepnumber; @@ -36,7 +38,7 @@ int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar int SEND[3] = { // Pins used as sender for capacitive touch buttons 5, 4, 16 }; int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons - 6, 3, 17 }; + 6, 1, 17 }; int LEDS[4] = { // Pins used for leds 21, 20, 19, 18 }; int OW = 2; // Pin used for overwrite switch @@ -48,23 +50,27 @@ int ADD = 14; // Pin used for add button // PLACEHOLDERS byte velocity = 100; // -byte channel = 1; // int bpm = 360; // - // SEQUENCER POINTERS -link head = NULL; -link current = NULL; + // SEQUENCER POINTERS AND RELATED ARRAYS +link head[6]; +link current[6]; +link previous; +unsigned short nstep[6]; // Keeps track of the sequencer steps +bool mute[6]; +byte channel; // Current selected channel. Drums are shifted of DRUMSHIFT channels (so channels can only be 6) // SYSTEM VARIABLES -unsigned short nstep = 0; // Keeps track of the sequencer steps int arp = 0; // Keeps track of last played NOTE if arpeggiating int midiclock = 0; // Used to sync with MIDI clock bool add_step = LOW; // This is used to remember the addition of a step +bool del_step = LOW; // This is used to remember the deletion of a step +bool chan_up = LOW; // Only for now because I have few buttons :C int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing unsigned long last_gate = 0; // Gate start time for last sequencer step -unsigned long gate_length = 500; // ms of keypress if arpeggiator +unsigned long gate_length = 100; // ms of keypress if arpeggiator bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed bool kboard[MAXKEYS]; // Last status of keyboard @@ -79,7 +85,7 @@ void setup() { for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration - bCap[cButton]->set_CS_Timeout_Millis(10); // Timeout set to 20ms (instead of 2s) + bCap[cButton]->set_CS_Timeout_Millis(1); // Timeout set to 20ms (instead of 2s) dpad[cButton] = LOW; // Button starts LOW } @@ -91,60 +97,86 @@ void setup() { pinMode(OW, INPUT_PULLUP); // Used for overwrite switch pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch + for (int i = 0, i < 6, i++){ + current[i] = NULL; + head[i] = NULL; + nstep[i] = 0; + mute[i] = LOW; + } + channel = 1; + for (int i = 0; i < 16; i++) { // Boot up fancyness! display(i); delay(200); } - display(nstep); + // ONLY FOR DEBUG + for (int i=0; i<16, i++){ + for (byte chan=1; chan <= 6; chan++) insertStep(chan); + } + + + display(0); } void loop() { - // Serial.println(midiclock); - // Serial.print("Start | "); - // Serial.print(millis()); - // Serial.print('\n'); sync(); - add_step = (add_step || !digitalRead(ADD)); - // Serial.print("SPEPS DONE | "); - // Serial.print(millis()); - // Serial.print('\n'); + // add_step = (add_step || !digitalRead(ADD)); + // del_step = (del_step || !digitalRead(DEL)); + chan_up = (chan_up || !digitalRead(ADD)) if (sem_beat > 0) { sem_beat--; if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step sem_gate--; - for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); - for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); + for (byte chan = 1; chan <= 6; chan++) { + if (mute[chan]) continue; + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], chan); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], chan); + } } - if (add_step) { + + if (add_step && !del_step) { add_step = LOW; - if (nstep < MAXSTEP) insertStep(); + if (nstep[channel-1] < MAXSTEP) insertStep(channel-1); + } + if (del_step && !add_step) { + del_step = LOW; + if (nstep[channel-1] < MAXSTEP) deleteStep(channel-1); + } + if (add_step && del_step) { + add_step = LOW; + del_step = LOW; + } + + // ONLY FOR NOW because I don't have enough buttons :C + if (chan_up) { + chan_up = LOW; + channel++; + if (channel > 6) channel = (byte) 1; } - //if (digitalRead(ADD) && !digitalRead(OW)) deleteStep(); // Placeholder because I miss a button nextStep(); - display(current->stepnumber); - - if (current != NULL) { // Play all step notes and begin counting for gate - for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, current->kboard_s[i]); - for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, current->dpad_s[i]); - last_gate = millis(); - sem_gate++; + display(current[channel-1]->stepnumber); + for (byte chan = 1; chan <= 6; chan++) { + if (mute[chan]) continue; + if (current[chan] != NULL) { // Play all step notes and begin counting for gate + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, current[chan]->kboard_s[i]); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i]); + } } - // Serial.print("BEAT ELABORATED AND PLAYED | "); - // Serial.print(millis()); - // Serial.print('\n'); + last_gate = millis(); + sem_gate++; } if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; - for (int i = 0; i < MAXKEYS; i++) if (current->kboard_s[i]) playNOTE(i, !current->kboard_s[i]); - for (int i = 0; i < MAXDPAD; i++) if (current->dpad_s[i]) playDrum(i, !current->dpad_s[i]); - // Serial.print("GATE FINISHED | "); - // Serial.print(millis()); - // Serial.print('\n'); + for (byte chan = 1; chan <= 6; chan++) { + if (mute[chan]) continue; + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], chan); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], chan); + } } dpadhit = LOW; @@ -159,16 +191,11 @@ void loop() { npressed += eval(scan(cOCTAVE)); digitalWrite(OCTAVE[cOCTAVE], LOW); } - // Serial.print("READ KEYBOARD | "); - // Serial.print(millis()); - // Serial.print('\n'); if (digitalRead(OW)) { - if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current->kboard_s[i] = kboard[i]; - if (dpadhit) for (int i = 0; i < MAXDPAD; i++) current->dpad_s[i] = dpad[i]; - // Serial.print("OVERWRITTEN STUFF | "); - // Serial.print(millis()); - // Serial.print('\n'); + if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current[channel-1]->kboard_s[i] = kboard[i]; + if (dpadhit) for (int i = 0; i < MAXDPAD; i++) current[channel-1]->dpad_s[i] = dpad[i]; + current[channel-1]->clean = LOW; } } @@ -200,14 +227,14 @@ bool evalButton(CapacitiveSensor* b, bool value, int note_number) { if (sensor > 15) { if (value) return HIGH; else { - playDrum(note_number, HIGH); + playDrum(note_number, HIGH, channel); return HIGH; } } else { if (!value) return LOW; else { - playDrum(note_number, LOW); + playDrum(note_number, LOW, channel); return LOW; } } @@ -221,7 +248,7 @@ int eval(octst input) { for (int c = 0; c < 12; c++) { if (input.stat[c] ^ kboard[c + sNOTE]) { - playNOTE(c + sNOTE, input.stat[c]); + playNote(c + sNOTE, input.stat[c], channel); kboard[c + sNOTE] = input.stat[c]; } if (kboard[c + sNOTE] == HIGH) pressed++; @@ -229,23 +256,23 @@ int eval(octst input) { return pressed; } -void playNOTE(int c, bool status) { +void playNote(int c, bool status, byte chan) { byte n = c + NOTEOffset; if (status == HIGH) { - MIDI.sendNoteOn(n, velocity, channel); + MIDI.sendNoteOn(n, velocity, chan); } else if (status == LOW) { - MIDI.sendNoteOff(n, velocity, channel); + MIDI.sendNoteOff(n, velocity, chan); } } -void playDrum(int c, bool status) { +void playDrum(int c, bool status, byte chan) { byte n = c + drumOffset; if (status == HIGH) { - MIDI.sendNoteOn(n, velocity, (byte)7); + MIDI.sendNoteOn(n, velocity, (byte)(chan + DRUMSHIFT)); } else if (status == LOW) { - MIDI.sendNoteOff(n, velocity, (byte)7); + MIDI.sendNoteOff(n, velocity, (byte)(chan + DRUMSHIFT)); } } @@ -270,7 +297,7 @@ link newStep() { return (link)malloc(sizeof(struct SequencerStep)); } -bool insertStep() { +bool insertStep(byte chan) { link newS = newStep(); link buffer; if (newS == NULL) { @@ -281,42 +308,70 @@ bool insertStep() { for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = LOW; for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = LOW; - if (current == NULL) { + if (current[chan] == NULL) { newS->next = newS; newS->stepnumber = (unsigned short) 0; - current = newS; - head = newS; - nstep = 1; + current[chan] = newS; + head[chan] = newS; + nstep[chan] = 1; } else { newS->stepnumber = nstep; - buffer = current; - while (buffer->next != head) buffer = buffer->next; + buffer = current[chan]; + while (buffer->next != head[chan]) buffer = buffer->next; buffer->next = newS; - newS->next = head; - nstep++; + newS->next = head[chan]; + nstep[chan]++; } return HIGH; } void nextStep() { - if (current == NULL) return; - current = current->next; + for (byte chan=1; chan <= 6; chan++) { + if (current[chan] == NULL) continue; + current[chan] = current[chan]->next; + } } -bool deleteStep() { - if (nstep < 1) return LOW; +bool deleteStep(byte chan) { + if (nstep[chan] < 1) return LOW; - if (nstep == 1) { + if (!current[chan]->clean) { + for (int i = 0; i < MAXKEYS; i++) current[chan]->kboard_s[i] = LOW; + for (int i = 0; i < MAXDPAD; i++) current[chan]->dpad_s[i] = LOW; + current[chan]->clean = HIGH; + return LOW; + } + + if (nstep[chan] == 1) { free(current); - head = NULL; - current = NULL; + head[chan] = NULL; + current[chan] = NULL; } else { - link buffer = current->next->next; - free(current->next); - current->next = buffer; + link buffer = current[chan]; + while (buffer->next != current[chan]) buffer = buffer->next; + buffer->next = current[chan]->next; + if (current[chan] == head[chan]) { + head[chan] = head[chan]->next; + int i = 0 + buffer = head[chan] + do { + buffer->stepnumber = i; + buffer = buffer->next; + i++; + } while (buffer != head[chan]) + } + else { + buffer = buffer->next; + while (buffer != head[chan]) { + buffer->stepnumber--; + buffer = buffer->next; + } + } + free(current[chan]); + buffer = buffer->next; } - nstep--; + nstep[chan]--; return HIGH; } \ No newline at end of file From 3949ea64f07e4c1dbfe36b97d3b699404e1741bf Mon Sep 17 00:00:00 2001 From: alemi Date: Sun, 30 Jun 2019 02:55:50 +0200 Subject: [PATCH 22/30] Channels now work as intended --- cvkeyboard.ino | 59 +++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 210b92d..7ab7e08 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -97,13 +97,13 @@ void setup() { pinMode(OW, INPUT_PULLUP); // Used for overwrite switch pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch - for (int i = 0, i < 6, i++){ + for (int i = 0; i < 6; i++){ current[i] = NULL; head[i] = NULL; nstep[i] = 0; mute[i] = LOW; } - channel = 1; + channel = (byte) 1; for (int i = 0; i < 16; i++) { // Boot up fancyness! display(i); @@ -111,29 +111,26 @@ void setup() { } // ONLY FOR DEBUG - for (int i=0; i<16, i++){ - for (byte chan=1; chan <= 6; chan++) insertStep(chan); - } + for (int chan=1; chan <= 6; chan++) for (int i=0; i<16; i++) insertStep((byte) chan - 1); - - display(0); + display(10); } void loop() { sync(); // add_step = (add_step || !digitalRead(ADD)); // del_step = (del_step || !digitalRead(DEL)); - chan_up = (chan_up || !digitalRead(ADD)) + chan_up = (chan_up || !digitalRead(ADD)); if (sem_beat > 0) { sem_beat--; if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step sem_gate--; - for (byte chan = 1; chan <= 6; chan++) { + for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], chan); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], chan); + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); } } @@ -154,16 +151,16 @@ void loop() { if (chan_up) { chan_up = LOW; channel++; - if (channel > 6) channel = (byte) 1; + if (channel > 3) channel = (byte) 1; } nextStep(); display(current[channel-1]->stepnumber); - for (byte chan = 1; chan <= 6; chan++) { + for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; if (current[chan] != NULL) { // Play all step notes and begin counting for gate - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, current[chan]->kboard_s[i]); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i]); + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); } } last_gate = millis(); @@ -172,10 +169,10 @@ void loop() { if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; - for (byte chan = 1; chan <= 6; chan++) { + for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], chan); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], chan); + for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); } } @@ -269,10 +266,10 @@ void playNote(int c, bool status, byte chan) { void playDrum(int c, bool status, byte chan) { byte n = c + drumOffset; if (status == HIGH) { - MIDI.sendNoteOn(n, velocity, (byte)(chan + DRUMSHIFT)); + MIDI.sendNoteOn(n, velocity, chan + (byte) DRUMSHIFT); } else if (status == LOW) { - MIDI.sendNoteOff(n, velocity, (byte)(chan + DRUMSHIFT)); + MIDI.sendNoteOff(n, velocity, chan + (byte) DRUMSHIFT); } } @@ -300,15 +297,13 @@ link newStep() { bool insertStep(byte chan) { link newS = newStep(); link buffer; - if (newS == NULL) { - free(newS); - return LOW; - } + + if (newS == NULL) return LOW; for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = LOW; for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = LOW; - if (current[chan] == NULL) { + if (head[chan] == NULL) { newS->next = newS; newS->stepnumber = (unsigned short) 0; current[chan] = newS; @@ -316,7 +311,7 @@ bool insertStep(byte chan) { nstep[chan] = 1; } else { - newS->stepnumber = nstep; + newS->stepnumber = nstep[chan]; buffer = current[chan]; while (buffer->next != head[chan]) buffer = buffer->next; buffer->next = newS; @@ -327,8 +322,8 @@ bool insertStep(byte chan) { } void nextStep() { - for (byte chan=1; chan <= 6; chan++) { - if (current[chan] == NULL) continue; + for (int chan=0; chan < 6; chan++) { + if (head[chan] == NULL) continue; current[chan] = current[chan]->next; } } @@ -344,7 +339,7 @@ bool deleteStep(byte chan) { } if (nstep[chan] == 1) { - free(current); + free(current[chan]); head[chan] = NULL; current[chan] = NULL; } @@ -354,13 +349,13 @@ bool deleteStep(byte chan) { buffer->next = current[chan]->next; if (current[chan] == head[chan]) { head[chan] = head[chan]->next; - int i = 0 - buffer = head[chan] + int i = 0; + buffer = head[chan]; do { buffer->stepnumber = i; buffer = buffer->next; i++; - } while (buffer != head[chan]) + } while (buffer != head[chan]); } else { buffer = buffer->next; From 3a1368b8be72b8dca90f1b1182cf3f0b5488ba0a Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 4 Jul 2019 16:11:44 +0200 Subject: [PATCH 23/30] Various fixes. Unrolled single line loops. --- cvkeyboard.ino | 60 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 7ab7e08..2d2bed2 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -12,6 +12,7 @@ #define MAXKEYS 48 #define MAXDPAD 3 #define MAXSTEP 16 +#define NBITS 6 MIDI_CREATE_DEFAULT_INSTANCE(); @@ -39,8 +40,8 @@ int SEND[3] = { // Pins used as sender for capacitive touch buttons 5, 4, 16 }; int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons 6, 1, 17 }; -int LEDS[4] = { // Pins used for leds - 21, 20, 19, 18 }; +int LEDS[NBITS] = { // Pins used for leds + 15, 3, 21, 20, 19, 18 }; int OW = 2; // Pin used for overwrite switch int DEL = -1; // Pin used for delete button int ADD = 14; // Pin used for add button @@ -70,7 +71,7 @@ bool chan_up = LOW; // Only for now because I have few buttons :C int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing unsigned long last_gate = 0; // Gate start time for last sequencer step -unsigned long gate_length = 100; // ms of keypress if arpeggiator +unsigned long gate_length = 1000; // ms of keypress if arpeggiator bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed bool kboard[MAXKEYS]; // Last status of keyboard @@ -81,7 +82,7 @@ CapacitiveSensor* bCap[MAXDPAD]; void setup() { for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); for (int cNOTE = 0; cNOTE < 12; cNOTE++) pinMode(NOTE[cNOTE], INPUT); - for (int cLED = 0; cLED < 4; cLED++) pinMode(LEDS[cLED], OUTPUT); + for (int cLED = 0; cLED < NBITS; cLED++) pinMode(LEDS[cLED], OUTPUT); for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration @@ -92,7 +93,7 @@ void setup() { for (int cStat = 0; cStat < MAXKEYS; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW MIDI.begin(MIDI_CHANNEL_OFF); - Serial.begin(115200); + Serial.begin(115200); // Uncomment this if you use Hairless and set baud rate pinMode(OW, INPUT_PULLUP); // Used for overwrite switch pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch @@ -105,15 +106,17 @@ void setup() { } channel = (byte) 1; - for (int i = 0; i < 16; i++) { // Boot up fancyness! + for (int i = 0; i < 64; i++) { // Boot up fancyness! NBITS*NBITS display(i); - delay(200); + delay(50); } // ONLY FOR DEBUG - for (int chan=1; chan <= 6; chan++) for (int i=0; i<16; i++) insertStep((byte) chan - 1); + for (int i=0; i<64; i++) insertStep((byte) 0); + for (int i=0; i<32; i++) insertStep((byte) 1); + for (int i=0; i<16; i++) insertStep((byte) 2); - display(10); + display(0); } void loop() { @@ -125,12 +128,16 @@ void loop() { if (sem_beat > 0) { sem_beat--; - if (sem_gate > 0) { // If step was shorter than gate, close all open notes before next step + if (sem_gate > 0) { // If step was shorter than GATE, close all open notes before next step sem_gate--; for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); + for (int i = 0; i < MAXKEYS; i++) + if (current[chan]->kboard_s[i] && !kboard[i] && !current[chan]->next->kboard_s[i]) + playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) + if (current[chan]->dpad_s[i] && !dpad[i]) + playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); } } @@ -154,13 +161,18 @@ void loop() { if (channel > 3) channel = (byte) 1; } - nextStep(); + nextStep(); // ALL STEPS INCREMENTED display(current[channel-1]->stepnumber); for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - if (current[chan] != NULL) { // Play all step notes and begin counting for gate - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, current[chan]->kboard_s[i], (byte) chan+1); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); + if (current[chan] != NULL) { // PLAY all step notes in all unmuted channels + if (npressed < 1) // If the user is currently playing this step no note will play to avoid overruling (if monophonic) + for (int i = 0; i < MAXKEYS; i++) + if (current[chan]->kboard_s[i]) + playNote(i, current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) // Drums are played nonetheless because drums already layered won't overrule + if (current[chan]->dpad_s[i] && !dpad[i]) + playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); } } last_gate = millis(); @@ -171,8 +183,12 @@ void loop() { sem_gate--; for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) if (current[chan]->kboard_s[i] && !kboard[i]) playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); - for (int i = 0; i < MAXDPAD; i++) if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); + for (int i = 0; i < MAXKEYS; i++) + if (current[chan]->kboard_s[i] && !kboard[i]) + playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) + if (current[chan]->dpad_s[i] && !dpad[i]) + playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); } } @@ -190,8 +206,10 @@ void loop() { } if (digitalRead(OW)) { - if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current[channel-1]->kboard_s[i] = kboard[i]; - if (dpadhit) for (int i = 0; i < MAXDPAD; i++) current[channel-1]->dpad_s[i] = dpad[i]; + if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) + current[channel-1]->kboard_s[i] = kboard[i]; + if (dpadhit) for (int i = 0; i < MAXDPAD; i++) + current[channel-1]->dpad_s[i] = dpad[i] || current[channel-1]->dpad_s[i]; // Drum hits aren't exclusive! current[channel-1]->clean = LOW; } } @@ -211,7 +229,7 @@ octst scan(int nOct) { // This function reads the 12 NOTE pins and retu } void display(int number){ - for(int i = 0; i < 4; i++) { + for(int i = 0; i < NBITS; i++) { digitalWrite(LEDS[i], number & (unsigned short) 1); number = number >> 1; } From c42b016f583b4c80ae7f733e58fc3ea581a2bfc1 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 6 Jul 2019 03:39:35 +0200 Subject: [PATCH 24/30] Switched to adafruit MPR121. Only the necessary channel is muted when user plays. Various bugfixes. Removed debug code. Added idle animation. --- cvkeyboard.ino | 106 +++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 66 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 2d2bed2..fbaa4a3 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -1,6 +1,7 @@ -#include #include #include +#include +#include #define BPQN 24 // Ableton sends 24, VCV rack only one, by standard should be 24? @@ -36,18 +37,17 @@ int NOTE[12] = { // Pins used to read each note (C is 0, B is 11) 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 }; int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar 12, 9, 8, 10 }; -int SEND[3] = { // Pins used as sender for capacitive touch buttons - 5, 4, 16 }; -int RECEIVE[3] = { // Pins used as receiver for capacitive touch buttons - 6, 1, 17 }; int LEDS[NBITS] = { // Pins used for leds - 15, 3, 21, 20, 19, 18 }; + 14, 15, 16, 17, 18, 19 }; int OW = 2; // Pin used for overwrite switch -int DEL = -1; // Pin used for delete button -int ADD = 14; // Pin used for add button +int DEL = 11; // Capacitive button used for DELETE button +int PLUS = 10; // Capacitive button used for PLUS button +int MINUS = 9; // Capacitive button used for MINUS button // GLOBAL SETTINGS //bool overwrite; // Step content is overwritten with pressed keys, could not be needed +int pentathonic[10] = { // Used to quantize drum notes + 0, 2, 5, 7, 9, 12, 14, 17, 19, 21 }; // PLACEHOLDERS byte velocity = 100; // @@ -76,28 +76,24 @@ bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is tr int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed bool kboard[MAXKEYS]; // Last status of keyboard bool dpad[MAXDPAD]; // Last status of Capacitive Buttons -CapacitiveSensor* bCap[MAXDPAD]; +int cap_read = 0; +Adafruit_MPR121 cap = Adafruit_MPR121(); void setup() { + display(1); for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); for (int cNOTE = 0; cNOTE < 12; cNOTE++) pinMode(NOTE[cNOTE], INPUT); for (int cLED = 0; cLED < NBITS; cLED++) pinMode(LEDS[cLED], OUTPUT); - for (int cButton = 0; cButton < MAXDPAD; cButton++) { // Capacitive Buttons configuration - bCap[cButton] = new CapacitiveSensor(SEND[cButton], RECEIVE[cButton]); // Initialized - bCap[cButton]->set_CS_AutocaL_Millis(0xFFFFFFFF); // No recalibration - bCap[cButton]->set_CS_Timeout_Millis(1); // Timeout set to 20ms (instead of 2s) - dpad[cButton] = LOW; // Button starts LOW - } - + display(3); + while (!cap.begin(0x5A)) delay(10); // If MPR121 is not ready, wait for it + display(7); for (int cStat = 0; cStat < MAXKEYS; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW - + display(15); MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); // Uncomment this if you use Hairless and set baud rate - pinMode(OW, INPUT_PULLUP); // Used for overwrite switch - pinMode(ADD, INPUT_PULLUP); // Used for overwrite switch - + display(31); for (int i = 0; i < 6; i++){ current[i] = NULL; head[i] = NULL; @@ -105,25 +101,15 @@ void setup() { mute[i] = LOW; } channel = (byte) 1; - - for (int i = 0; i < 64; i++) { // Boot up fancyness! NBITS*NBITS - display(i); - delay(50); - } - - // ONLY FOR DEBUG - for (int i=0; i<64; i++) insertStep((byte) 0); - for (int i=0; i<32; i++) insertStep((byte) 1); - for (int i=0; i<16; i++) insertStep((byte) 2); - - display(0); + display(63); } void loop() { sync(); - // add_step = (add_step || !digitalRead(ADD)); - // del_step = (del_step || !digitalRead(DEL)); - chan_up = (chan_up || !digitalRead(ADD)); + + if (current[channel-1] == NULL) display(analogRead(channel)); + else display(current[channel-1]->stepnumber); + cap_read = cap.touched(); if (sem_beat > 0) { sem_beat--; @@ -165,11 +151,11 @@ void loop() { display(current[channel-1]->stepnumber); for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; + if (npressed > 0 && chan == (int) channel-1) continue; // If the user is playing in this channel no note should be played if (current[chan] != NULL) { // PLAY all step notes in all unmuted channels - if (npressed < 1) // If the user is currently playing this step no note will play to avoid overruling (if monophonic) - for (int i = 0; i < MAXKEYS; i++) - if (current[chan]->kboard_s[i]) - playNote(i, current[chan]->kboard_s[i], (byte) chan+1); + for (int i = 0; i < MAXKEYS; i++) + if (current[chan]->kboard_s[i] && !kboard[i]) + playNote(i, current[chan]->kboard_s[i], (byte) chan+1); for (int i = 0; i < MAXDPAD; i++) // Drums are played nonetheless because drums already layered won't overrule if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); @@ -194,7 +180,10 @@ void loop() { dpadhit = LOW; for (int cButton = 0; cButton < MAXDPAD; cButton++) { - dpad[cButton] = evalButton(bCap[cButton], dpad[cButton], cButton); + if (( 1 & (cap_read >> cButton)) ^ dpad[cButton]) { + dpad[cButton] = (bool) 1 & (cap_read >> cButton); + playDrum(cButton, dpad[cButton], channel); + } dpadhit = (dpad[cButton] || dpadhit); } @@ -235,26 +224,6 @@ void display(int number){ } } -bool evalButton(CapacitiveSensor* b, bool value, int note_number) { - long sensor = b->capacitiveSensor(1); - // Serial.println(sensor); - - if (sensor > 15) { - if (value) return HIGH; - else { - playDrum(note_number, HIGH, channel); - return HIGH; - } - } - else { - if (!value) return LOW; - else { - playDrum(note_number, LOW, channel); - return LOW; - } - } -} - // NOTE Functions int eval(octst input) { @@ -282,7 +251,8 @@ void playNote(int c, bool status, byte chan) { } void playDrum(int c, bool status, byte chan) { - byte n = c + drumOffset; + // The note is first quantized to a pentathonic and then scaled up to start at C4. + byte n = (byte) (pentathonic[c] + drumOffset); if (status == HIGH) { MIDI.sendNoteOn(n, velocity, chan + (byte) DRUMSHIFT); } @@ -313,6 +283,7 @@ link newStep() { } bool insertStep(byte chan) { + // Creates a new enpty step and places it as next step in the channel passed as argument link newS = newStep(); link buffer; @@ -329,12 +300,15 @@ bool insertStep(byte chan) { nstep[chan] = 1; } else { - newS->stepnumber = nstep[chan]; - buffer = current[chan]; - while (buffer->next != head[chan]) buffer = buffer->next; - buffer->next = newS; - newS->next = head[chan]; + newS->stepnumber = current[chan]->stepnumber +1; + buffer = current[chan]->next; + current[chan]->next = newS; + newS->next = buffer; nstep[chan]++; + while (buffer != head[chan]) { + buffer->stepnumber++; + buffer = buffer->next; + } } return HIGH; } From c175980952fc52bc6d48211c763be3beeeb2ec93 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 6 Jul 2019 04:09:00 +0200 Subject: [PATCH 25/30] Added code for PLUS-MINUS-CLEAR buttons --- cvkeyboard.ino | 102 ++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index fbaa4a3..2f82e20 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -52,6 +52,7 @@ int pentathonic[10] = { // Used to quantize drum notes // PLACEHOLDERS byte velocity = 100; // int bpm = 360; // +bool chan_up = LOW; // SEQUENCER POINTERS AND RELATED ARRAYS @@ -65,8 +66,9 @@ byte channel; // Current selected channel. Drums are shifted of DRUMSH // SYSTEM VARIABLES int arp = 0; // Keeps track of last played NOTE if arpeggiating int midiclock = 0; // Used to sync with MIDI clock -bool add_step = LOW; // This is used to remember the addition of a step -bool del_step = LOW; // This is used to remember the deletion of a step +bool plus_step = LOW; // This is used to remember the addition of a step +bool minus_step = LOW; // This is used to remember the deletion of a step +bool clear_step = LOW; // This is used to remember the clearing of a step bool chan_up = LOW; // Only for now because I have few buttons :C int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing @@ -109,7 +111,19 @@ void loop() { if (current[channel-1] == NULL) display(analogRead(channel)); else display(current[channel-1]->stepnumber); + cap_read = cap.touched(); + plus_step = plus_step || ((cap_read >> PLUS) & 1); + minus_step = minus_step || ((cap_read >> MINUS) & 1); + clear_step = clear_step || ((cap_read >> DEL) & 1); + + if (chan_up != (bool) ((cap_read >> 8) & 1)) { // Used to increase channel with a button because I don't have a rotary switch (yet!) + chan_up = (bool) ((cap_read >> 8) & 1); + if (chan_up == LOW) { + channel++; + if (channel > 3) channel = (byte) 1; + } + } if (sem_beat > 0) { sem_beat--; @@ -127,28 +141,29 @@ void loop() { } } - if (add_step && !del_step) { - add_step = LOW; + if (plus_step && minus_step) { + plus_step = LOW; + minus_step = LOW; + } + if (plus_step) { + plus_step = LOW; if (nstep[channel-1] < MAXSTEP) insertStep(channel-1); } - if (del_step && !add_step) { - del_step = LOW; - if (nstep[channel-1] < MAXSTEP) deleteStep(channel-1); - } - if (add_step && del_step) { - add_step = LOW; - del_step = LOW; + if (minus_step) { + minus_step = LOW; + if (nstep[channel-1] > 0) deleteStep(channel-1); } + if (clear_step) { + clear_step = LOW; + if (current[channel-1] != NULL) { + for (int i = 0; i < MAXKEYS; i++) current[channel-1]->kboard_s[i] = LOW; + for (int i = 0; i < MAXDPAD; i++) current[channel-1]->dpad_s[i] = LOW; + } + } - // ONLY FOR NOW because I don't have enough buttons :C - if (chan_up) { - chan_up = LOW; - channel++; - if (channel > 3) channel = (byte) 1; - } - - nextStep(); // ALL STEPS INCREMENTED + nextStep(); // ALL STEPS INCREMENTED display(current[channel-1]->stepnumber); + for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; if (npressed > 0 && chan == (int) channel-1) continue; // If the user is playing in this channel no note should be played @@ -323,42 +338,35 @@ void nextStep() { bool deleteStep(byte chan) { if (nstep[chan] < 1) return LOW; - if (!current[chan]->clean) { - for (int i = 0; i < MAXKEYS; i++) current[chan]->kboard_s[i] = LOW; - for (int i = 0; i < MAXDPAD; i++) current[chan]->dpad_s[i] = LOW; - current[chan]->clean = HIGH; - return LOW; - } - if (nstep[chan] == 1) { free(current[chan]); head[chan] = NULL; current[chan] = NULL; + return HIGH; + } + + link buffer = current[chan]; + while (buffer->next != current[chan]) buffer = buffer->next; + current[chan] = buffer; + buffer->next = current[chan]->next; + if (current[chan] == head[chan]) { + head[chan] = head[chan]->next; + int i = 0; + buffer = head[chan]; + do { + buffer->stepnumber = i; + buffer = buffer->next; + i++; + } while (buffer != head[chan]); } else { - link buffer = current[chan]; - while (buffer->next != current[chan]) buffer = buffer->next; - buffer->next = current[chan]->next; - if (current[chan] == head[chan]) { - head[chan] = head[chan]->next; - int i = 0; - buffer = head[chan]; - do { - buffer->stepnumber = i; - buffer = buffer->next; - i++; - } while (buffer != head[chan]); - } - else { - buffer = buffer->next; - while (buffer != head[chan]) { - buffer->stepnumber--; - buffer = buffer->next; - } - } - free(current[chan]); buffer = buffer->next; + while (buffer != head[chan]) { + buffer->stepnumber--; + buffer = buffer->next; + } } + free(current[chan]); nstep[chan]--; return HIGH; } \ No newline at end of file From 9792260a1dc88a0d31596d3d82e2e6cdbe93710e Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 8 Jul 2019 02:45:50 +0200 Subject: [PATCH 26/30] Many bugfixes related to hardware upgrade --- cvkeyboard.ino | 114 +++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 2f82e20..2a51c2c 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -12,8 +12,9 @@ #define MIDICLOCK 0xf8 #define MAXKEYS 48 #define MAXDPAD 3 -#define MAXSTEP 16 +#define MAXSTEP 64 #define NBITS 6 +#define DEBOUNCE 100 MIDI_CREATE_DEFAULT_INSTANCE(); @@ -38,22 +39,20 @@ int NOTE[12] = { // Pins used to read each note (C is 0, B is 11) int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar 12, 9, 8, 10 }; int LEDS[NBITS] = { // Pins used for leds - 14, 15, 16, 17, 18, 19 }; + 5, 4, 3, 14, 16, 18 }; int OW = 2; // Pin used for overwrite switch +int NEXT = 51; // Pin used for next step switch int DEL = 11; // Capacitive button used for DELETE button int PLUS = 10; // Capacitive button used for PLUS button int MINUS = 9; // Capacitive button used for MINUS button // GLOBAL SETTINGS -//bool overwrite; // Step content is overwritten with pressed keys, could not be needed int pentathonic[10] = { // Used to quantize drum notes 0, 2, 5, 7, 9, 12, 14, 17, 19, 21 }; // PLACEHOLDERS byte velocity = 100; // -int bpm = 360; // -bool chan_up = LOW; - +int bpm = 360; // // SEQUENCER POINTERS AND RELATED ARRAYS link head[6]; @@ -70,10 +69,12 @@ bool plus_step = LOW; // This is used to remember the addition of a step bool minus_step = LOW; // This is used to remember the deletion of a step bool clear_step = LOW; // This is used to remember the clearing of a step bool chan_up = LOW; // Only for now because I have few buttons :C +bool next_step = LOW; // Used to wait for a full switch cycle int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing unsigned long last_gate = 0; // Gate start time for last sequencer step unsigned long gate_length = 1000; // ms of keypress if arpeggiator +unsigned long last_next = 0; bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed bool kboard[MAXKEYS]; // Last status of keyboard @@ -83,19 +84,15 @@ int cap_read = 0; Adafruit_MPR121 cap = Adafruit_MPR121(); void setup() { - display(1); for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); for (int cNOTE = 0; cNOTE < 12; cNOTE++) pinMode(NOTE[cNOTE], INPUT); for (int cLED = 0; cLED < NBITS; cLED++) pinMode(LEDS[cLED], OUTPUT); - display(3); while (!cap.begin(0x5A)) delay(10); // If MPR121 is not ready, wait for it - display(7); for (int cStat = 0; cStat < MAXKEYS; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW - display(15); MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); // Uncomment this if you use Hairless and set baud rate pinMode(OW, INPUT_PULLUP); // Used for overwrite switch - display(31); + pinMode(NEXT, INPUT_PULLUP); // Used for overwrite switch for (int i = 0; i < 6; i++){ current[i] = NULL; head[i] = NULL; @@ -103,27 +100,37 @@ void setup() { mute[i] = LOW; } channel = (byte) 1; - display(63); + for (int i=0; i> 8) & 1) { // Only for now! + for (int i=0; istepnumber); - cap_read = cap.touched(); plus_step = plus_step || ((cap_read >> PLUS) & 1); minus_step = minus_step || ((cap_read >> MINUS) & 1); clear_step = clear_step || ((cap_read >> DEL) & 1); if (chan_up != (bool) ((cap_read >> 8) & 1)) { // Used to increase channel with a button because I don't have a rotary switch (yet!) - chan_up = (bool) ((cap_read >> 8) & 1); - if (chan_up == LOW) { - channel++; - if (channel > 3) channel = (byte) 1; - } + chan_up = (bool) ((cap_read >> 8) & 1); + if (chan_up == HIGH) { + channel++; + if (channel > 6) channel = (byte) 1; } + } if (sem_beat > 0) { sem_beat--; @@ -166,11 +173,11 @@ void loop() { for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; - if (npressed > 0 && chan == (int) channel-1) continue; // If the user is playing in this channel no note should be played if (current[chan] != NULL) { // PLAY all step notes in all unmuted channels - for (int i = 0; i < MAXKEYS; i++) - if (current[chan]->kboard_s[i] && !kboard[i]) - playNote(i, current[chan]->kboard_s[i], (byte) chan+1); + if (!(npressed > 0 && chan == (int) channel-1)) + for (int i = 0; i < MAXKEYS; i++) + if (current[chan]->kboard_s[i] && !kboard[i]) + playNote(i, current[chan]->kboard_s[i], (byte) chan+1); for (int i = 0; i < MAXDPAD; i++) // Drums are played nonetheless because drums already layered won't overrule if (current[chan]->dpad_s[i] && !dpad[i]) playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); @@ -209,7 +216,7 @@ void loop() { digitalWrite(OCTAVE[cOCTAVE], LOW); } - if (digitalRead(OW)) { + if (current[channel-1] != NULL && digitalRead(OW)) { if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) current[channel-1]->kboard_s[i] = kboard[i]; if (dpadhit) for (int i = 0; i < MAXDPAD; i++) @@ -279,6 +286,13 @@ void playDrum(int c, bool status, byte chan) { // Sync functions void sync() { + if (next_step != (bool) !digitalRead(NEXT)) { // Used to increase channel with a button because I don't have a rotary switch (yet!) + next_step = (bool) !digitalRead(NEXT); + if (millis() > last_next+DEBOUNCE && next_step == HIGH) { + last_next = millis(); + sem_beat++; + } + } if (Serial.available()) { if (Serial.read() == MIDICLOCK) { //sem_beat++; @@ -313,18 +327,19 @@ bool insertStep(byte chan) { current[chan] = newS; head[chan] = newS; nstep[chan] = 1; + return HIGH; } - else { - newS->stepnumber = current[chan]->stepnumber +1; - buffer = current[chan]->next; - current[chan]->next = newS; - newS->next = buffer; - nstep[chan]++; - while (buffer != head[chan]) { - buffer->stepnumber++; - buffer = buffer->next; - } + + newS->stepnumber = current[chan]->stepnumber +1; + buffer = current[chan]->next; + current[chan]->next = newS; + newS->next = buffer; + nstep[chan]++; + while (buffer != head[chan]) { + buffer->stepnumber++; + buffer = buffer->next; } + return HIGH; } @@ -346,27 +361,16 @@ bool deleteStep(byte chan) { } link buffer = current[chan]; - while (buffer->next != current[chan]) buffer = buffer->next; - current[chan] = buffer; - buffer->next = current[chan]->next; - if (current[chan] == head[chan]) { - head[chan] = head[chan]->next; - int i = 0; - buffer = head[chan]; - do { - buffer->stepnumber = i; - buffer = buffer->next; - i++; - } while (buffer != head[chan]); + while (buffer->next != current[chan]) buffer = buffer->next; // Search for previous step + buffer->next = current[chan]->next; // Skip step which is being deleted + if (current[chan] == head[chan]) head[chan] = head[chan]->next; // If deleting head, head moves forward + free(current[chan]); // Step is actually deleted + nstep[chan]--; // Decreased the counter + current[chan] = buffer; // Current step becomes previous step + buffer = buffer->next; // Skip the current step which was just before deleted step + while(buffer != head[chan]) { // If this is not the head, + buffer->stepnumber--; // decrease counter + buffer = buffer->next; // and move on } - else { - buffer = buffer->next; - while (buffer != head[chan]) { - buffer->stepnumber--; - buffer = buffer->next; - } - } - free(current[chan]); - nstep[chan]--; return HIGH; } \ No newline at end of file From 66fe2591af4ad92b9700562c324fd1c8afc48a87 Mon Sep 17 00:00:00 2001 From: alemi Date: Tue, 9 Jul 2019 21:21:15 +0200 Subject: [PATCH 27/30] Some fixes. Will need to reduce step size --- cvkeyboard.ino | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index 2a51c2c..ecf7ba8 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -11,7 +11,7 @@ #define MINUTE 60000 #define MIDICLOCK 0xf8 #define MAXKEYS 48 -#define MAXDPAD 3 +#define MAXDPAD 7 #define MAXSTEP 64 #define NBITS 6 #define DEBOUNCE 100 @@ -39,8 +39,8 @@ int NOTE[12] = { // Pins used to read each note (C is 0, B is 11) int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar 12, 9, 8, 10 }; int LEDS[NBITS] = { // Pins used for leds - 5, 4, 3, 14, 16, 18 }; -int OW = 2; // Pin used for overwrite switch + 5, 4, 2, 14, 16, 18 }; +int OW = 3; // Pin used for overwrite switch int NEXT = 51; // Pin used for next step switch int DEL = 11; // Capacitive button used for DELETE button int PLUS = 10; // Capacitive button used for PLUS button @@ -316,7 +316,11 @@ bool insertStep(byte chan) { link newS = newStep(); link buffer; - if (newS == NULL) return LOW; + if (newS == NULL) { + display(63); + delay(500); + return LOW; + } for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = LOW; for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = LOW; @@ -334,11 +338,19 @@ bool insertStep(byte chan) { buffer = current[chan]->next; current[chan]->next = newS; newS->next = buffer; - nstep[chan]++; - while (buffer != head[chan]) { - buffer->stepnumber++; + + int c = 0; + buffer = head[chan]; + + buffer->stepnumber = c; + c++; + buffer = buffer->next; + while(buffer != head[chan]) { + buffer->stepnumber = c; + c++; buffer = buffer->next; } + nstep[chan] = c; return HIGH; } @@ -365,12 +377,19 @@ bool deleteStep(byte chan) { buffer->next = current[chan]->next; // Skip step which is being deleted if (current[chan] == head[chan]) head[chan] = head[chan]->next; // If deleting head, head moves forward free(current[chan]); // Step is actually deleted - nstep[chan]--; // Decreased the counter current[chan] = buffer; // Current step becomes previous step - buffer = buffer->next; // Skip the current step which was just before deleted step - while(buffer != head[chan]) { // If this is not the head, - buffer->stepnumber--; // decrease counter - buffer = buffer->next; // and move on + + int c = 0; + buffer = head[chan]; + + buffer->stepnumber = c; + c++; + buffer = buffer->next; + while(buffer != head[chan]) { + buffer->stepnumber = c; + c++; + buffer = buffer->next; } + nstep[chan] = c; return HIGH; } \ No newline at end of file From 51f6c942f37ca2347f38ce4b40a2be51392213a7 Mon Sep 17 00:00:00 2001 From: alemi Date: Wed, 10 Jul 2019 03:55:27 +0200 Subject: [PATCH 28/30] Storage required for sequences has been dramatically reduced. Introduced auto save and load (every 15 mins) --- cvkeyboard.ino | 275 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 195 insertions(+), 80 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index ecf7ba8..c39fe07 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -1,6 +1,7 @@ #include #include #include +#include #include #define BPQN 24 // Ableton sends 24, VCV rack only one, by standard should be 24? @@ -8,35 +9,43 @@ #define NOTEOffset 36 #define DRUMSHIFT 6 #define drumOffset 60 + #define MINUTE 60000 +#define INTERVAL 15 // How many minutes between autosave #define MIDICLOCK 0xf8 + #define MAXKEYS 48 #define MAXDPAD 7 #define MAXSTEP 64 +#define MAXCHANNEL 6 +#define NKEYS 12 +#define NOCTAVES 4 #define NBITS 6 + #define DEBOUNCE 100 MIDI_CREATE_DEFAULT_INSTANCE(); typedef struct SequencerStep* link; -typedef struct OCTAVEStatus { // This struct is for an OCTAVE status. Each bool is for 1 NOTE - bool stat[12]; - int nOct; -} octst; +typedef struct SavePoint { + int headAddr[MAXCHANNEL]; + int tailAddr[MAXCHANNEL]; +} save_p; typedef struct SequencerStep { - bool clean = LOW; - bool kboard_s[MAXKEYS]; - bool dpad_s[MAXDPAD]; + int kboard_s[4]; + int dpad_s; unsigned short stepnumber; link next; } step; +save_p saveH; + // PIN DECLARATIONS -int NOTE[12] = { // Pins used to read each note (C is 0, B is 11) +int NOTE[NKEYS] = { // Pins used to read each note (C is 0, B is 11) 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44 }; -int OCTAVE[4] = { // Pins associated to each OCTAVE's contact bar +int OCTAVE[NOCTAVES] = { // Pins associated to each OCTAVE's contact bar 12, 9, 8, 10 }; int LEDS[NBITS] = { // Pins used for leds 5, 4, 2, 14, 16, 18 }; @@ -46,9 +55,11 @@ int DEL = 11; // Capacitive button used for DELETE button int PLUS = 10; // Capacitive button used for PLUS button int MINUS = 9; // Capacitive button used for MINUS button - // GLOBAL SETTINGS + // USEFUL ITERABLES int pentathonic[10] = { // Used to quantize drum notes 0, 2, 5, 7, 9, 12, 14, 17, 19, 21 }; +int loadingDisplay[6] = { + 1, 3, 7, 15, 31, 63}; // PLACEHOLDERS byte velocity = 100; // @@ -72,41 +83,48 @@ bool chan_up = LOW; // Only for now because I have few buttons :C bool next_step = LOW; // Used to wait for a full switch cycle int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing -unsigned long last_gate = 0; // Gate start time for last sequencer step -unsigned long gate_length = 1000; // ms of keypress if arpeggiator -unsigned long last_next = 0; +unsigned long last_gate; // Gate start time for last sequencer step +unsigned long last_next; +unsigned long last_save; +unsigned long gate_length = 200; // ms of keypress if arpeggiator bool dpadhit = LOW; // If any drum pad has been hit in this cycle, this is true -int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed -bool kboard[MAXKEYS]; // Last status of keyboard -bool dpad[MAXDPAD]; // Last status of Capacitive Buttons -int cap_read = 0; +int npressed; // Number of keys pressed, used to avoid doing anything when no keys are pressed +int kboard[4]; // Last status of keyboard +int dpad; // Last status of Capacitive Buttons +int cap_read; +int difference = 0; // Used in many places, might as well be a global variable Adafruit_MPR121 cap = Adafruit_MPR121(); void setup() { - for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); - for (int cNOTE = 0; cNOTE < 12; cNOTE++) pinMode(NOTE[cNOTE], INPUT); + display(loadingDisplay[0]); + for (int cOCTAVE = 0; cOCTAVE < NOCTAVES; cOCTAVE++) pinMode(OCTAVE[cOCTAVE], OUTPUT); + for (int cNOTE = 0; cNOTE < NKEYS; cNOTE++) pinMode(NOTE[cNOTE], INPUT); for (int cLED = 0; cLED < NBITS; cLED++) pinMode(LEDS[cLED], OUTPUT); - while (!cap.begin(0x5A)) delay(10); // If MPR121 is not ready, wait for it - for (int cStat = 0; cStat < MAXKEYS; cStat++) kboard[cStat] = LOW; // All keyboard keys start LOW + pinMode(OW, INPUT_PULLUP); // Used for overwrite switch + pinMode(NEXT, INPUT_PULLUP); + display(loadingDisplay[1]); MIDI.begin(MIDI_CHANNEL_OFF); Serial.begin(115200); // Uncomment this if you use Hairless and set baud rate - pinMode(OW, INPUT_PULLUP); // Used for overwrite switch - pinMode(NEXT, INPUT_PULLUP); // Used for overwrite switch + display(loadingDisplay[2]); for (int i = 0; i < 6; i++){ current[i] = NULL; head[i] = NULL; nstep[i] = 0; mute[i] = LOW; } + display(loadingDisplay[3]); + for (int cOCTAVE = 0; cOCTAVE < NOCTAVES; cOCTAVE++) kboard[cOCTAVE] = 0; + dpad = 0; + cap_read = 0; channel = (byte) 1; - for (int i=0; i 0) { // If step was shorter than GATE, close all open notes before next step sem_gate--; for (int chan = 0; chan < 6; chan++) { - if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) - if (current[chan]->kboard_s[i] && !kboard[i] && !current[chan]->next->kboard_s[i]) - playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + if (current[chan] == NULL) continue; + for (int i = 0; i < NOCTAVES; i++) + for (int j = 0; j < NKEYS; j++) + if (((current[chan]->kboard_s[i] >> j) & 1) && !(chan+1 != channel && ((kboard[i]>>j) & 1))) + playNote((i*NKEYS)+j, LOW, (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) - if (current[chan]->dpad_s[i] && !dpad[i]) - playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); - } + if (((current[chan]->dpad_s >> i) & 1) && !(chan+1 != channel && ((dpad>>i) & 1))) + playDrum(i, LOW, (byte) chan+1); + } } if (plus_step && minus_step) { @@ -163,8 +183,8 @@ void loop() { if (clear_step) { clear_step = LOW; if (current[channel-1] != NULL) { - for (int i = 0; i < MAXKEYS; i++) current[channel-1]->kboard_s[i] = LOW; - for (int i = 0; i < MAXDPAD; i++) current[channel-1]->dpad_s[i] = LOW; + for (int i = 0; i < NOCTAVES; i++) current[channel-1]->kboard_s[i] = 0; + current[channel-1]->dpad_s = 0; } } @@ -174,13 +194,14 @@ void loop() { for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; if (current[chan] != NULL) { // PLAY all step notes in all unmuted channels - if (!(npressed > 0 && chan == (int) channel-1)) - for (int i = 0; i < MAXKEYS; i++) - if (current[chan]->kboard_s[i] && !kboard[i]) - playNote(i, current[chan]->kboard_s[i], (byte) chan+1); - for (int i = 0; i < MAXDPAD; i++) // Drums are played nonetheless because drums already layered won't overrule - if (current[chan]->dpad_s[i] && !dpad[i]) - playDrum(i, current[chan]->dpad_s[i], (byte) chan+1); + for (int i = 0; i < NOCTAVES; i++) + for (int j = 0; j < NKEYS; j++) + if (((current[chan]->kboard_s[i] >> j) & 1) && !(chan+1 == channel && npressed > 0)) + playNote((i*NKEYS)+j, HIGH, (byte) chan+1); + + for (int i = 0; i < MAXDPAD; i++) // Drums are played nonetheless because drums already layered won't overrule + if ((current[chan]->dpad_s >> i) & 1) + playDrum(i, HIGH, (byte) chan+1); } } last_gate = millis(); @@ -190,51 +211,48 @@ void loop() { if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; for (int chan = 0; chan < 6; chan++) { - if (mute[chan]) continue; - for (int i = 0; i < MAXKEYS; i++) - if (current[chan]->kboard_s[i] && !kboard[i]) - playNote(i, !current[chan]->kboard_s[i], (byte) chan+1); + if (current[chan] == NULL) continue; + for (int i = 0; i < NOCTAVES; i++) + for (int j = 0; j < NKEYS; j++) + if (((current[chan]->kboard_s[i] >> j) & 1) && !(chan+1 != channel && ((kboard[i]>>j) & 1))) + playNote((i*NKEYS)+j, LOW, (byte) chan+1); + for (int i = 0; i < MAXDPAD; i++) - if (current[chan]->dpad_s[i] && !dpad[i]) - playDrum(i, !current[chan]->dpad_s[i], (byte) chan+1); + if (((current[chan]->dpad_s >> i) & 1) && !(chan+1 != channel && ((dpad>>i) & 1))) + playDrum(i, LOW, (byte) chan+1); } } dpadhit = LOW; - for (int cButton = 0; cButton < MAXDPAD; cButton++) { - if (( 1 & (cap_read >> cButton)) ^ dpad[cButton]) { - dpad[cButton] = (bool) 1 & (cap_read >> cButton); - playDrum(cButton, dpad[cButton], channel); - } - dpadhit = (dpad[cButton] || dpadhit); + difference = dpad ^ cap_read; + for (int c = 0; c < MAXDPAD; c++) { + if ((difference>>c) & 1) playDrum(c, ((cap_read>>c) & 1), channel); + if (dpadhit || ((cap_read>>c) & 1)) dpadhit = HIGH; + if (difference != 0) dpad = cap_read; } npressed = 0; for (int cOCTAVE = 0; cOCTAVE < 4; cOCTAVE++) { digitalWrite(OCTAVE[cOCTAVE], HIGH); - npressed += eval(scan(cOCTAVE)); + npressed += eval(scan(), cOCTAVE); digitalWrite(OCTAVE[cOCTAVE], LOW); } if (current[channel-1] != NULL && digitalRead(OW)) { - if (npressed > 0) for (int i = 0; i < MAXKEYS; i++) - current[channel-1]->kboard_s[i] = kboard[i]; - if (dpadhit) for (int i = 0; i < MAXDPAD; i++) - current[channel-1]->dpad_s[i] = dpad[i] || current[channel-1]->dpad_s[i]; // Drum hits aren't exclusive! - current[channel-1]->clean = LOW; + if (npressed > 0) for (int i = 0; i < NOCTAVES; i++) { + difference = kboard[i] ^ current[channel-1]->kboard_s[i]; + if (difference != 0) current[channel-1]->kboard_s[i] = kboard[i]; + } + if (dpadhit) current[channel-1]->dpad_s = current[channel-1]->dpad_s | dpad; // Drum hits aren't exclusive! } } // Hardware specific functions -octst scan(int nOct) { // This function reads the 12 NOTE pins and returns a struct - int c; // with 1 bool for each NOTE - octst output; - - output.nOct = nOct; - - for (c = 0; c < 12; c++) { - output.stat[c] = digitalRead(NOTE[c]); +int scan() { // This function reads the 12 NOTE pins and returns a struct + int output = 0; + for (int c = 0; c < NKEYS; c++) { + if (digitalRead(NOTE[c])) output = output | (1<>c) & 1) playNote(c + sNOTE, ((input>>c) & 1), channel); + if (((input>>c) & 1)) pressed++; } + if (difference != 0) kboard[nOct] = input; return pressed; } @@ -286,6 +303,12 @@ void playDrum(int c, bool status, byte chan) { // Sync functions void sync() { + + if (millis() > last_save + (unsigned long) MINUTE*INTERVAL) { + saveAll(); + last_save = millis(); + } + if (next_step != (bool) !digitalRead(NEXT)) { // Used to increase channel with a button because I don't have a rotary switch (yet!) next_step = (bool) !digitalRead(NEXT); if (millis() > last_next+DEBOUNCE && next_step == HIGH) { @@ -297,7 +320,7 @@ void sync() { if (Serial.read() == MIDICLOCK) { //sem_beat++; midiclock++; - if (midiclock == BPQN){ + if (midiclock == BPQN) { midiclock = 0; sem_beat++; } @@ -322,8 +345,8 @@ bool insertStep(byte chan) { return LOW; } - for (int i = 0; i < MAXKEYS; i++) newS->kboard_s[i] = LOW; - for (int i = 0; i < MAXDPAD; i++) newS->dpad_s[i] = LOW; + for (int i = 0; i < NOCTAVES; i++) newS->kboard_s[i] = 0; + newS->dpad_s = 0; if (head[chan] == NULL) { newS->next = newS; @@ -392,4 +415,96 @@ bool deleteStep(byte chan) { } nstep[chan] = c; return HIGH; +} + +// SAVING FUNCTIONS + +void saveAll() { + int currAddr = (int) sizeof(save_p); + link buffer; + + for (int c=0; cnext; + while (buffer != head[c]) { + currAddr = saveStep(buffer, currAddr); + buffer = buffer->next; + } + saveH.tailAddr[c] = currAddr; + } + saveHead(saveH); +} + +void loadAll() { + saveH = loadHead(); + int currAddr = saveH.headAddr[0]; + link buffer; + for (int c=0; cnext = newS; + buffer = newS; + } + buffer->next = head[c]; + } +} + +save_p loadHead() { + save_p save; + byte* pointer = (byte*) (void*) &save; + int addr = 0; + for (int i=0; i < (int) sizeof(save_p); i++) { + *pointer = EEPROM.read(addr); + addr++; + pointer++; + } + return save; +} + +void saveHead(save_p save) { + byte* pointer = (byte*) (void*) &save; + int addr = 0; + for (int i=0; i < (int) sizeof(save_p); i++){ + EEPROM.update(addr, *pointer); + addr++; + pointer++; + } +} + +int saveStep(link curr_step, int addr) { + step buffer = *curr_step; + buffer.next = (link) (addr + (int) sizeof(SequencerStep)); + byte* pointer = (byte*) (void*) &buffer; + for (int i=0; i < (int) sizeof(SequencerStep); i++) { + EEPROM.update(addr, *pointer); + pointer++; + addr++; + } + return addr; +} + +int loadStep(link step, int addr) { + byte* pointer = (byte*) (void*) step; + for (int i=0; i<(int) sizeof(SequencerStep); i++) { + *pointer = EEPROM.read(addr); + pointer++; + addr++; + } + return addr; } \ No newline at end of file From 1458cf72f0c4ea305b5561dcaf82f8f1216ee087 Mon Sep 17 00:00:00 2001 From: alemi Date: Fri, 19 Jul 2019 17:38:15 +0200 Subject: [PATCH 29/30] Reintroduced a basic arpeggiator --- cvkeyboard.ino | 54 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/cvkeyboard.ino b/cvkeyboard.ino index c39fe07..1d1dba2 100644 --- a/cvkeyboard.ino +++ b/cvkeyboard.ino @@ -54,6 +54,7 @@ int NEXT = 51; // Pin used for next step switch int DEL = 11; // Capacitive button used for DELETE button int PLUS = 10; // Capacitive button used for PLUS button int MINUS = 9; // Capacitive button used for MINUS button +int ARP = 7; // Capacitive button used for ARP button // USEFUL ITERABLES int pentathonic[10] = { // Used to quantize drum notes @@ -74,13 +75,15 @@ bool mute[6]; byte channel; // Current selected channel. Drums are shifted of DRUMSHIFT channels (so channels can only be 6) // SYSTEM VARIABLES -int arp = 0; // Keeps track of last played NOTE if arpeggiating +int arp[2]; // arp[0] = OCTAVE, arp[1] = KEY (arp[0] for iterations, arp[1] for shifting) int midiclock = 0; // Used to sync with MIDI clock +bool arpeggiating = LOW; // Goes HIGH if the user is requesting an arpeggio bool plus_step = LOW; // This is used to remember the addition of a step bool minus_step = LOW; // This is used to remember the deletion of a step bool clear_step = LOW; // This is used to remember the clearing of a step bool chan_up = LOW; // Only for now because I have few buttons :C bool next_step = LOW; // Used to wait for a full switch cycle +bool overwrite = LOW; int sem_beat = 0; // Basic semaphore used to sync with MIDI beat int sem_gate = 0; // Basic semaphore used for gate timing unsigned long last_gate; // Gate start time for last sequencer step @@ -116,6 +119,8 @@ void setup() { display(loadingDisplay[3]); for (int cOCTAVE = 0; cOCTAVE < NOCTAVES; cOCTAVE++) kboard[cOCTAVE] = 0; dpad = 0; + arp[0] = 0; + arp[1] = 0; cap_read = 0; channel = (byte) 1; display(loadingDisplay[4]); @@ -138,9 +143,11 @@ void loop() { else if (current[channel-1] == NULL) display(analogRead(channel)); else display(current[channel-1]->stepnumber); - plus_step = plus_step || ((cap_read >> PLUS) & 1); - minus_step = minus_step || ((cap_read >> MINUS) & 1); - clear_step = clear_step || ((cap_read >> DEL) & 1); + plus_step = plus_step || (bool) ((cap_read >> PLUS) & 1); + minus_step = minus_step || (bool) ((cap_read >> MINUS) & 1); + clear_step = clear_step || (bool) ((cap_read >> DEL) & 1); + arpeggiating = (bool) ((cap_read >> ARP) & 1); + overwrite = digitalRead(OW); if (chan_up != (bool) ((cap_read >> 8) & 1)) { // Used to increase channel with a button because I don't have a rotary switch (yet!) chan_up = (bool) ((cap_read >> 8) & 1); @@ -155,11 +162,12 @@ void loop() { if (sem_gate > 0) { // If step was shorter than GATE, close all open notes before next step sem_gate--; + if (arpeggiating) playNote((arp[0]*NKEYS)+arp[1], LOW, channel); for (int chan = 0; chan < 6; chan++) { if (current[chan] == NULL) continue; for (int i = 0; i < NOCTAVES; i++) - for (int j = 0; j < NKEYS; j++) - if (((current[chan]->kboard_s[i] >> j) & 1) && !(chan+1 != channel && ((kboard[i]>>j) & 1))) + for (int j = 0; j < NKEYS; j++) // IF note was played AND user is not playing on this channel AND this note is not kept played + if (((current[chan]->kboard_s[i] >> j) & 1) && !(chan+1 != channel && ((kboard[i]>>j) & 1)) && !((current[chan]->next->kboard_s[i] >> j) & 1)) playNote((i*NKEYS)+j, LOW, (byte) chan+1); for (int i = 0; i < MAXDPAD; i++) @@ -191,6 +199,26 @@ void loop() { nextStep(); // ALL STEPS INCREMENTED display(current[channel-1]->stepnumber); + if (arpeggiating) { + while (npressed > 0) { + arp[1]++; + if (arp[1] == NKEYS) { + arp[1] = 0; + arp[0]++; + } + if (arp[0] == NOCTAVES) arp[0] = 0; + + if ((kboard[arp[0]] >> arp[1]) & 1) { + playNote((arp[0]*NKEYS)+arp[1], HIGH, channel); + if (overwrite && current[channel-1] != NULL) { + for (int i=0; ikboard_s[i] = 0; + current[channel-1]->kboard_s[arp[0]] = current[channel-1]->kboard_s[arp[0]] | (1 << arp[1]); + } + break; + } + } + } + for (int chan = 0; chan < 6; chan++) { if (mute[chan]) continue; if (current[chan] != NULL) { // PLAY all step notes in all unmuted channels @@ -210,6 +238,7 @@ void loop() { if (sem_gate > 0 && (millis() - last_gate) > gate_length) { sem_gate--; + if (arpeggiating) playNote((arp[0]*NKEYS)+arp[1], LOW, channel); for (int chan = 0; chan < 6; chan++) { if (current[chan] == NULL) continue; for (int i = 0; i < NOCTAVES; i++) @@ -238,11 +267,12 @@ void loop() { digitalWrite(OCTAVE[cOCTAVE], LOW); } - if (current[channel-1] != NULL && digitalRead(OW)) { - if (npressed > 0) for (int i = 0; i < NOCTAVES; i++) { - difference = kboard[i] ^ current[channel-1]->kboard_s[i]; - if (difference != 0) current[channel-1]->kboard_s[i] = kboard[i]; - } + if (current[channel-1] != NULL && overwrite) { + if (!arpeggiating && npressed > 0) + for (int i = 0; i < NOCTAVES; i++) { + difference = kboard[i] ^ current[channel-1]->kboard_s[i]; + if (difference != 0) current[channel-1]->kboard_s[i] = kboard[i]; + } if (dpadhit) current[channel-1]->dpad_s = current[channel-1]->dpad_s | dpad; // Drum hits aren't exclusive! } } @@ -272,7 +302,7 @@ int eval(int input, int nOct) { difference = kboard[nOct] ^ input; for (int c = 0; c < 12; c++) { - if ((difference>>c) & 1) playNote(c + sNOTE, ((input>>c) & 1), channel); + if (!arpeggiating && ((difference>>c) & 1)) playNote(c + sNOTE, ((input>>c) & 1), channel); if (((input>>c) & 1)) pressed++; } if (difference != 0) kboard[nOct] = input; From d772fc63df20192057334a4cbb28f1ed4c17e512 Mon Sep 17 00:00:00 2001 From: alemi Date: Sat, 20 Jul 2019 18:31:02 +0200 Subject: [PATCH 30/30] Uploaded firmwares used --- hex/Arduino-usbserial-mega.hex | 256 +++++++++++++++++++++++++++++++++ hex/arduino_midi.hex | 232 ++++++++++++++++++++++++++++++ 2 files changed, 488 insertions(+) create mode 100644 hex/Arduino-usbserial-mega.hex create mode 100644 hex/arduino_midi.hex diff --git a/hex/Arduino-usbserial-mega.hex b/hex/Arduino-usbserial-mega.hex new file mode 100644 index 0000000..e7e262a --- /dev/null +++ b/hex/Arduino-usbserial-mega.hex @@ -0,0 +1,256 @@ +:10000000A2C00000BBC00000B9C00000B7C0000023 +:10001000B5C00000B3C00000B1C00000AFC0000018 +:10002000ADC00000ABC00000A9C000005AC4000071 +:100030001EC40000A3C00000A1C000009FC00000BB +:100040009DC000009BC0000099C0000097C0000048 +:1000500095C0000093C0000091C0000014C10000D2 +:100060008DC000008BC0000089C0000087C0000068 +:1000700085C0000083C0000081C000007FC0000078 +:100080007DC000007BC0000079C0000077C0000088 +:1000900075C0000073C000001201100102000008CA +:1000A0004123100001000102DC0109023E000201AF +:1000B00000C0320904000001020201000524000111 +:1000C0001004240206052406000107058203080027 +:1000D000FF09040100020A000000070504024000B5 +:1000E00001070583024000010403090432034100B3 +:1000F00072006400750069006E006F002000280027 +:100100007700770077002E006100720064007500B0 +:1001100069006E006F002E0063006300290000007C +:100120002403410072006400750069006E006F00D6 +:1001300020004D006500670061002000320035009E +:1001400036003000000011241FBECFEFD2E0DEBF2A +:10015000CDBF11E0A0E0B1E0E0EDFFE002C005900E +:100160000D92A631B107D9F712E0A6E1B1E001C0C6 +:100170001D92AF32B107E1F7F1D028C741CF9C0102 +:10018000DC01AE57BF4FED91FC9111974191119653 +:10019000FC93EE9380589F4FE817F90711F42D93C5 +:1001A0003C939FB7F894F901EC57FF4F8081815041 +:1001B00080839FBF842F0895DF92EF92FF920F9369 +:1001C0001F93FC018489813019F0823021F405C02D +:1001D00040E3D42E04C0DD2402C030E2D32E838954 +:1001E000823011F488E0D82A8589873031F0883050 +:1001F00031F0863031F482E003C084E001C086E053 +:10020000D82A1092C9001092C8001092CA00E78440 +:10021000F0880189128980E0E81681EEF80680E016 +:10022000080780E0180719F420E130E00FC0C8018A +:10023000B701969587957795679560587B47814E6E +:100240009F4FA8019701A0D6215030403093CD0098 +:100250002093CC00D092CA0080E0E81681EEF80628 +:1002600080E0080780E0180711F082E001C080E01C +:100270008093C80088E98093C9001F910F91FF9077 +:10028000EF90DF9008951F920F920FB60F921124F6 +:100290002F938F939F93EF93FF939091CE008EB304 +:1002A0008430F1F4E0919901F0919A019083E0910A +:1002B0009901F0919A01CF01019690939A01809350 +:1002C00099018959914021F489E191E092838183D8 +:1002D0009FB7F89480919D018F5F80939D019FBF90 +:1002E000FF91EF919F918F912F910F900FBE0F90E3 +:1002F0001F901895FC01858580FF02C05F980895C6 +:100300005F9A089580E091E0D5C580E091E088C5CE +:1003100084B7877F84BF28E10FB6F89420936000EC +:10032000109260000FBE87E690E09093CD0080931E +:10033000CC0086E08093CA001092C8002093C900C8 +:10034000539A5A9A8AB180638AB98BB180638BB908 +:1003500083D284E085BD5F9A579A08950F931F93C7 +:10036000CF93DF93D5DF2FB7F8948EE991E0909388 +:100370001F0280931E0290932102809320022FBFC0 +:100380002FB7F89489E191E090939A0180939901B5 +:1003900090939C0180939B012FBF7894CEE9D1E08C +:1003A00003E08FB7F894909122028FBF903809F143 +:1003B00080E091E0ABD497FD1CC0E0911E02F0916B +:1003C0001F028083E0911E02F0911F02CF0101966F +:1003D00090931F0280931E028E51924011F4D2839B +:1003E000C1839FB7F894809122028F5F809322028D +:1003F0009FBF8FB7F89410919D018FBFA89902C03D +:10040000113678F1A89A80919D01882361F05D985A +:100410000093160108C089E191E0B1DE682F80E009 +:1004200091E0DAD411501123B1F78091160188239D +:1004300051F0809116018150809316018091160130 +:10044000882309F45D9A80911701882351F08091E7 +:10045000170181508093170180911701882309F4B7 +:100460005C9A8FB7F894909122028FBF992369F01C +:100470008EE991E084DE982F8091C80085FFFCCF43 +:100480009093CE005C980093170180E091E095D4A2 +:100490002AD487CFDA01923049F0933061F091305D +:1004A000F9F4E8E9F0E022E130E01EC0EAEAF0E029 +:1004B0002EE330E019C0813049F0813018F08230ED +:1004C00079F408C0E8EEF0E0849107C0ECEEF0E0CB +:1004D000849103C0E0E2F1E08491282F30E004C071 +:1004E000E0E0F0E020E030E0ED93FC93C9010895F6 +:1004F00028E030E040E003C04F5F220F331F281791 +:100500003907D0F3842F8295807F08958093E90086 +:100510008091EB0081608093EB001092ED0060937E +:10052000EC004093ED008091EE00881F8827881F23 +:1005300008951092F40090E09093E9001092F0007A +:100540001092E8001092ED008091EB008E7F809376 +:10055000EB009F5F953081F70895809127028823F3 +:100560008CF403C08EB38823B1F08091E80082FF41 +:10057000F9CF8091E8008B778093E80008958EB3DF +:10058000882349F08091E80080FFF9CF8091E8004E +:100590008E778093E800089594E68091EC0080FFC8 +:1005A00005C08091E80080FF05C023C08091E8006D +:1005B00082FD1FC08EB3882311F482E008958EB3AC +:1005C000853011F483E008958091EB0085FF02C02F +:1005D00081E008958091E10082FFDFCF8091E1000A +:1005E0008B7F8093E100992311F484E0089591506A +:1005F000D4CF80E008959C0140912D0250912E02AD +:100600004617570718F4F90120E038C06115710545 +:1006100011F0AB01F8CF8091E8008E778093E8006D +:1006200040E050E0F0CF8091E80083FF02C081E01D +:1006300008958091E80082FD2DC08EB3882381F15A +:100640008EB3853079F18091E80080FF17C09091DA +:10065000F20006C081918093F100415050409F5FAD +:100660004115510511F09830A8F320E0983009F4B5 +:1006700021E08091E8008E778093E80041155105D4 +:1006800091F6222381F606C08EB3882349F08EB3FB +:10069000853041F08091E80082FFF6CF80E0089538 +:1006A00082E0089583E008959C0140912D025091CD +:1006B0002E024617570710F490E03BC061157105F4 +:1006C00011F0AB01F9CF8091E8008E778093E800BC +:1006D00040E050E0F1CF8091E80083FF02C081E06C +:1006E00008958091E80082FD30C08EB3882399F18F +:1006F0008EB3853091F18091E80080FF1AC080911F +:10070000F20009C0F9012F5F3F4FE491E093F1003F +:10071000415050408F5F4115510511F0883090F3E2 +:1007200090E0883009F491E08091E8008E77809322 +:10073000E8004115510579F6992369F606C08EB394 +:10074000882349F08EB3853041F08091E80082FF24 +:10075000F6CF80E0089582E0089583E008959C013B +:100760006115710529F48091E8008B778093E8008A +:10077000F90120C08091E80083FF02C081E0089564 +:100780008EB3882339F18EB3853031F18091E80042 +:1007900082FFF0CF06C08091F100819361507040DC +:1007A00021F08091F2008823B1F78091E8008B77E7 +:1007B0008093E80061157105E9F606C08EB38823C1 +:1007C00049F08EB3853041F08091E80080FFF6CF8C +:1007D00080E0089582E0089583E0089542D044D0F7 +:1007E0001EBA10922502109224021092230284E075 +:1007F00089BD89B5826089BD09B400FEFDCF8091B5 +:10080000D800982F9F779093D80080688093D80065 +:10081000809163008E7F809363008091D8008F7DEC +:100820008093D8008091E0008E7F8093E0008091DB +:10083000E1008E7F8093E1008091E20081608093EF +:10084000E2008091E100877F8093E1008091E200E7 +:1008500088608093E2000895C1DF81E080932602E2 +:1008600008951092E20008951092E10008951F92F9 +:100870000F920FB60F9211241F932F933F934F9314 +:100880005F936F937F938F939F93AF93BF93EF93F8 +:10089000FF93E9EEF0E0108117701082E0EFF0E0D6 +:1008A0008081877F80837894C3D0F894A9EEB0E0EC +:1008B0001C92E0EFF0E08081886080831C93FF91C0 +:1008C000EF91BF91AF919F918F917F916F915F91C8 +:1008D0004F913F912F911F910F900FBE0F901F903E +:1008E00018951F920F920FB60F9211242F933F93DA +:1008F0004F935F936F937F938F939F93AF93BF9328 +:10090000EF93FF938091E10080FF1BC08091E20094 +:1009100080FF17C08091E1008E7F8093E10080917D +:10092000E2008E7F8093E2008091E20080618093FC +:10093000E2008091D80080628093D80019BC1EBA72 +:10094000D1D18091E10084FF29C08091E20084FF31 +:1009500025C084E089BD89B5826089BD09B400FEE7 +:10096000FDCF8091D8008F7D8093D8008091E100E9 +:100970008F7E8093E1008091E2008F7E8093E20081 +:100980008091E20081608093E200809125028823BB +:1009900011F481E001C084E08EBBA4D18091E1001C +:1009A00083FF27C08091E20083FF23C08091E10094 +:1009B000877F8093E10082E08EBB109225028091B8 +:1009C000E1008E7F8093E1008091E2008E7F809332 +:1009D000E2008091E20080618093E200AADD80E085 +:1009E00060E042E093DD8091F00088608093F00049 +:1009F00079D18091E10082FF0AC08091E20082FFFC +:100A000006C08091E1008B7F8093E1006BD1FF9164 +:100A1000EF91BF91AF919F918F917F916F915F9176 +:100A20004F913F912F910F900FBE0F901F901895EF +:100A30001F93DF93CF93CDB7DEB7AC970FB6F89483 +:100A4000DEBF0FBECDBFE7E2F2E08091F1008193FF +:100A500022E0EF32F207C9F7809127023091280295 +:100A6000353009F487C0363040F43130C9F13130C7 +:100A700070F0333009F01DC133C0383009F4EFC0D5 +:100A8000393009F4FEC0363009F013C192C0803805 +:100A900021F0823809F00DC108C0909123028091A5 +:100AA0002402882399F0926011C080912B028770F4 +:100AB0008093E9008091EB0090E025E09695879582 +:100AC0002A95E1F7982F91701092E9008091E80043 +:100AD000877F8093E8009093F1001092F100CAC0E4 +:100AE000882319F0823009F0E4C090E08F71907093 +:100AF000009721F0029709F0DDC00CC08091290217 +:100B0000813009F0D7C010922402333069F5809308 +:100B100024022AC080912902882331F520912B02DA +:100B2000277009F4C7C02093E9008091EB0080FF93 +:100B3000C1C0333021F48091EB00806213C08091FA +:100B4000EB0080618093EB0081E090E002C0880FB1 +:100B5000991F2A95E2F78093EA001092EA008091AB +:100B6000EB0088608093EB001092E9008091E80030 +:100B7000877F83C0882309F09CC01091290280914F +:100B8000E800877F8093E800E8DC04C08EB3882308 +:100B900009F490C08091E80080FFF8CF812F8F7713 +:100BA00011F492E001C093E09EBB80688093E30063 +:100BB00081C08058823008F07CC0809129029091D9 +:100BC0002A0223E08C3D920799F55FB7F894DE0185 +:100BD00015964EE020E030E061E2E42FF0E0609313 +:100BE0005700849120FF03C082958F704F5F982F2C +:100BF0009F70892F805D8A3308F0895F8C931196EE +:100C00001C9211972F5F3F4F12962431310529F71F +:100C10005FBF8AE28B8383E08C838091E800877FCB +:100C20008093E800CE0103966AE270E0E4DC11C034 +:100C300060912B02AE014F5F5F4F2CDCBC0100972F +:100C4000C9F18091E800877F8093E80089819A81CB +:100C50002BDD8091E8008B778093E8002BC08038F3 +:100C600041F58091E800877F8093E800809125021C +:100C70008093F1008091E8008E778093E8006DDC2E +:100C800019C08823B1F490912902923098F4809190 +:100C9000E800877F8093E800909325025EDC8091D6 +:100CA0002502882311F483E001C084E08EBB2DDB94 +:100CB00001C028DB8091E80083FF0AC08091EB002F +:100CC00080628093EB008091E800877F8093E8004A +:100CD000AC960FB6F894DEBF0FBECDBFCF91DF91BB +:100CE0001F91089508951F938EB3882361F010918A +:100CF000E9001092E9008091E80083FF01C098DECE +:100D000017701093E9001F9108950895FC018EB3A8 +:100D1000843021F587859089A189B2890097A10542 +:100D2000B105E1F085818093E9008091E80082FFC0 +:100D300015C08091F200882319F42FEF3FEF04C013 +:100D40008091F100282F30E08091F200882341F457 +:100D50008091E8008B778093E80002C02FEF3FEF8F +:100D6000C9010895FC018EB3843011F587859089FF +:100D7000A189B2890097A105B105D1F08181809345 +:100D8000E9008091F2008823A9F09091E800809119 +:100D9000E8008E778093E80095FD0CC0FDDB982F6E +:100DA000882349F48091E8008E778093E80003C09F +:100DB00092E001C090E0892F0895FC018EB3843049 +:100DC00051F487859089A189B2890097A105B10561 +:100DD00011F0CF01C7CF08951F93FC01162F8EB3DA +:100DE0008430D9F487859089A189B2890097A105BB +:100DF000B10599F081818093E9008091E80085FD3B +:100E000008C08091E8008E778093E800C5DB8823D6 +:100E100029F41093F10080E001C082E01F91089551 +:100E20000F931F93CF93DF93EC010D96FC0189E0A4 +:100E3000DF011D928A95E9F72A813B8109818C8126 +:100E4000882311F410E001C014E0C90151DB182B14 +:100E50001260802F61E8412F59DB882329F12E8110 +:100E60003F810D818885882311F410E001C014E0D2 +:100E7000C9013EDB182B1260802F60E8412F46DB52 +:100E8000882391F02A853B8509858C85882311F478 +:100E900010E001C014E0C9012BDB182B1260802F79 +:100EA00061EC412F33DB01C080E0DF91CF911F91D6 +:100EB0000F910895CF93DF93EC018091E80083FFB9 +:100EC00060C0888190E020912B0230912C0228177D +:100ED000390709F056C080912802813261F08232D0 +:100EE00020F4803209F04DC019C0823269F183329A +:100EF00009F047C038C080912702813A09F041C00B +:100F00008091E800877F8093E800CE010F9667E02C +:100F100070E071DB8091E8008B7713C0809127022D +:100F2000813279F58091E800877F8093E800CE01D7 +:100F30000F9667E070E013DCCE013ED98091E800A7 +:100F40008E778093E8001DC0809127028132C9F41A +:100F50008091E800877F8093E800809129028D8747 +:100F6000CE01C8D90DC080912702813251F4809101 +:100F7000E800877F8093E800CE0160912902C5DEFA +:100F8000ECDADF91CF910895A1E21A2EAA1BBB1BC8 +:100F9000FD010DC0AA1FBB1FEE1FFF1FA217B30745 +:100FA000E407F50720F0A21BB30BE40BF50B661F5B +:100FB000771F881F991F1A9469F76095709580951F +:100FC00090959B01AC01BD01CF010895F894FFCF2E +:100FD0000003400000044000000208000000000080 +:060FE0000000000000000B +:00000001FF \ No newline at end of file diff --git a/hex/arduino_midi.hex b/hex/arduino_midi.hex new file mode 100644 index 0000000..3199519 --- /dev/null +++ b/hex/arduino_midi.hex @@ -0,0 +1,232 @@ +:1000000096C00000AFC00000ADC00000ABC0000053 +:10001000A9C00000A7C00000A5C00000A3C0000048 +:10002000A1C000009FC000009DC0000006C50000E8 +:1000300099C0000097C0000095C0000093C0000068 +:1000400091C000008FC000008DC000008BC0000078 +:1000500089C0000087C0000085C000008DC100007D +:1000600081C000007FC000007DC000007BC0000098 +:1000700079C000001A036100720064007500690015 +:100080006E006F005F006D00690064006900000091 +:100090002003680069006400750069006E006F004D +:1000A0002000700072006F006A0065006300740039 +:1000B00000000403090409026500020100C03209BE +:1000C00004000000010100000924010001090001F1 +:1000D000010904010002010300000724010001419D +:1000E0000006240201010006240202020009240382 +:1000F00001030102010009240302040101010009B6 +:1001000005020240000500000525010101090581E5 +:100110000240000500000525010103120110010045 +:10012000000008EB03482001000102000100112437 +:100130001FBECFEFD2E0DEBFCDBF11E0A0E0B1E047 +:10014000E2E6FEE002C005900D92A030B107D9F7BB +:1001500021E0A0E0B1E001C01D92AD31B207E1F7AE +:10016000CAD07DC64DCF84B7877F84BF88E10FB6E4 +:10017000F89480936000109260000FBE8FE190E0D1 +:100180009093CD008093CC0086E08093CA001092BB +:10019000C800E9ECF0E088E18083539A5A9A84E041 +:1001A00085BD108288E98083469A3E9A90E080E877 +:1001B0000FB6F89480936100909361000FBE8AB1EE +:1001C000806F8AB98BB18F708BB928D45C985D9899 +:1001D00008958BB18F70806A8BB908958BB18F7041 +:1001E00080618BB90895CF9342E361E881E057D3F2 +:1001F000C82F42E360E882E052D3882321F0CC2369 +:1002000011F090E601C090E98BB18F70892B8BB90A +:10021000CF91089580911401843041F581E080935D +:10022000E9008091E80080FF21C0809108018823C7 +:10023000E9F01092080140E050E064E070E084E0F2 +:1002400091E0D6D11092040110920501109206019E +:10025000109207018091E8008E778093E8005D9806 +:1002600088E893E190930301809302010895CF936E +:10027000DF9300D000D0CDB7DEB780911401843079 +:1002800099F582E08093E9008091E80082FF2CC01C +:1002900040E050E064E070E0CE010196F1D18A8147 +:1002A0009091C80095FFFCCF8093CE008B819091F8 +:1002B000C80095FFFCCF8093CE008C819091C80040 +:1002C00095FFFCCF8093CE005C9888E893E19093F3 +:1002D0000101809300018091F200811105C080919D +:1002E000E8008B778093E8000F900F900F900F90AD +:1002F000DF91CF91089537DF78947894809102014F +:1003000090910301009731F001979093030180933E +:10031000020101C05D9A8091000190910101009756 +:1003200031F00197909301018093000101C05C9A24 +:100330009EDF70DF78D5E2CF87FF0CC09CE0980F7E +:10034000923040F0982F9B7F993F21F0803F18F426 +:10035000807F089580E00895982F9F7E282F2F7C1E +:10036000203839F0803E29F081E0903C19F080E09F +:1003700001C081E0817008951F920F920FB60F9215 +:1003800011242F933F934F935F936F937F938F939A +:100390009F93AF93BF93CF93DF93EF93FF9380919E +:1003A0001401843009F0DFC0C091CE0040910B01F0 +:1003B000411178C0C0930D01D0910A018D2FBCDF8F +:1003C000CBDF882349F0C7FD07C0D0930D01C09350 +:1003D0000E0181E080930B01C0910D018C2FACDFE9 +:1003E000813F91F188F4803B89F138F4803971F1D3 +:1003F000803A61F1803869F529C0803D29F1803E5D +:1004000029F1803C31F520C0883F69F030F4833F0A +:10041000D9F0E0F0863F39F01CC08A3FD0F08D3F24 +:1004200010F08E3FB0F08FE080930401C09305017F +:10043000109206011092070181E08093080110924A +:100440000B01109209018FC092E001C093E09093DC +:10045000090190910B0160910901492F50E0262F6D +:1004600030E02150310942175307C4F082958F7054 +:1004700080930401C093050180910E0180930601D1 +:10048000633029F480910F018093070102C010921C +:10049000070110920B01109209011BC09F5F9093FE +:1004A0000B0161C0C7FF19C0CD3F28F4CA3F28F433 +:1004B000C83F99F402C0CE3F80F08C2F3DDF82957B +:1004C0008F7080930401C093050110920601109271 +:1004D000070181E08093080146C0242F30E0F90134 +:1004E000E35FFE4FC083D09109018D2F90E001970B +:1004F00028173907ACF1C0910D018C2F1DDF982F03 +:1005000092959F7090930401C093050190910E0104 +:1005100090930601D33029F490910F019093070135 +:1005200002C01092070110920B011092090191E094 +:1005300090930801803B71F038F4803959F0803A8B +:1005400049F0803851F406C0803D21F0803E11F022 +:10055000803C19F4C0930A0106C010920A0103C03E +:100560004F5F40930B01FF91EF91DF91CF91BF91CE +:10057000AF919F918F917F916F915F914F913F913B +:100580002F910F900FBE0F901F901895292F332792 +:100590002230310559F02330310569F02130310521 +:1005A000F9F482E190E02BE131E01EC085E690E0B5 +:1005B00026EB30E019C099278130910541F0823057 +:1005C000910541F0892B61F4E2EBF0E005C0E0E930 +:1005D000F0E002C0E4E7F0E0849190E09F0104C005 +:1005E00080E090E020E030E0FA013183208308953C +:1005F000CF92DF92EF92FF920F931F93CF93DF93EF +:100600007C018B01EA01A1D1811131C0209731F029 +:1006100088819981081B190BE80EF91EC12CD12C79 +:100620000115110519F18091E80085FD14C0809134 +:10063000E8008E778093E800F6D3209741F0888118 +:1006400099818C0D9D1D9983888385E010C07DD193 +:10065000882331F30CC0F70181917F018093F10071 +:1006600001501109FFEFCF1ADF0ADACF80E0DF91E6 +:10067000CF911F910F91FF90EF90DF90CF90089551 +:10068000CF92DF92EF92FF920F931F93CF93DF935E +:100690007C018B01EA0159D1811131C0209731F0E1 +:1006A00088819981081B190BE80EF91EC12CD12CE9 +:1006B0000115110519F18091E80085FD14C08091A4 +:1006C000E8008B778093E800AED3209741F08881D3 +:1006D00099818C0D9D1D9983888385E010C035D14B +:1006E000882331F30CC08091F100F70181937F01E1 +:1006F00001501109FFEFCF1ADF0ADACF80E0DF9156 +:10070000CF911F910F91FF90EF90DF90CF900895C0 +:1007100020911B0130911C012617370748F0611505 +:10072000710539F42091E8002E772093E80001C08C +:10073000B90120E061157105D1F130911401332325 +:1007400009F443C0353009F442C03091E80033FD6C +:1007500040C03091E80032FF06C08091E80082FF7F +:1007600029C080E008953091E80030FFE3CF209168 +:10077000F20030E0FC01281B390BCF01820F931FE0 +:100780006115710549F08830910530F481918093AD +:10079000F10061507109F1CF21E0089709F020E0E4 +:1007A0008091E8008E778093E800CF01C3CF2111BC +:1007B000C4CFD3CF80911401882339F0853039F02C +:1007C0008091E80083FFC9CF04C082E0089583E0F0 +:1007D000089581E0089520911B0130911C01261796 +:1007E000370748F06115710539F42091E8002E773C +:1007F0002093E80001C0B90120E061157105D9F12D +:1008000030911401332309F444C0353009F443C056 +:100810003091E80033FD41C03091E80032FF06C05E +:100820008091E80082FF2AC080E008953091E800BE +:1008300030FFE3CF2091F20030E0FC01281B390BA0 +:10084000C9018E0F9F1F6115710551F08830910508 +:1008500038F484918093F100319661507109F0CFA2 +:1008600021E0089709F020E08091E8008E778093DE +:10087000E800CF01C2CF2111C3CFD2CF80911401A4 +:10088000882339F0853039F08091E80083FFC8CFA4 +:1008900004C082E0089583E0089581E00895982FD0 +:1008A000953058F59093E900981739F07091EC0065 +:1008B0002091ED005091F00003C0242F762F50E0DE +:1008C00021FF19C03091EB003E7F3093EB00309157 +:1008D000ED003D7F3093ED003091EB0031603093BF +:1008E000EB007093EC002093ED005093F00020910A +:1008F000EE0027FF07C09F5FD3CF8F708093E90082 +:1009000081E0089580E008958091150187FD05C07C +:100910008091E80080FF0EC012C08091E80082FD47 +:1009200005C0809114018111F8CF08958091E800ED +:100930008B7708C0809114018111EACF08958091CE +:10094000E8008E778093E80008958091E40090910C +:10095000E50045E62091EC0020FF21C02091E80051 +:1009600020FD21C020911401222389F0253089F037 +:100970002091EB0025FD0FC02091E4003091E500AF +:100980002817390739F3415041F0C901E3CF82E01C +:10099000089583E0089581E0089584E0089520910A +:1009A000E80022FFDFCF80E0089541D043D080915E +:1009B000D8008F778093D8008091D800806880938A +:1009C000D8008091D8008F7D8093D80084E089BDC5 +:1009D00086E089BD09B400FEFDCF1092140110928B +:1009E0001001109212011092110142E060E080E0CB +:1009F00056DF8091E1008E7F8093E1008091E200DC +:100A000081608093E2008091E20088608093E20040 +:100A10008091E0008E7F8093E0000895E3E6F0E0AF +:100A200080818E7F808381E080931301BECF1092FE +:100A3000E20008951092E10008951F920F920FB600 +:100A40000F9211242F933F934F935F936F937F9354 +:100A50008F939F93AF93BF93EF93FF938091E100A8 +:100A600082FF0AC08091E20082FF06C08091E1000F +:100A70008B7F8093E100D6D18091E10080FF17C089 +:100A80008091E20080FF13C08091E2008E7F80930E +:100A9000E2008091E20080618093E2008091D800C2 +:100AA00080628093D80019BC1092140197DB80916A +:100AB000E10084FF2FC08091E20084FF2BC084E01E +:100AC00089BD86E089BD09B400FEFDCF8091D800C4 +:100AD0008F7D8093D8008091E1008F7E8093E1002C +:100AE0008091E2008F7E8093E2008091E20081603D +:100AF0008093E20080911001882311F084E007C008 +:100B00008091E30087FF02C083E001C081E0809311 +:100B100014015FDB8091E10083FF22C08091E2003D +:100B200083FF1EC08091E100877F8093E10082E017 +:100B300080931401109210018091E1008E7F8093C8 +:100B4000E1008091E2008E7F8093E2008091E200DC +:100B500080618093E20042E060E080E0A0DE62D14C +:100B6000FF91EF91BF91AF919F918F917F916F9185 +:100B70005F914F913F912F910F900FBE0F901F905B +:100B800018951F93CF93DF93CDB7DEB7AA970FB613 +:100B9000F894DEBF0FBECDBFE5E1F1E08091F1003A +:100BA000819321E0ED31F207C9F73CD18091E80053 +:100BB00083FF20C19091150180911601853009F4C1 +:100BC00077C030F4813081F168F0833069F112C16F +:100BD000883009F4E1C0893009F4F0C0863009F0AA +:100BE00009C188C0903881F0923809F003C1809122 +:100BF00019018F708093E9008091EB0085FB8827B5 +:100C000080F91092E90006C08091110190911201C3 +:100C1000911182609091E800977F9093E800809313 +:100C2000F1001092F100C3C0292F2D7F09F0E2C01E +:100C3000992319F0923061F0DDC090911701913045 +:100C400009F0D8C0833009F090E0909312012AC0D7 +:100C500090911701911126C0209119012F7009F46C +:100C6000C9C02093E9009091EB0090FF1BC0833036 +:100C700021F48091EB00806213C08091EB008061D1 +:100C80008093EB0081E090E0022E01C0880F0A946F +:100C9000EAF78093EA001092EA008091EB00886006 +:100CA0008093EB001092E9008091E800877F82C07A +:100CB0009111A0C0109117011F778091E3008078F7 +:100CC000812B8093E3008091E800877F8093E80088 +:100CD0001BDE8091E80080FFFCCF8091E3008068FC +:100CE0008093E300112311F083E001C082E0809340 +:100CF000140180C09058923008F07CC08091170198 +:100D0000909118018C3D23E0920771F583E08A836E +:100D10008AE289834FB7F894DE01139620E03EE023 +:100D200051E2E32FF0E050935700E49120FF03C01D +:100D3000E295EF703F5FEF708E2F90E0EA3010F099 +:100D4000C79601C0C0968D939D932F5F243149F7BC +:100D50004FBF8091E800877F8093E8006AE270E0EF +:100D6000CE010196D5DC12C0AE014F5F5F4F60919E +:100D700019010CDC009709F43DC02091E800277FA1 +:100D80002093E800BC0189819A8125DD8091E800EB +:100D90008B778093E8002EC0903861F58091E80051 +:100DA000877F8093E800809110018093F10080910B +:100DB000E8008E778093E800A7DD1CC091111AC06F +:100DC000909117019230B0F48091E800877F809372 +:100DD000E8009093100198DD80911001811104C00A +:100DE0008091E30087FF02C084E001C081E080932E +:100DF0001401F9D98091E80083FF0AC08091E800CE +:100E0000877F8093E8008091EB0080628093EB0005 +:100E1000AA960FB6F894DEBF0FBECDBFDF91CF917B +:100E20001F9108950895CF93809114018823A9F00C +:100E30008091E9008F709091EC0090FF02C090E8E3 +:100E400001C090E0C92FC82B1092E9008091E80002 +:100E500083FD97DECF70C093E900CF910895F89499 +:020E6000FFCFC2 +:00000001FF \ No newline at end of file