Go Down

Topic: Decoding MIDI messages (Read 157 times) previous topic - next topic

PaulMurrayCbr

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 :)

Code: [Select]


// 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) {}

Nick Gammon

Just for comparison, this is my MIDI decoder sketch:

https://vimeo.com/80328016
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PaulMurrayCbr


Go Up