Reading MIDI from SD card for a digital output

Hello,
This involves a couple different topics so I figured general programming guidance would cover all my bases.

I am writing code for an arduino mega that is meant to read MIDI files from an SD card and then spit out a digital output for each "note" through the digital output pins. Things run test code totally fine and my hardware/wiring is all tested and verified. But when I put it all together with my completed code (shown below) I keep receiving a reset byte (255) as my first read byte from the line starting with:

Serial.println(incomingByte); //I believe line 72 in the code below

when I am expecting a command byte. Maybe because the first byte of a midi song file defaults to start from a blank slate? Unsure. This obviously bypasses all of my if/else statements so I do not get any deeper into my loops like I need to in order to play the notes. Any suggestions? Maybe I ignore the very first byte of each song?/how would I do that? I am open to ideas, expert opinions welcome (and preferred) :slight_smile:

//variables setup
#include <SD.h>
#include <SPI.h>

File songName;

byte incomingByte;
byte note;
byte velocity;

//MIDI pitch values for each "note"
int Aa [ ] = {0, 1, 12, 13, 24, 25, 36, 37, 48, 49, 60, 61, 72, 73, 84, 85, 96, 97, 108, 109, 120, 121}; //22 items in list
int Bb [ ] = {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}; 
int Cc [ ] = {3, 4, 15, 16, 27, 28, 39, 40, 51, 52, 63, 64, 75, 76, 87, 88, 99, 100, 111, 112, 123, 124};
int Dd [ ] = {5, 6, 17, 18, 29, 30, 41, 42, 53, 54, 65, 66, 77, 78, 89, 90, 101, 102, 113, 114, 125, 126};
int Ee [ ] = {7, 7, 19, 19, 31, 31, 43, 43, 55, 55, 67, 67, 79, 79, 91, 91, 103, 103, 115, 115, 127, 127};
int Ff [ ] = {8,  9, 20, 21, 32, 33, 44, 45, 56, 57, 68, 69, 80, 81, 92, 93, 104, 105, 116, 117, 128, 129};
int Gg [ ] = {10, 11, 22, 23, 34, 35, 46, 47, 58, 59, 70, 71, 82, 83, 94, 95, 106, 107, 118, 119, 130, 131};

const char* songsList [ ] = {"firstmid.mid","second.mid"}; //songlist on SD card
int songNums = 1; //number of songs on SD card

//create all motor outputs (relay signals (HIGH/LOW))
const int NoteA = 2;
const int NoteB = 3;
const int NoteC = 4;
const int NoteD = 5;
const int NoteE = 6;
const int NoteF = 7;
const int NoteG = 8;
int statusLed = LED_BUILTIN;   // select the pin for the LED

int action=2; //0 =note off ; 1=note on ; 2= nada


//setup: declaring iputs and outputs and begin serial
void setup() {
  pinMode(statusLed,OUTPUT);   // declare the LED's pin as output
  pinMode(NoteA,OUTPUT);
  pinMode(NoteB,OUTPUT);
  pinMode(NoteC,OUTPUT);
  pinMode(NoteD,OUTPUT);
  pinMode(NoteE,OUTPUT);
  pinMode(NoteF,OUTPUT);
  pinMode(NoteG,OUTPUT);

  
  //start serial with midi baudrate 31250 or 38400 for debugging
  Serial.begin(38400);        
  digitalWrite(statusLed,HIGH);  

  if (!SD.begin(53)) {//chip select pin on arduino mega is pin # 53
    Serial.println("initialization failed!");
    while (1);
    }
  Serial.println("initialization done.");
}

//loop: wait for serial data, and interpret the message
void loop () {
  digitalWrite(statusLed, LOW);
  for (int i = 0; i <= songNums; i++){ 
    songName = SD.open(songsList[i]);
    if (songName) {
    Serial.println("song:");
    }
    // read from the file until there's nothing else in it:
    while (songName.available()) {
      Serial.println(songsList[i]);
        // read the incoming byte:
        incomingByte = Serial.read(); ///COME ONNNNNNNNNNNNNNNBNBNNNNNNNNNNNNN FUUUUUCKKKKKKKK SOOOOO CLOOOOSSSEEEEEEEEEE
        Serial.println(incomingByte);
        // wait for as status-byte, channel 1, note on or off
        if (incomingByte== 144){ // note on message starting starting
          action=1;
        }
        else if (incomingByte== 128){ // note off message starting
          action=0;
        }
        else if (incomingByte== 208){ // aftertouch message starting
           //not implemented yet
        }
        else if (incomingByte== 160){ // polypressure message starting
           //not implemented yet
        }
        else if ( (action==0)&&(note==0) ){ // if we received a "note off", we wait for which note (databyte)
          note=incomingByte;
          playNote(note, 0);
          digitalWrite(statusLed, LOW);
          note=0;
          velocity=0;
          action=2;
        }
        else if ( (action==1)&&(note==0) ){ // if we received a "note on", we wait for the note (databyte)
          note=incomingByte;
        }
        else if ( (action==1)&&(note!=0) ){ // ...and then the velocity
          velocity=incomingByte;
          playNote(note, velocity);
          digitalWrite(statusLed, LOW);
          note=0;
          velocity=0;
          action=0;
        }
        else{
          //nada
        }
     // close the file:
     songName.close(); 
    }
  }
}


