Skip to content

Instantly share code, notes, and snippets.

@dr-kd
Created January 15, 2020 23:31
Show Gist options
  • Save dr-kd/d8dae0c21bf63ed3bd3c4e298190ea6a to your computer and use it in GitHub Desktop.
Save dr-kd/d8dae0c21bf63ed3bd3c4e298190ea6a to your computer and use it in GitHub Desktop.
#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);
}
}
}
// 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