I write this in response to a post here on the boards. This lump of code should make it easy to sense and respond to MIDI messages. This is not a complete sketch - it's a set of functions that you drive by pushing incoming bytes into pushByteIntoMIDIQueue(). To make something happen, write something useful into the empty functions at the end.
I haven't tested this at all as I do not have a MIDI keyboard lying around. I believe it should work correctly. Hope there are no silly errors in it
// this sketch in reply to http://forum.arduino.cc/index.php?topic=318597.0
// to make this stuff go, put the serial bytes into pushByteIntoMIDIQueue()
// as you get them. When stuff happens, the various empty functions
// at the end of this code get called
#define MIDI_WAIT_FOR_FIRST_BTYE 0
#define MIDI_WAIT_FOR_SECOND_BTYE 1
#define MIDI_WAIT_FOR_THIRD_BTYE 2
#define MIDI_READ_SYSTEM_EXCLUSIVE 4
int midistate = MIDI_WAIT_FOR_FIRST_BTYE;
byte byte1;
byte byte2;
byte byte3;
// http://www.midi.org/aboutmidi/tut_protocol.php
// http://www.midi.org/techspecs/midimessages.php
void pushByteIntoMIDIQueue(byte b) {
// ok! we have an incomig byte!
switch (midistate) {
case MIDI_WAIT_FOR_FIRST_BTYE: handle_byte_1(b); break;
case MIDI_WAIT_FOR_SECOND_BTYE:
handle_byte_2(b);
if (midistate == MIDI_WAIT_FOR_SECOND_BTYE) {
// we have a programming bug if this ever happens
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
}
break;
case MIDI_WAIT_FOR_THIRD_BTYE: handle_byte_3(b);
if (midistate == MIDI_WAIT_FOR_THIRD_BTYE) {
// we have a programming bug if this ever happens
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
}
break;
case MIDI_READ_SYSTEM_EXCLUSIVE:
if (b == 0b11110111) {
MIDI_end_of_sysex();
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
}
else if (b & 0b10000000) {
MIDI_bad_byte_in_sysex(b);
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
}
else {
MIDI_sysex_byte(b);
}
break;
default: // WTF??
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
break;
}
}
void handle_byte_1(byte b) {
byte1 = b;
int status = (b >> 4) & 0xF; // high nybble
if (status < 0b1000) {
MIDI_bad_status_byte(b);
return;
}
if (status < 0b1111) {
midistate = MIDI_WAIT_FOR_SECOND_BTYE;
return;
}
// ok. system common messages.
switch (b & 0xF) { // low nybble
case 0b0000:
midistate = MIDI_READ_SYSTEM_EXCLUSIVE;
MIDI_start_of_sysex();
return;
case 0b0001: //MIDI Time Code Quarter Frame.
case 0b0010: //Song Position Pointer.
case 0b0011:// Song Select.
midistate = MIDI_WAIT_FOR_SECOND_BTYE;
return;
case 0b0100:// Song Select.
case 0b0101:// Song Select.
MIDI_undefined_status_byte(b);
return;
case 0b0110:
MIDI_tune_request();
return;
case 0b0111:
// this should only happen during a system exclusive message
MIDI_bad_status_byte(b);
return;
case 0b1000:
MIDI_timing_clock();
return;
case 0b1001:
MIDI_undefined_status_byte(b);
return;
case 0b1010:
MIDI_timing_clock_start();
return;
case 0b1011:
MIDI_timing_clock_cotinue();
return;
case 0b1100:
MIDI_timing_clock_stop();
return;
case 0b1101:
MIDI_bad_status_byte(b);
return;
case 0b1110:
MIDI_active_sensing();
return;
case 0b1111:
MIDI_reset();
return;
}
}
void handle_byte_2(byte b) {
// this only happens when the high nybble of the first byte was 1000-1110
byte2 = b;
if (b & 0b10000000) {
MIDI_bad_second_byte(b);
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
return;
}
switch ((byte1 >> 4) & 0xF) {
case 0b1000:
case 0b1001:
case 0b1010:
case 0b1011:
case 0b1110:
midistate = MIDI_WAIT_FOR_THIRD_BTYE;
return;
case 0b1100:
MIDI_program_change(((int)byte1) & 0x0F, byte2);
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
return;
case 0b1101:
MIDI_channel_after_touch(((int)byte1) & 0x0F, byte2);
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
return;
}
}
void handle_byte_3(byte b) {
byte3 = b;
if (b & 0b10000000) {
MIDI_bad_third_byte(b);
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
return;
}
int channel = ((int)byte1) & 0x0F;
switch ((byte1 >> 4) & 0xF) {
case 0b1000:
MIDI_note_off(channel, byte2, byte3);
break;
case 0b1001:
MIDI_note_on(channel, byte2, byte3);
break;
case 0b1010:
MIDI_note_after_touch(channel, byte2, byte3);
break;
case 0b1011:
if (byte2 < 120) {
MIDI_control_change(channel, byte2, byte3);
}
else {
// midi channel mode message
switch (byte2) {
case 120: MIDI_all_sound_off(channel); break;
case 121: MIDI_reset_all_controllers(channel, byte3); break;
case 122:
if (byte3 == 0) MIDI_local_control_off(channel);
else if (byte3 == 127) MIDI_local_control_on(channel);
else MIDI_bad_third_byte(b);
break;
case 123: MIDI_all_notes_off(channel); break;
case 124: MIDI_all_notes_off_omni_off(channel); break;
case 125: MIDI_all_notes_off_omni_on(channel); break;
case 126: MIDI_all_notes_off_mono_on(channel, byte3); break;
case 127: MIDI_all_notes_off_poly_on(channel); break;
}
}
break;
case 0b1110:
MIDI_pitch_bend(channel, (((int)byte2) | (((int)byte3) << 7)) - 0x2000 );
break;
}
midistate = MIDI_WAIT_FOR_FIRST_BTYE;
}
/////////////
// Everything below this point are functions for you to fill in.
//
void MIDI_bad_status_byte(byte b) {}
void MIDI_undefined_status_byte(byte b) {}
void MIDI_start_of_sysex() {}
void MIDI_end_of_sysex() {}
void MIDI_sysex_byte(byte b) {}
void MIDI_bad_byte_in_sysex(byte b) {}
void MIDI_tune_request() {}
void MIDI_timing_clock() {}
void MIDI_timing_clock_start() {}
void MIDI_timing_clock_cotinue() {}
void MIDI_timing_clock_stop() {}
void MIDI_active_sensing() {}
void MIDI_reset() {}
void MIDI_bad_second_byte(byte b) {}
void MIDI_program_change(byte channel, byte patch) {}
void MIDI_channel_after_touch(int channel, int patch) {}
void MIDI_bad_third_byte(byte b) {}
void MIDI_note_off(byte channel, byte key, byte velocity) {}
void MIDI_note_on(byte channel, byte key, byte velocity) {}
void MIDI_note_after_touch(byte channel, byte key, byte pressure) {}
void MIDI_control_change(byte channel, byte controller, byte value) {}
void MIDI_all_sound_off(byte channel) {}
void MIDI_reset_all_controllers(byte channel, byte value) {}
void MIDI_local_control_off(byte channel) {}
void MIDI_local_control_on(byte channel) {}
void MIDI_all_notes_off(byte channel) {}
void MIDI_all_notes_off_omni_off(byte channel) {}
void MIDI_all_notes_off_omni_on(byte channel) {}
void MIDI_all_notes_off_mono_on(byte channel, byte num_channels) {}
void MIDI_all_notes_off_poly_on(byte channel) {}
void MIDI_pitch_bend(byte channel, int value) {}