void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }
  else{
   value=LOW;
  }
 for (int count = 0; count <= 21; count++){
   if(note == Aa[count]){
      digitalWrite(NoteA, value);
      digitalWrite(statusLed, HIGH); 
      }
    if(note == Bb[count]){
      digitalWrite(NoteB, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Cc[count]){
      digitalWrite(NoteC, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Dd[count]){
      digitalWrite(NoteD, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Ee[count]){
      digitalWrite(NoteE, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Ff[count]){
      digitalWrite(NoteF, value); 
      digitalWrite(statusLed, HIGH);
      }    
    if(note == Gg[count]){
      digitalWrite(NoteG, value); 
      digitalWrite(statusLed, HIGH);
      }
    else {
      }
  
   }
 }

Not sure what MIDI files you are reading, but if they follow the MIDI file standard, these are not just a string of notes. There is a structure to the MIDI file that you need to understand and work with in order to run the notes sequence so that it is recognisable music.

I wrote a library some time ago that does this properly (https://github.com/MajicDesigns/MD_MIDIFile) or you can get it through the Arduino library manager. Library documentation is in the docs folder of the library - open index.html.

Thanks Marco, I actually already tried your library but I could not get it to work for me, probably because I have some knowledge gaps in the areas I need to tweak the code to work it for my purposes. I was hoping to accomplish this with minimal library use because of that, but would you be willing to help me out here if I go back to trying to use your library?

Did you try one of the library examples, like MD_MIDIFile_Dump.ino?

So I just tried that and I found that I got a compile error for mega250 (the arduino I am using) and now I cannot upload at all...this happened to me the last time I tried to run a piece of code from the MD_MIDIFile library so maybe some work has to be done to streamline that library a bit more?

I only say that because I encountered that same error when I was trying to upload MD_MIDIFile_Play to my mega prior to this topic and it scrambled something in my files so badly I couldn't even upload the blink test to an arduino uno (it returned that same compile error for uno and everything else after that). I ended up having to do a complete uninstall and reinstall of my ide and all my libraries in order to get my ide functioning normally again. This is also why I was hesitant to go with libraries because I don't know whats going on in there and now I have to do a complete un/reinstall again because it scrambled smth inside my files again when i tried to compile the dump example.

EDIT: I forgot to change Dump and show unused meta to 1 in the library settings! Will test and update promptly

EDIT II: That worked! Got dump working! Now how do I get MD_MIDIFile_Play to automatically play all the files on the SD card and send each note through my digital output in real time?

hey Marco_C,
any chance you could explain these lines to me? I don't know what pev is or what -> means...if I could understand how to capture those trigger moments and then relay them through my note arrays from my original code to trigger my motors on and off would that work?//How would I implement that?

#if USE_MIDI
  if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0))
  {
    Serial.write(pev->data[0] | pev->channel);
    Serial.write(&pev->data[1], pev->size-1);
  }
  else
    Serial.write(pev->data, pev->size);

Using the library is not that hard, you just need to get your head around it. Most of the complexity is actually hidden from you if you just use the interface methods. Get familiar with the documentation and read the blog post about it at https://arduinoplusplus.wordpress.com/2018/05/11/playing-midi-files-on-arduino-part-1-standard-midi-files/.

Starting from one of the examples that play back files from the SD card, your focus should be on the callback function. An example of how I used the callback in another project is at (MD_SN76489/MD_SN76489_MIDI_Player_CLI.ino at master · MajicDesigns/MD_SN76489 · GitHub) (the midiCallBack() function).

Ok I think I am starting to understand...but could you explain what exactly
*pev
is so can I know how to break that down (because from what I can see it looks like you were able to break whatever *pev was into usable data bytes, unless I misunderstand the code completely (which is definitely still possible) )

*pev is a pointer to a structure of data (midiEvent). The definition of that structure will be in the MD_MIDIFile header file and in the documentation for the library.
To access the fields in the data structure you use the notation pev->field.

You may want to read up on pointers.

/**
 * MIDI event definition structure
 *
 * Structure defining a MIDI event and its related data.
 * A pointer to this structure type is passed the the related callback function.
 */
typedef struct
{
  uint8_t track;    ///< the track this was on
  uint8_t channel;  ///< the midi channel
  uint8_t size;     ///< the number of data bytes
  uint8_t data[4];  ///< the data. Only 'size' bytes are valid
} midi_event;

Hey Marco,
First off thanks, I did more reading on pointers and reread your article from up above again about SMF format. If I am understanding this now correctly, can I insert my "scan for note and velocity values" function inside the FOR loop shown at the bottom of this midiCallBack() function?:

void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  DEBUG("\n");
  DEBUG(millis());
  DEBUG("\tM T");
  DEBUG(pev->track);
  DEBUG(":  Ch ");
  DEBUG(pev->channel+1);
  DEBUG(" Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
  DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
}

I would want to call a motor-relay function like the one below, replacing my if(note/velocity==... with if(DEBUGX(pev->data[i])== ...

In this case I would find out which "i" values represent the note and velocity bytes respectively, and then call each case using constant values for i.

Not sure if I explained myself well enough but...does this seem feasible?

void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }
  else{
   value=LOW;
  }
 for (int count = 0; count <= 21; count++){ ///this is because there are 22 note values that could trigger each motor (a few octaves of the same note plus sharps of the same note all send to one motor) (note values to be appropriately listed in arrays above setup)
   if(note == Aa[count]){
      digitalWrite(NoteA, value);
      digitalWrite(statusLed, HIGH); 
      }
    if(note == Bb[count]){
      digitalWrite(NoteB, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Cc[count]){
      digitalWrite(NoteC, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Dd[count]){
      digitalWrite(NoteD, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Ee[count]){
      digitalWrite(NoteE, value); 
      digitalWrite(statusLed, HIGH);
      }
    if(note == Ff[count]){
      digitalWrite(NoteF, value); 
      digitalWrite(statusLed, HIGH);
      }    
    if(note == Gg[count]){
      digitalWrite(NoteG, value); 
      digitalWrite(statusLed, HIGH);
      }
    else {
      }
  
   }
 }

EDIT: in this scenario NoteA-NoteG are digital output pins

I went ahead and tried integrating it...What am I doing wrong? It compiles just fine but my motors (LEDs for now to test) are not turning on at all.

// Test playing a succession of MIDI files from the SD card.
// Example program to demonstrate the use of the MIDFile library
// Just for fun light up a LED in time to the music.
//
// Hardware required:
//  SD card interface - change SD_SELECT for SPI comms
//  3 LEDs (optional) - to display current status and beat. 
//  Change pin definitions for specific hardware setup - defined below.

#include "SdFat.h"
#include <MD_MIDIFile.h>

#define DEBUG(x)  Serial.print(x)
#define DEBUGX(x) Serial.print(x, HEX)
#define DEBUGS(s) Serial.print(F(s))
#define SERIAL_RATE 57600


byte note;
byte velocity;

//create all motor outputs (relay signals (HIGH/LOW))
const int NoteA = 2;
const int NoteB = 3;
const int NoteC = 4;
const int NoteD = 5;
const int NoteE = 6;
const int NoteF = 7;
const int NoteG = 8;

//MIDI pitch values for each "note"
int Aa [ ] = {0, 1, 12, 13, 24, 25, 36, 37, 48, 49, 60, 61, 72, 73, 84, 85, 96, 97, 108, 109, 120, 121}; //22 items in list
int Bb [ ] = {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}; 
int Cc [ ] = {3, 4, 15, 16, 27, 28, 39, 40, 51, 52, 63, 64, 75, 76, 87, 88, 99, 100, 111, 112, 123, 124};
int Dd [ ] = {5, 6, 17, 18, 29, 30, 41, 42, 53, 54, 65, 66, 77, 78, 89, 90, 101, 102, 113, 114, 125, 126};
int Ee [ ] = {7, 7, 19, 19, 31, 31, 43, 43, 55, 55, 67, 67, 79, 79, 91, 91, 103, 103, 115, 115, 127, 127};
int Ff [ ] = {8,  9, 20, 21, 32, 33, 44, 45, 56, 57, 68, 69, 80, 81, 92, 93, 104, 105, 116, 117, 128, 129};
int Gg [ ] = {10, 11, 22, 23, 34, 35, 46, 47, 58, 59, 70, 71, 82, 83, 94, 95, 106, 107, 118, 119, 130, 131};


// SD chip select pin for SPI comms.
// Arduino Ethernet shield, pin 4.
// Default SD chip select is the SPI SS pin (10). (53 for mega)
// Other hardware will be different as documented for that hardware.
const uint8_t SD_SELECT = 53;

// LED definitions for status and user indicators
const uint8_t READY_LED = 9;      // when finished
const uint8_t SMF_ERROR_LED = 10;  // SMF error
const uint8_t SD_ERROR_LED = 11;   // SD error
const uint8_t BEAT_LED = LED_BUILTIN;       // toggles to the 'beat'

const uint16_t WAIT_DELAY = 2000; // ms

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))

// The files in the tune list should be located on the SD card 
// or an error will occur opening the file and the next in the 
// list will be opened (skips errors).
const char *tuneList[] = 
{
  "firstmid.mid",  // simplest and shortest file
  "second.mid",

};


SdFat  SD;
MD_MIDIFile SMF;



void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  DEBUG("\n");
  DEBUG(millis());
  DEBUG("\tM T");
  DEBUG(pev->track);
  DEBUG(":  Ch ");
  DEBUG(pev->channel+1);
  DEBUG(" Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
  DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
  playNote(pev->data[0], pev->data[1]);
}

void playNote(byte note, byte velocity)
{
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }
  else{
   value=LOW;
  }
 for (int count = 0; count <= 21; count++){
   if(note == Aa[count]){
      digitalWrite(NoteA, value);
      }
    if(note == Bb[count]){
      digitalWrite(NoteB, value); 
      }
    if(note == Cc[count]){
      digitalWrite(NoteC, value); 
      }
    if(note == Dd[count]){
      digitalWrite(NoteD, value); 
      }
    if(note == Ee[count]){
      digitalWrite(NoteE, value); 
      }
    if(note == Ff[count]){
      digitalWrite(NoteF, value); 
      }    
    if(note == Gg[count]){
      digitalWrite(NoteG, value); 
      }
    else {
      }
  
   }
 }

void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs 
// to be processed through the midi communications interface. Most sysex events cannot 
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function.
{
  DEBUG("\nS T");
  DEBUG(pev->track);
  DEBUG(": Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
    DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
}

void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
  midi_event ev;

  // All sound off
  // When All Sound Off is received all oscillators will turn off, and their volume
  // envelopes are set to zero as soon as possible.
  ev.size = 0;
  ev.data[ev.size++] = 0xb0;
  ev.data[ev.size++] = 120;
  ev.data[ev.size++] = 0;

  for (ev.channel = 0; ev.channel < 16; ev.channel++)
    midiCallback(&ev);
}



 

void setup(void)
{
  // Set up LED pins
  pinMode(READY_LED, OUTPUT);
  pinMode(SD_ERROR_LED, OUTPUT);
  pinMode(SMF_ERROR_LED, OUTPUT);
  pinMode(BEAT_LED, OUTPUT);

  //set up motor pins
  pinMode(NoteA,OUTPUT);
  pinMode(NoteB,OUTPUT);
  pinMode(NoteC,OUTPUT);
  pinMode(NoteD,OUTPUT);
  pinMode(NoteE,OUTPUT);
  pinMode(NoteF,OUTPUT);
  pinMode(NoteG,OUTPUT);

  // reset LEDs
  digitalWrite(READY_LED, LOW);
  digitalWrite(SD_ERROR_LED, LOW);
  digitalWrite(SMF_ERROR_LED, LOW);
  digitalWrite(BEAT_LED, LOW);
  
  Serial.begin(SERIAL_RATE);

  DEBUG("\n[MidiFile Play List]");

  // Initialize SD
  if (!SD.begin(SD_SELECT, SPI_FULL_SPEED))
  {
    DEBUG("\nSD init fail!");
    digitalWrite(SD_ERROR_LED, HIGH);
    while (true) ;
  }

  // Initialize MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.setSysexHandler(sysexCallback);

  digitalWrite(READY_LED, HIGH);
}

void tickMetronome(void)
// flash a LED to the beat
{
  static uint32_t lastBeatTime = 0;
  static boolean  inBeat = false;
  uint16_t  beatTime;

  beatTime = 60000/SMF.getTempo();    // msec/beat = ((60sec/min)*(1000 ms/sec))/(beats/min)
  if (!inBeat)
  {
    if ((millis() - lastBeatTime) >= beatTime)
    {
      lastBeatTime = millis();
      digitalWrite(BEAT_LED, HIGH);
      inBeat = true;
    }
  }
  else
  {
    if ((millis() - lastBeatTime) >= 100) // keep the flash on for 100ms only
    {
      digitalWrite(BEAT_LED, LOW);
      inBeat = false;
    }
  }
}

void loop(void)
{
  static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
  static uint16_t currTune = ARRAY_SIZE(tuneList);
  static uint32_t timeStart;

  switch (state)
  {
  case S_IDLE:    // now idle, set up the next tune
    {
      int err;

      DEBUGS("\nS_IDLE");

      digitalWrite(READY_LED, LOW);
      digitalWrite(SMF_ERROR_LED, LOW);

      currTune++;
      if (currTune >= ARRAY_SIZE(tuneList))
        currTune = 0;

      // use the next file name and play it
      DEBUG("\nFile: ");
      DEBUG(tuneList[currTune]);
      err = SMF.load(tuneList[currTune]);
      if (err != MD_MIDIFile::E_OK)
      {
        DEBUG(" - SMF load Error ");
        DEBUG(err);
        digitalWrite(SMF_ERROR_LED, HIGH);
        timeStart = millis();
        state = S_WAIT_BETWEEN;
        DEBUGS("\nWAIT_BETWEEN");
      }
      else
      {
        DEBUGS("\nS_PLAYING");
        state = S_PLAYING;
      }
    }
    break;

  case S_PLAYING: // play the file
    DEBUGS("\nS_PLAYING");
    if (!SMF.isEOF())
    {
      if (SMF.getNextEvent())
        tickMetronome();
    }
    else
      state = S_END;
    break;

  case S_END:   // done with this one
    DEBUGS("\nS_END");
    SMF.close();
    midiSilence();
    timeStart = millis();
    state = S_WAIT_BETWEEN;
    DEBUGS("\nWAIT_BETWEEN");
    break;

  case S_WAIT_BETWEEN:    // signal finished with a dignified pause
    digitalWrite(READY_LED, HIGH);
    if (millis() - timeStart >= WAIT_DELAY)
      state = S_IDLE;
    break;

  default:
    state = S_IDLE;
    break;
  }
}

I think that could work. Hard to tell without the rest of the code or really understanding what you are trying to achieve with your note arrays.

Thanks! Somehow I still must've messed something up because my digital output pins are still not receiving signal and thus not turning on. Would extra appreciate any insight//help here...feels like the last hurdle to figure out.

Full code is posted in reply #11 up above...my arrays are just lists that hold the MIDI value of each note A-G (plus the sharp next door) for 11 octaves. the duplicates listed for Bb and Ee are because those notes do not have affiliated sharps.

I think how you handle the data can be improved and be made more robust. I would write the code like this (not compiled and not tested):

// Test playing a succession of MIDI files from the SD card.
// Example program to demonstrate the use of the MIDFile library
// Just for fun light up a LED in time to the music.
//
// Hardware required:
//  SD card interface - change SD_SELECT for SPI comms
//  3 LEDs (optional) - to display current status and beat. 
//  Change pin definitions for specific hardware setup - defined below.

#include "SdFat.h"
#include <MD_MIDIFile.h>

#define DEBUG(x)  Serial.print(x)
#define DEBUGX(x) Serial.print(x, HEX)
#define DEBUGS(s) Serial.print(F(s))
#define SERIAL_RATE 57600


byte note;
byte velocity;

#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

const uint8_t NOTE_SIZE = 22;

// Define a structure to contain all the related data for executing a note
typedef struct 
{
  const int pin;              // motor output relay pin
  const int note[NOTE_SIZE];  // MIDI pitch value for each "note"
} noteData_t;

noteData_t noteData[] = 
{
  { 2, {0, 1, 12, 13, 24, 25, 36, 37, 48, 49, 60, 61, 72, 73, 84, 85, 96, 97, 108, 109, 120, 121}},  // note Aa
  { 3, {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}},  // note Bb
  { 4, {3, 4, 15, 16, 27, 28, 39, 40, 51, 52, 63, 64, 75, 76, 87, 88, 99, 100, 111, 112, 123, 124}},  // note Cc
  { 5, {5, 6, 17, 18, 29, 30, 41, 42, 53, 54, 65, 66, 77, 78, 89, 90, 101, 102, 113, 114, 125, 126}},  // note Dd
  { 6, {7, 7, 19, 19, 31, 31, 43, 43, 55, 55, 67, 67, 79, 79, 91, 91, 103, 103, 115, 115, 127, 127}},  // note Ee
  { 7, {8,  9, 20, 21, 32, 33, 44, 45, 56, 57, 68, 69, 80, 81, 92, 93, 104, 105, 116, 117, 128, 129}},  // note Ff
  { 8, {10, 11, 22, 23, 34, 35, 46, 47, 58, 59, 70, 71, 82, 83, 94, 95, 106, 107, 118, 119, 130, 131}},  // note Gg
}

// SD chip select pin for SPI comms.
// Arduino Ethernet shield, pin 4.
// Default SD chip select is the SPI SS pin (10). (53 for mega)
// Other hardware will be different as documented for that hardware.
const uint8_t SD_SELECT = 53;

// LED definitions for status and user indicators
const uint8_t READY_LED = 9;      // when finished
const uint8_t SMF_ERROR_LED = 10;  // SMF error
const uint8_t SD_ERROR_LED = 11;   // SD error
const uint8_t BEAT_LED = LED_BUILTIN;       // toggles to the 'beat'

const uint16_t WAIT_DELAY = 2000; // ms

// The files in the tune list should be located on the SD card 
// or an error will occur opening the file and the next in the 
// list will be opened (skips errors).
const char *tuneList[] = 
{
  "firstmid.mid",  // simplest and shortest file
  "second.mid",
};

SdFat  SD;
MD_MIDIFile SMF;

void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  DEBUG("\n");
  DEBUG(millis());
  DEBUG("\tM T");
  DEBUG(pev->track);
  DEBUG(":  Ch ");
  DEBUG(pev->channel+1);
  DEBUG(" Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
  DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
  playNote(pev->data[0], pev->data[1]);  // YOU SHOULD ONLY DO THIS FOR A NOTE ON OR OFF. Events are more than just on and off.
}

void playNote(byte note, byte velocity)
{
  int state = (velocity >10) ? HIGH : LOW;   // ternary operator
  bool found = false;   // set to true when the note has been found

  for (uint8_t n = 0; n < ARRAY_SIZE(noteData) && !found; n++)    // step through all the noteData entries
  {
    for (uint8_t count = 0; count <= NOTE_SIZE && !found; count++)  // step through each note for each entry
    {
      if (note == noteData[n].note[count])
      {
        found = true;
        digitalWrite(noteData[n].pin, value);
      }
    }
  }
  if (!found) 
  {
    DEBUG("\nNote ");
    DEBUG(note);
    DEBUG(" note found.");
  }
}

void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs 
// to be processed through the midi communications interface. Most sysex events cannot 
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function.
{
  DEBUG("\nS T");
  DEBUG(pev->track);
  DEBUG(": Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
    DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
}

void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
  midi_event ev;

  // All sound off
  // When All Sound Off is received all oscillators will turn off, and their volume
  // envelopes are set to zero as soon as possible.
  ev.size = 0;
  ev.data[ev.size++] = 0xb0;
  ev.data[ev.size++] = 120;
  ev.data[ev.size++] = 0;

  for (ev.channel = 0; ev.channel < 16; ev.channel++)
    midiCallback(&ev);
}



 

void setup(void)
{
  // Set up LED pins
  pinMode(READY_LED, OUTPUT);
  pinMode(SD_ERROR_LED, OUTPUT);
  pinMode(SMF_ERROR_LED, OUTPUT);
  pinMode(BEAT_LED, OUTPUT);

  //set up motor pins
  for (uint8_t i = 0; i < ARRAY_SIZE(noteData); i++)
  {
    pinMode(noteData[i].pin,OUTPUT);
    digitalWrite(noteData[i].pin, LOW);
  }

  // reset LEDs
  digitalWrite(READY_LED, LOW);
  digitalWrite(SD_ERROR_LED, LOW);
  digitalWrite(SMF_ERROR_LED, LOW);
  digitalWrite(BEAT_LED, LOW);
  
  Serial.begin(SERIAL_RATE);

  DEBUG("\n[MidiFile Play List]");

  // Initialize SD
  if (!SD.begin(SD_SELECT, SPI_FULL_SPEED))
  {
    DEBUG("\nSD init fail!");
    digitalWrite(SD_ERROR_LED, HIGH);
    while (true) ;
  }

  // Initialize MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.setSysexHandler(sysexCallback);

  digitalWrite(READY_LED, HIGH);
}

void tickMetronome(void)
// flash a LED to the beat
{
  static uint32_t lastBeatTime = 0;
  static boolean  inBeat = false;
  uint16_t  beatTime;

  beatTime = 60000/SMF.getTempo();    // msec/beat = ((60sec/min)*(1000 ms/sec))/(beats/min)
  if (!inBeat)
  {
    if ((millis() - lastBeatTime) >= beatTime)
    {
      lastBeatTime = millis();
      digitalWrite(BEAT_LED, HIGH);
      inBeat = true;
    }
  }
  else
  {
    if ((millis() - lastBeatTime) >= 100) // keep the flash on for 100ms only
    {
      digitalWrite(BEAT_LED, LOW);
      inBeat = false;
    }
  }
}

void loop(void)
{
  static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
  static uint16_t currTune = ARRAY_SIZE(tuneList);
  static uint32_t timeStart;

  switch (state)
  {
  case S_IDLE:    // now idle, set up the next tune
    {
      int err;

      DEBUGS("\nS_IDLE");

      digitalWrite(READY_LED, LOW);
      digitalWrite(SMF_ERROR_LED, LOW);

      currTune++;
      if (currTune >= ARRAY_SIZE(tuneList))
        currTune = 0;

      // use the next file name and play it
      DEBUG("\nFile: ");
      DEBUG(tuneList[currTune]);
      err = SMF.load(tuneList[currTune]);
      if (err != MD_MIDIFile::E_OK)
      {
        DEBUG(" - SMF load Error ");
        DEBUG(err);
        digitalWrite(SMF_ERROR_LED, HIGH);
        timeStart = millis();
        state = S_WAIT_BETWEEN;
        DEBUGS("\nWAIT_BETWEEN");
      }
      else
      {
        DEBUGS("\nS_PLAYING");
        state = S_PLAYING;
      }
    }
    break;

  case S_PLAYING: // play the file
    DEBUGS("\nS_PLAYING");
    if (!SMF.isEOF())
    {
      if (SMF.getNextEvent())
        tickMetronome();
    }
    else
      state = S_END;
    break;

  case S_END:   // done with this one
    DEBUGS("\nS_END");
    SMF.close();
    midiSilence();
    timeStart = millis();
    state = S_WAIT_BETWEEN;
    DEBUGS("\nWAIT_BETWEEN");
    break;

  case S_WAIT_BETWEEN:    // signal finished with a dignified pause
    digitalWrite(READY_LED, HIGH);
    if (millis() - timeStart >= WAIT_DELAY)
      state = S_IDLE;
    break;

  default:
    state = S_IDLE;
    break;
  }
}

This is a lot more compact and less prone to errors. There are probably a few new concepts for you in this, but you should read up on defining structures and also what a ternary operator is.

One thing to be aware of. In this data:

  { 3, {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}},  // note Bb

You will only ever find the first '2' and the first '14', '38, etc in the list because the search is linear and stops at the first match.

That should be totally fine for what I am doing because the duplicates are only there to make a consistent array size for each note/pitch (bc b and e dont have sharps).

I did some more reading on structures and how this new implementation works and I think I have it figured out and all edited correctly but my hardware is still not working for some reason. It compiles fine but when it runs I don't see my LED output pins turning on or off at all...I feel close now but I can't figure out where the snag is happening so I would super appreciate a fine tooth combover of the code below.

// Test playing a succession of MIDI files from the SD card.
// Example program to demonstrate the use of the MIDFile library
// Just for fun light up a LED in time to the music.
//
// Hardware required:
//  SD card interface - change SD_SELECT for SPI comms
//  3 LEDs (optional) - to display current status and beat. 
//  Change pin definitions for specific hardware setup - defined below.

#include "SdFat.h"
#include <MD_MIDIFile.h>

#define DEBUG(x)  Serial.print(x)
#define DEBUGX(x) Serial.print(x, HEX)
#define DEBUGS(s) Serial.print(F(s))
#define SERIAL_RATE 57600


#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

const uint8_t NOTE_SIZE = 22; //number of items in each note listing

// Define a structure to contain all the related data for executing a note
typedef struct 
{
  const int pin;              // motor output relay pin
  const int note[NOTE_SIZE];  // MIDI pitch value for each "note"
} noteData_t;

noteData_t noteData[] = 
{
  { 2, {0, 1, 12, 13, 24, 25, 36, 37, 48, 49, 60, 61, 72, 73, 84, 85, 96, 97, 108, 109, 120, 121}},  // note Aa
  { 3, {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}},  // note Bb
  { 4, {3, 4, 15, 16, 27, 28, 39, 40, 51, 52, 63, 64, 75, 76, 87, 88, 99, 100, 111, 112, 123, 124}},  // note Cc
  { 5, {5, 6, 17, 18, 29, 30, 41, 42, 53, 54, 65, 66, 77, 78, 89, 90, 101, 102, 113, 114, 125, 126}},  // note Dd
  { 6, {7, 7, 19, 19, 31, 31, 43, 43, 55, 55, 67, 67, 79, 79, 91, 91, 103, 103, 115, 115, 127, 127}},  // note Ee
  { 7, {8,  9, 20, 21, 32, 33, 44, 45, 56, 57, 68, 69, 80, 81, 92, 93, 104, 105, 116, 117, 128, 129}},  // note Ff
  { 8, {10, 11, 22, 23, 34, 35, 46, 47, 58, 59, 70, 71, 82, 83, 94, 95, 106, 107, 118, 119, 130, 131}},  // note Gg
};

// SD chip select pin for SPI comms.
// Arduino Ethernet shield, pin 4.
// Default SD chip select is the SPI SS pin (10). (53 for mega)
// Other hardware will be different as documented for that hardware.
const uint8_t SD_SELECT = 53;

// LED definitions for status and user indicators
const uint8_t READY_LED = 9;      // when finished
const uint8_t SMF_ERROR_LED = 10;  // SMF error
const uint8_t SD_ERROR_LED = 11;   // SD error
const uint8_t BEAT_LED = LED_BUILTIN;       // toggles to the 'beat'

const uint16_t WAIT_DELAY = 2000; // ms

// The files in the tune list should be located on the SD card 
// or an error will occur opening the file and the next in the 
// list will be opened (skips errors).
const char *tuneList[] = 
{
  "firstmid.mid",  // simplest and shortest file
  "second.mid",
};

SdFat  SD;
MD_MIDIFile SMF;

void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  DEBUG("\n");
  DEBUG(millis());
  DEBUG("\tM T");
  DEBUG(pev->track);
  DEBUG(":  Ch ");
  DEBUG(pev->channel+1);
  DEBUG(" Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
  DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
  if(DEBUGX(pev->data[0]) == 80 or DEBUGX(pev->data[0]) == 90);{
    playNote(DEBUGX(pev->data[1]), DEBUGX(pev->data[2]));
    }
}

void playNote(int pitch, int velocity)
{
  int state = (velocity >10) ? HIGH : LOW;   // ternary operator
  bool found = false;   // set to true when the note has been found

  for (uint8_t n = 0; n < ARRAY_SIZE(noteData) && !found; n++)    // step through all the noteData entries
  {
    for (uint8_t m = 0; m <= NOTE_SIZE && !found; m++)  // step through each note for each entry
    {
      if (pitch == noteData[n].note[m])
      {
        found = true;
        digitalWrite(noteData[n].pin, state);
      }
    }
  }
  if (!found) 
  {
    DEBUG("\nNote ");
    DEBUG(pitch);
    DEBUG(" note found.");
  }
}

void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs 
// to be processed through the midi communications interface. Most sysex events cannot 
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function.
{
  DEBUG("\nS T");
  DEBUG(pev->track);
  DEBUG(": Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
    DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
}

void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
  midi_event ev;

  // All sound off
  // When All Sound Off is received all oscillators will turn off, and their volume
  // envelopes are set to zero as soon as possible.
  ev.size = 0;
  ev.data[ev.size++] = 0xb0;
  ev.data[ev.size++] = 120;
  ev.data[ev.size++] = 0;

  for (ev.channel = 0; ev.channel < 16; ev.channel++)
    midiCallback(&ev);
}


void tickMetronome(void)
// flash a LED to the beat
{
  static uint32_t lastBeatTime = 0;
  static boolean  inBeat = false;
  uint16_t  beatTime;

  beatTime = 60000/SMF.getTempo();    // msec/beat = ((60sec/min)*(1000 ms/sec))/(beats/min)
  if (!inBeat)
  {
    if ((millis() - lastBeatTime) >= beatTime)
    {
      lastBeatTime = millis();
      digitalWrite(BEAT_LED, HIGH);
      inBeat = true;
    }
  }
  else
  {
    if ((millis() - lastBeatTime) >= 100) // keep the flash on for 100ms only
    {
      digitalWrite(BEAT_LED, LOW);
      inBeat = false;
    }
  }
}



void setup(void)
{
  // Set up LED pins
  pinMode(READY_LED, OUTPUT);
  pinMode(SD_ERROR_LED, OUTPUT);
  pinMode(SMF_ERROR_LED, OUTPUT);
  pinMode(BEAT_LED, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  

  // reset LEDs
  digitalWrite(READY_LED, LOW);
  digitalWrite(SD_ERROR_LED, LOW);
  digitalWrite(SMF_ERROR_LED, LOW);
  digitalWrite(BEAT_LED, LOW);
  
  Serial.begin(SERIAL_RATE);

  DEBUG("\n[MidiFile Play List]");

  // Initialize SD
  if (!SD.begin(SD_SELECT, SPI_FULL_SPEED))
  {
    DEBUG("\nSD init fail!");
    digitalWrite(SD_ERROR_LED, HIGH);
    while (true) ;
  }

  // Initialize MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.setSysexHandler(sysexCallback);

  digitalWrite(READY_LED, HIGH);

}


void loop(void) 
{
  static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
  static uint16_t currTune = ARRAY_SIZE(tuneList);
  static uint32_t timeStart;

  switch (state)
  {
  case S_IDLE:    // now idle, set up the next tune
    {
      int err;

      DEBUGS("\nS_IDLE");

      digitalWrite(READY_LED, LOW);
      digitalWrite(SMF_ERROR_LED, LOW);

      currTune++;
      if (currTune >= ARRAY_SIZE(tuneList))
        currTune = 0;

      // use the next file name and play it
      DEBUG("\nFile: ");
      DEBUG(tuneList[currTune]);
      err = SMF.load(tuneList[currTune]);
      if (err != MD_MIDIFile::E_OK)
      {
        DEBUG(" - SMF load Error ");
        DEBUG(err);
        digitalWrite(SMF_ERROR_LED, HIGH);
        timeStart = millis();
        state = S_WAIT_BETWEEN;
        DEBUGS("\nWAIT_BETWEEN");
      }
      else
      {
        DEBUGS("\nS_PLAYING");
        state = S_PLAYING;
      }
    }
    break;

  case S_PLAYING: // play the file
    DEBUGS("\nS_PLAYING");
    if (!SMF.isEOF())
    {
      if (SMF.getNextEvent())
        tickMetronome();
    }
    else
      state = S_END;
    break;

  case S_END:   // done with this one
    DEBUGS("\nS_END");
    SMF.close();
    midiSilence();
    timeStart = millis();
    state = S_WAIT_BETWEEN;
    DEBUGS("\nWAIT_BETWEEN");
    break;

  case S_WAIT_BETWEEN:    // signal finished with a dignified pause
    digitalWrite(READY_LED, HIGH);
    if (millis() - timeStart >= WAIT_DELAY)
      state = S_IDLE;
    break;

  default:
    state = S_IDLE;
    break;
  }
}

EDIT: After parsing through the serial monitor, my playNote() function only seems to be getting called at the end of each file in a giant mash, seemingly responding to the midiSilence() function. I only know this because I inserted a Serial.println() function at the end of my notePlay() function and it only got printed at the end as stated above, instead of every time a noteON or noteOFF was received. Still no response from my LED output though. Still stuck! Help!

I think you don't fully understand DEBUG(). This is a macro that will print debug statements, nothing else. It is a replacement for Serial.print() that you can eliminate by changing the macro (as it would have been in the original code that you mashed) using a global #define. It has no place here - would you have written this code?

  if(Serial.print(pev->data[0]) == 80 or Serial.print(pev->data[0]) == 90);{
    playNote(Serial.print(pev->data[1]), Serial.print(pev->data[2]));
    }

Also,

  • you need to lose the ; after the if condition - you have created a null statement for the if.
  • what is an 'or'? The correct syntax is ||. Not sure how this even compiles.
  • you may find it easier to learn code if braces {} are on separate lines starting and ending blocks as this makes the line ending on the previous line explicit.
  • don't be afraid to use spaces to separate elements in the code. Sentencesarenotwrittenlikethis and neither should code if you want to understand it easily.
  if (pev->data[0]) == 80 || pev->data[0]) == 90)
  {
    playNote(pev->data[1], pev->data[2]);
  }

Your setup() should be using a loop to initialise the outputs, like in my example to you.

Hahahaha hell no. Thanks for that insight now that you said so it of course makes sense...I fixed the syntax errors and made things a little more clear in my code but it is still not working and I am confused as to where that is happening. Do I have to call my playNote() as the actual setMIDIHandler() function? I thought that because my playNote() was inside of the midiCallback() function that I would be ok but clearly something is still not adding up..any chance that would be the solution? Or is there any other glaring misunderstanding somewhere?

Are you getting ANY debug output from the midiCallback() other than when it is invoked by midiSilence()? You should be getting something EVERY time it is called. Maybe include what debug output you are getting so I can see it.

It would also be good to confirm that the MD_MIDIFile example programs run and will show debug info as expected. Often the problems are with the SD card access.

OK here is my code again (with the edits you suggested from up above) And I can confirm that the MD_MIDIFile dump file works perfectly with the same sd card.

// Test playing a succession of MIDI files from the SD card.
// Example program to demonstrate the use of the MIDFile library
// Just for fun light up a LED in time to the music.
//
// Hardware required:
//  SD card interface - change SD_SELECT for SPI comms
//  3 LEDs (optional) - to display current status and beat. 
//  Change pin definitions for specific hardware setup - defined below.

#include "SdFat.h"
#include <MD_MIDIFile.h>

#define DEBUG(x)  Serial.print(x)
#define DEBUGX(x) Serial.print(x, HEX)
#define DEBUGS(s) Serial.print(F(s))
#define SERIAL_RATE 57600


#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

const uint8_t NOTE_SIZE = 22; //number of items in each note listing

// Define a structure to contain all the related data for executing a note
typedef struct 
{
  const int pin;              // motor output relay pin
  const int note[NOTE_SIZE];  // MIDI pitch value for each "note"
} noteData_t;

noteData_t noteData[] = 
{
  { 2, {0, 1, 12, 13, 24, 25, 36, 37, 48, 49, 60, 61, 72, 73, 84, 85, 96, 97, 108, 109, 120, 121}},  // note Aa
  { 3, {2, 2, 14, 14, 26, 26, 38, 38, 50, 50, 62, 62, 74, 74, 86, 86, 98, 98, 110, 110, 122, 122}},  // note Bb
  { 4, {3, 4, 15, 16, 27, 28, 39, 40, 51, 52, 63, 64, 75, 76, 87, 88, 99, 100, 111, 112, 123, 124}},  // note Cc
  { 5, {5, 6, 17, 18, 29, 30, 41, 42, 53, 54, 65, 66, 77, 78, 89, 90, 101, 102, 113, 114, 125, 126}},  // note Dd
  { 6, {7, 7, 19, 19, 31, 31, 43, 43, 55, 55, 67, 67, 79, 79, 91, 91, 103, 103, 115, 115, 127, 127}},  // note Ee
  { 7, {8,  9, 20, 21, 32, 33, 44, 45, 56, 57, 68, 69, 80, 81, 92, 93, 104, 105, 116, 117, 128, 129}},  // note Ff
  { 8, {10, 11, 22, 23, 34, 35, 46, 47, 58, 59, 70, 71, 82, 83, 94, 95, 106, 107, 118, 119, 130, 131}},  // note Gg
};

// SD chip select pin for SPI comms.
// Arduino Ethernet shield, pin 4.
// Default SD chip select is the SPI SS pin (10). (53 for mega)
// Other hardware will be different as documented for that hardware.
const uint8_t SD_SELECT = 53;

// LED definitions for status and user indicators
const uint8_t READY_LED = 9;      // when finished
const uint8_t SMF_ERROR_LED = 10;  // SMF error
const uint8_t SD_ERROR_LED = 11;   // SD error
const uint8_t BEAT_LED = LED_BUILTIN;       // toggles to the 'beat'

const uint16_t WAIT_DELAY = 2000; // ms

// The files in the tune list should be located on the SD card 
// or an error will occur opening the file and the next in the 
// list will be opened (skips errors).
const char *tuneList[] = 
{
  "firstmid.mid",  // simplest and shortest file
  "second.mid",
};

SdFat  SD;
MD_MIDIFile SMF;

void midiCallback(midi_event *pev)
// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
{
  DEBUG("\n");
  DEBUG(millis());
  DEBUG("\tM T");
  DEBUG(pev->track);
  DEBUG(":  Ch ");
  DEBUG(pev->channel+1);
  DEBUG(" Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
  DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
  if(pev->data[0] == 80 || pev->data[0] == 90)
  {
    playNote(pev->data[1], pev->data[2]);
    }
}



void playNote(int pitch, int velocity)
{
  int state = (velocity >10) ? HIGH : LOW;   // ternary operator
  bool found = false;   // set to true when the note has been found

  for (uint8_t n = 0; n < ARRAY_SIZE(noteData) && !found; n++)    // step through all the noteData entries
  {
    for (uint8_t m = 0; m <= NOTE_SIZE && !found; m++)  // step through each note for each entry
    {
      if (pitch == noteData[n].note[m])
      {
        found = true;
        digitalWrite(noteData[n].pin, state);
        Serial.println("ALMOSTTTTTTTTTTTTTTTT");
      }
    }
  }
  if (!found) 
  {
    DEBUG("\nNote ");
    DEBUG(pitch);
    DEBUG(" note found.");
  }
}

void sysexCallback(sysex_event *pev)
// Called by the MIDIFile library when a system Exclusive (sysex) file event needs 
// to be processed through the midi communications interface. Most sysex events cannot 
// really be processed, so we just ignore it here.
// This callback is set up in the setup() function.
{
  DEBUG("\nS T");
  DEBUG(pev->track);
  DEBUG(": Data ");
  for (uint8_t i=0; i<pev->size; i++)
  {
    DEBUGX(pev->data[i]);
    DEBUG(' ');
  }
}

void midiSilence(void)
// Turn everything off on every channel.
// Some midi files are badly behaved and leave notes hanging, so between songs turn
// off all the notes and sound
{
  midi_event ev;

  // All sound off
  // When All Sound Off is received all oscillators will turn off, and their volume
  // envelopes are set to zero as soon as possible.
  ev.size = 0;
  ev.data[ev.size++] = 0xb0;
  ev.data[ev.size++] = 120;
  ev.data[ev.size++] = 0;

  for (ev.channel = 0; ev.channel < 16; ev.channel++)
    midiCallback(&ev);
}


void tickMetronome(void)
// flash a LED to the beat
{
  static uint32_t lastBeatTime = 0;
  static boolean  inBeat = false;
  uint16_t  beatTime;

  beatTime = 60000/SMF.getTempo();    // msec/beat = ((60sec/min)*(1000 ms/sec))/(beats/min)
  if (!inBeat)
  {
    if ((millis() - lastBeatTime) >= beatTime)
    {
      lastBeatTime = millis();
      digitalWrite(BEAT_LED, HIGH);
      inBeat = true;
    }
  }
  else
  {
    if ((millis() - lastBeatTime) >= 100) // keep the flash on for 100ms only
    {
      digitalWrite(BEAT_LED, LOW);
      inBeat = false;
    }
  }
}



void setup(void)
{
  // Set up LED pins
  pinMode(READY_LED, OUTPUT);
  pinMode(SD_ERROR_LED, OUTPUT);
  pinMode(SMF_ERROR_LED, OUTPUT);
  pinMode(BEAT_LED, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  

  // reset LEDs
  digitalWrite(READY_LED, LOW);
  digitalWrite(SD_ERROR_LED, LOW);
  digitalWrite(SMF_ERROR_LED, LOW);
  digitalWrite(BEAT_LED, LOW);
  
  Serial.begin(SERIAL_RATE);

  DEBUG("\n[MidiFile Play List]");

  // Initialize SD
  if (!SD.begin(SD_SELECT, SPI_FULL_SPEED))
  {
    DEBUG("\nSD init fail!");
    digitalWrite(SD_ERROR_LED, HIGH);
    while (true) ;
  }

  // Initialize MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.setSysexHandler(sysexCallback);

  digitalWrite(READY_LED, HIGH);

}


void loop(void) 
{
  static enum { S_IDLE, S_PLAYING, S_END, S_WAIT_BETWEEN } state = S_IDLE;
  static uint16_t currTune = ARRAY_SIZE(tuneList);
  static uint32_t timeStart;

  switch (state)
  {
  case S_IDLE:    // now idle, set up the next tune
    {
      int err;

      DEBUGS("\nS_IDLE");

      digitalWrite(READY_LED, LOW);
      digitalWrite(SMF_ERROR_LED, LOW);

      currTune++;
      if (currTune >= ARRAY_SIZE(tuneList))
        currTune = 0;

      // use the next file name and play it
      DEBUG("\nFile: ");
      DEBUG(tuneList[currTune]);
      err = SMF.load(tuneList[currTune]);
      if (err != MD_MIDIFile::E_OK)
      {
        DEBUG(" - SMF load Error ");
        DEBUG(err);
        digitalWrite(SMF_ERROR_LED, HIGH);
        timeStart = millis();
        state = S_WAIT_BETWEEN;
        DEBUGS("\nWAIT_BETWEEN");
      }
      else
      {
        DEBUGS("\nS_PLAYING");
        state = S_PLAYING;
      }
    }
    break;

  case S_PLAYING: // play the file
    DEBUGS("\nS_PLAYING");
    if (!SMF.isEOF())
    {
      if (SMF.getNextEvent())
        tickMetronome();
    }
    else
      state = S_END;
    break;

  case S_END:   // done with this one
    DEBUGS("\nS_END");
    SMF.close();
    midiSilence();
    timeStart = millis();
    state = S_WAIT_BETWEEN;
    DEBUGS("\nWAIT_BETWEEN");
    break;

  case S_WAIT_BETWEEN:    // signal finished with a dignified pause
    digitalWrite(READY_LED, HIGH);
    if (millis() - timeStart >= WAIT_DELAY)
      state = S_IDLE;
    break;

  default:
    state = S_IDLE;
    break;
  }
}

and here are some screenshots of my debugging (serial monitor). I think getting rid of that semicolon prevented me from getting that debug statements whenever midisilence() was invoked (which is good!!) But still not turning on my LEDs like expected.






SUPPOSEDLY: I should see an "ALMOSTTTTT" printed as well with each noteOn or noteOff command but obviously there is nothing being printed so I am at a loss.

  if(pev->data[0] == 80 || pev->data[0] == 90)

Should these be hex numbers?