Created
January 15, 2020 23:31
-
-
Save dr-kd/d8dae0c21bf63ed3bd3c4e298190ea6a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Keypad.h> | |
const byte ROWS = 7; | |
const byte COLS = 5; | |
byte CHANNEL = 1; | |
char keys[COLS][ROWS] = { | |
{ 31, 32, 30, 10, 2, 28, 29 }, | |
{ 27, 7, 9, 25, 26, 24, 4 }, | |
{ 6, 22, 23, 21, 1, 3, 19 }, | |
{ 20, 18, 11, 0, 16, 17, 15 }, | |
{ 8, 33, 13, 14, 12, 5, 34 } | |
}; | |
// 33 and 34 octave keys. Others in order from a = 0 | |
byte rowPins[ROWS] = {11,10,9,8,7,6,5}; | |
byte colPins[COLS] = {0,1,2,3,4}; | |
// TODO - need to look up where the slider keys are too forgot to write them down. | |
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); | |
String msg; | |
int state; | |
int octave = 2; | |
int notes[127]; // store velocity or whatever current signal is in each slot (e.g. could be aftertouch). Slot number is current midi note. | |
char nowpressed[35]; // store last state in here | |
int currentCC = 7; // volume | |
// breath control stuff | |
#define ON_Thr 70 | |
#define ON_Delay 20 | |
#define breath_max 550 | |
#define BREATH_OFF 1 | |
#define RISE_WAIT 2 | |
#define BREATH_ON 3 | |
#define CC_INTERVAL 20 | |
#define ON_DELAY 20 | |
unsigned long ccSendTime = 0L; | |
unsigned long breath_on_time = 0L; | |
int initial_breath_value; | |
int pressureSensor; | |
byte velocity = 127; | |
int breathLevel = 0; | |
void setup() { | |
Serial.begin(9600); | |
state = BREATH_OFF; | |
} | |
int last_report = millis(); | |
bool cdebug = 0; | |
void check_debug () { | |
return; | |
int now = millis(); | |
if ( now - last_report > 1000) { | |
cdebug = 1; | |
last_report = millis(); | |
last_report = now; | |
} | |
else { | |
cdebug = 0; | |
} | |
} | |
void loop() { | |
check_debug(); | |
// Pre-calculate current range of nowplaying array | |
int offset = (octave * 12) + 21; | |
check_breath(); | |
bool changed = 0; // stores state if we need to change playing | |
if (kpd.getKeys()) { | |
for (int i=0; i < LIST_MAX; i++) { // Scan the whole key list. | |
if ( kpd.key[i].stateChanged ) { // Only find keys that have changed state | |
int thiskey = kpd.key[i].kchar; | |
nowpressed[thiskey] = kpd.key[i].kstate; | |
int lastoctave = octave; | |
int action = special_keys(thiskey, i); | |
switch ( action ) { | |
case 3: | |
octave--; | |
break; | |
case 4: | |
octave++; | |
break; | |
case 2: | |
// stop all notes | |
stop_all_notes(offset); | |
return; | |
case 1: // action 1 is a noop - when octave keys are released | |
return; | |
default: | |
break; | |
} | |
if ( ! maybe_adjust_octave(offset, lastoctave, notes) ) { | |
int keynum = thiskey + offset ; | |
switch(kpd.key[i].kstate) { | |
case PRESSED: | |
usbMIDI.sendNoteOn(keynum, velocity, CHANNEL); | |
notes[keynum] = velocity; | |
changed = 1; | |
break; | |
case RELEASED: | |
usbMIDI.sendNoteOff(keynum, velocity, CHANNEL); | |
notes[keynum] = 0; | |
changed = 1; | |
break; | |
default: // all other states noop | |
return; | |
} | |
} | |
} | |
} | |
} | |
} | |
/* int special_keys | |
Return 1 if action is a noop | |
Return 2 if action is to stop all notes | |
Return 3 if action is to go down an octave | |
Return 4 if action is to go up an octave | |
Relevant midi_cc list: | |
CC2 - Breath | |
CC7 - volume | |
*/ | |
int special_keys (char currkey, int i) { | |
// Octave keys | |
if ( currkey == 33 | |
&& octave < 6 // takes us up to G9 | |
&& kpd.key[i].kstate == PRESSED ) { | |
Serial.println("Oct up"); | |
octave = octave + 1; | |
return 4; | |
} | |
else if ( currkey == 34 | |
&& octave > 0 | |
&& kpd.key[i].kstate == PRESSED ) { | |
octave = octave - 1; | |
Serial.println("Oct down"); | |
return 3; | |
} | |
else if (currkey == 33 || currkey == 34) { | |
// noop | |
Serial.println("Oct release"); | |
return 1; | |
} | |
// special midi combos | |
else if ( nowpressed[1] == HOLD | |
&& nowpressed[2] == HOLD | |
&& nowpressed[3] == HOLD | |
) { | |
currentCC = 7; // patch volume | |
Serial.println("Coarse Vol"); | |
return 2; | |
} | |
else if ( nowpressed[4] == HOLD | |
&& nowpressed[5] == HOLD | |
&& nowpressed[6] == HOLD | |
) { | |
currentCC = 2; // midi cc | |
Serial.println("Breath CC"); | |
return 2; | |
} | |
else if ( nowpressed[7] == HOLD | |
&& nowpressed[8] == HOLD | |
&& nowpressed[9] == HOLD | |
) { | |
currentCC = 99; // kludge to support poly touch | |
Serial.println("Poly"); | |
return 2; | |
} | |
for (int i = 1; i < 10; i++) { | |
Serial.print(i); | |
Serial.print(":"); | |
switch (nowpressed[i]) { | |
case PRESSED: | |
Serial.print("p "); | |
break; | |
case HOLD: | |
Serial.print("h "); | |
break; | |
case RELEASED: | |
Serial.print("r "); | |
break; | |
default: | |
Serial.print(" "); | |
} | |
} | |
Serial.println(' '); | |
return 0; | |
} | |
/* maybe_adjust_octave | |
handle octave change i.e. transpose now playing to correct octave | |
*/ | |
bool maybe_adjust_octave (int offset, int lastoctave, int notes[]) { | |
bool changed = 0; | |
if (lastoctave != octave) { | |
// adjust notes for curent octave. | |
changed = 1; | |
int lastoffset = offset; | |
offset = (octave * 12) + 21; | |
for (int o = 0; o <= 32; o++) { | |
if (notes[ lastoffset + o] > 0) { | |
int lastnote = lastoffset+o; | |
int newnote = offset+o; | |
notes[ lastoffset + o ] = 0; | |
notes[ offset +o ] = velocity; | |
usbMIDI.sendNoteOn(newnote, velocity, CHANNEL); | |
usbMIDI.sendNoteOff(lastnote, velocity, CHANNEL); | |
} | |
} | |
} | |
return changed; | |
} | |
void check_breath () { | |
pressureSensor = analogRead(A0); | |
if (cdebug) { Serial.println(breathLevel); } | |
if (pressureSensor <= ON_Thr) { | |
if (cdebug) { Serial.println("Below threshold"); | |
} | |
state = BREATH_OFF; | |
return; | |
} | |
switch (state) { | |
case BREATH_OFF: | |
if (cdebug) { Serial.println("Note off"); } | |
breath_on_time = millis(); | |
initial_breath_value = pressureSensor; | |
state = RISE_WAIT; | |
return; | |
case RISE_WAIT: | |
if (cdebug) { Serial.println("RISE WAIT"); } | |
if (millis() - breath_on_time > ON_DELAY) { | |
// velocity = map(constrain(max(pressureSensor,initial_breath_value),ON_Thr,breath_max),ON_Thr,breath_max,7,127); | |
breathLevel = constrain(max(pressureSensor,initial_breath_value),ON_Thr,breath_max); | |
state = BREATH_ON; | |
} | |
return; | |
case BREATH_ON: | |
if (cdebug) { Serial.println("BREATH ON"); } | |
if (millis() - ccSendTime > CC_INTERVAL) { | |
breath(); | |
} | |
return; | |
} | |
} | |
void breath(){ | |
// Serial.println("In breath()"); | |
int breathCC; | |
breathLevel = breathLevel*0.8+pressureSensor*0.2; // smoothing of breathLevel value | |
breathCC = map(constrain(breathLevel,ON_Thr,breath_max),ON_Thr,breath_max,0,127); | |
if (currentCC != 99 ) { | |
usbMIDI.sendControlChange(currentCC, breathCC, CHANNEL); | |
} | |
else { | |
adjust_multipressure(breathCC); | |
} | |
ccSendTime = millis(); | |
} | |
void stop_all_notes(int offset) { | |
for (int o = 0; o <= 32; o++) { | |
int note = offset+o; | |
notes[ offset +o ] = 0 ; | |
usbMIDI.sendNoteOff(note, 0, CHANNEL); | |
} | |
} | |
void adjust_multipressure (int cc ) { | |
int offset = (octave * 12) + 22; | |
for (int o = 0; o <= 32; o++) { | |
int note = offset+o; | |
if ( notes[ offset +o ] > 0 ) { | |
usbMIDI.sendPolyPressure(note, cc, CHANNEL); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// copy pasted and modified from elsewhere to give it a decent midi name | |
// To give your project a unique name, this code must be | |
// placed into a .c file (its own tab). It can not be in | |
// a .cpp file or your main sketch (the .ino file). | |
#include "usb_names.h" | |
// Edit these lines to create your own name. The length must | |
// match the number of characters in your custom name. | |
#define MIDI_NAME {'M', 'i', 'k', 'o', 'r', 'd', 'i', 'n', 'a' } | |
#define MIDI_NAME_LEN 9 | |
// Do not change this part. This exact format is required by USB. | |
struct usb_string_descriptor_struct usb_string_product_name = { | |
2 + MIDI_NAME_LEN * 2, | |
3, | |
MIDI_NAME | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment