Close

(2) Reading MIDI input and output through MIDI shield on Arduino

A project log for Real-Time Audio-Controlled Neon

Building a system that encodes and reads music in MIDI format and controls the flashing of a Neon Sign with minimal latency

davidgargesdavid.garges 10/07/2018 at 23:320 Comments

I purchased a Olimex MIDI shield in order to be able to read raw MIDI data from the output of the Akai Drum Machine. At this point I wasn't sure what to expect at the output, my goal was just to be able to see something on the serial monitor via the Arduino IDE. I downloaded the Arduino MIDI library and I received an error that required me (through a google search) to purchase a specific Arduino Leonardo for reasons that I have understood later on in my development. 

With the arrival of my Arduino Leonardo, my attempt to utilize the Arduino MIDI library was still problematic. I was only able to read input signal from some instruments and not others - specifically the drum sounds that I wanted to listen to. I suspected that the instantiation of the MIDI listening port was only listening to a specific channel and ignoring another. I was not able to figure out how to change this within the source code.

I turned to the internet to learn more about the MIDI protocol. Sparkfun had an incredibly useful article on the history and the state diagrams of MIDI. I was able to understand how MIDI is build on the UART serial interface at a specific baud rate of 31250 and that there are certain 3-byte sequences that specify Note On/Off command, Channel, Note Value, and Volume Level. Forums online helped me realize that for drum sounds are standardized on channel 10 as: 

I decided to just use simple serial drivers to read byte values off of the buffer instead of using the MIDI library. Unfortunately the serial monitor's available baud rates and MIDI's baud rate (31250) were not compatible for viewing through the serial monitor. This meant that I could listen to the incoming signals, but the display to the serial monitor would be garbage. This is where the necessity of the Arduino Leonardo came into play: it has two serial ports. One for standard Tx and Rx ("Serial") and one for communicating over the USB ("Serial1"). This allowed me to listen at the MIDI baud rate and display at a rate that the serial monitor supported. I was able to successfully see the incoming commands as the drum machine outputted them with the following Arduino code:

void setup() {
  Serial.begin(9600);
  Serial1.begin(31250);
  byte Byte1;
  byte Byte2;
  byte Byte3;
}

void loop() {
  if (Serial1.available() > 0) {
    Byte1 = Byte2;
    Byte2 = Byte3;
    Byte3 = Serial1.read();
  
    if (Byte1 == 0x99 && Byte2 == 0x24){
      Serial.print("Kick Drum On  :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x89 && Byte2 == 0x24){
      Serial.print("Kick Drum Off :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    
    if (Byte1 == 0x99 && Byte2 == 0x26){
      Serial.print("Snare On  :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x89 && Byte2 == 0x26){
      Serial.print("Snare Off :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    
    if (Byte1 == 0x99 && Byte2 == 0x28){
      Serial.print("Clap On  :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x89 && Byte2 == 0x28){
      Serial.print("Clap Off :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x99 && Byte2 == 0x2A){
      Serial.print("Open Hat On  :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x89 && Byte2 == 0x2A){
      Serial.print("Open Hat Off :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x99 && Byte2 == 0x2C){
      Serial.print("Closed Hat On  :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
    if (Byte1 == 0x89 && Byte2 == 0x2C){
      Serial.print("Closed Hat Off :");
      Serial.print(Byte1);
      Serial.print(" ");
      Serial.print(Byte2);
      Serial.print(" ");
      Serial.println(Byte3);
    }
  }
}

I also had the ability to write MIDI to the drum machine but this write ability did not end up being utilized in the signal chain of my final project. 

void setup() {
  Serial1.begin(31250);
}

void loop() {
  delay(1000);
  Serial1.write(144); //Note on channel 1 1001.0000; 0x90; 144;
  Serial1.write(105);   //A 440             0110.1001; 0x69; 105;
  Serial1.write(127); //Max Volume        0111.1111; 0x7F; 127;         
  Serial1.write(0x90); //Note on channel 1 1001.0000; 0x90; 144;
  Serial1.write(0x69);   //A 440             0110.1001; 0x69; 105;
  Serial1.write(0x7F); //Max Volume        0111.1111; 0x7F; 127; 
}

Discussions