Pages: [1] 2   Go Down
Author Topic: Receiving MIDI Signals  (Read 2657 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi everybody,

I have been trying to receive MIDI messages using my Arduino Uno and the circuit shown here...

http://arduino.cc/forum/index.php/topic,22447.0.html

I have built the MIDI interface on a breadboard and am receiving signals from a keyboard.
Instead of connecting LED's like in the above post I am trying to control an 8x8 LED matrix. So far the MIDI input works however the response is slow and unreliable. If I play notes slowly the LED's will turn on and off as they are suppose to however if you play a note quickly the noteOff messages are missed. More specifically, striking a key quickly turns the LED on but not off.

Similarly, playing runs of notes quickly produces unreliable results with most of the messages on and off being missed.

On the other hand, if I play 4 of 5 notes at the same time the appropriate LED's turn on and off as expected. As long as you play slowly (keep each key pressed for at least a full second) the lights turn on and off.

I have modified the the code to use the Arduino Midi library instead of listening to the serial port like in the original post. This works slightly better however it still seems to be lagging behind.



Code:
  if (MIDI.read()) {
    if (MIDI.getType() == NoteOn) {
     // get note value
      byte dat1 = MIDI.getData1();
      // get velocity (though I'm not using it)
      byte dat2 = MIDI.getData2();
      // map MIDI note value to 8x8 array
      int row = dat1/8;
      int col = dat1%8;
      // my keyboard sends noteOn with 0 velocity instead of noteOff messages
      if (dat2 > 10) {
        displayGrid[row][col] = 1;
      }
      else {
        displayGrid[row][col] = 0;
      }

    }
    refreshDisplay();

The semantics on controlling the lights are not really important here. The 8x8 array is controlled by a 8x8 array on ints in memory. 0 for off, 1 for on. I simply change these values and refresh the display. This code comes from an old project and works fine.

Is the issues here the MIDI interface and am I better off building the one prescribed here.
http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html

Thanks heaps
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 639
Posts: 34726
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
am I better off building the one prescribed here.
The MIDI schematics described in those two links you posted are virtually the same are they not?

Your problem might be in using the MIDI library, I am not a great fan of libraries because all the code is hidden away. I think it is best to write your own, after all it is not at all a hard thing thing to do. The code in this project of mine shows you how to do it:-

 http://www.thebox.myzen.co.uk/Hardware/Glockenspiel.html
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Grumpy_Mike,

Thanks for the reply. I guess the circuits are the same however I thought the speed of the opto-isolator might be causing an issue. Just something I was reading in other posts but if they are the same I doubt it will matter.

In regards to the code, I didn't originally start with the MIDI library. I was using the code from the blog, I just changed it to turn on LEDs in the matrix instead of LEDs connected to pins 2-9. However, with this setup playing chords would only turn on one LED instead of multiple.

The following code is from the forum entry I got the circuit from. The only thing I have changed is the playNote method which now turns on LEDs in a matrix instead of LEDs connected directly to a port.
Code:
//variables setup

byte incomingByte;
byte note;
byte velocity;


int statusLed = 13;   // 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(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
 
  //start serial with midi baudrate 31250 or 38400 for debugging
  Serial.begin(31250);       
  digitalWrite(statusLed,HIGH); 
}

//loop: wait for serial data, and interpret the message
void loop () {
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();

    // 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);
      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);
      note=0;
      velocity=0;
      action=0;
    }else{
      //nada
    }
  }
}

void blink(){
  digitalWrite(statusLed, HIGH);
  delay(100);
  digitalWrite(statusLed, LOW);
  delay(100);
}


void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }else{
   value=LOW;
  }

 //since we don't want to "play" all notes we wait for a note between 36 & 44
 if(note>=36 && note<44){
   //byte myPin=note-34; // to get a pinnumber between 2 and 9
   //digitalWrite(myPin, value);

  //modified to control an 8x8 array
      // map MIDI note value to 8x8 array
      int row = note/8;
      int col = note%8;
        displayGrid[row][col] = value;
 }

}

 With this approach the response seems to be a bit faster (the delay between pressing and releasing a key can be smaller) but when you play chords only one LED lights up. Using the MIDI library seems to be slower (the delay between releasing the key must be at least a second for the note off to register) but playing chords causes multiple LED's to turn on and off.  I basically want the lights in the 8x8 matrix to turn on and off as I play the keyboard.

I had a look at your glockenspiel project the other day. Really cool. The code you have implemented is similar to the original approach I used. I will have a go at it with your code in the morning.
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 639
Posts: 34726
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes that looks like my code with some hacking done to it.  smiley ( I wrote it from scratch and those are my variable names and comments )

Quote
but when you play chords only one LED lights up.
The bit of code that does the LED lighting is not posted so I can only guess.
However, make sure that the note on message turns on the LED only and doesn't turn any off and the note off message turns off the LED.
I think you will find that the note on message is turning off existing LEDs.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

 smiley Snippets on that code appear in almost every MIDI in reference I have looked at lol.

I'm not to sure what your saying. A noteOn message changes the values in displayGrid
  • [y] to 1 and a noteOff (or noteOn with velocity <10) sets the value to 0. Based on that I update the matrix with the method below.


This is the code that updates the matrix. It is called each time the loop iterates.
Code:
 
void refreshDisplay() {
for(int x=0; x<LEDs; x++) {
    for (int y=0; y<LEDs; y++) {
      if (displayGrid[x][y] == 1) {
        lc.setLed(0,x,y,true);
      }
      else {
        lc.setLed(0,x,y,false);
      }
    }
  }
}
}
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 639
Posts: 34726
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

It's libraries again, I have no idea what lc.setLed() does with its parameters, especially as the first one is always zero. 
By the way there is no need to do the two stage approach, setting an array and then transferring it into your library functions, just use the lc.setLed() function directly in the place where you set an array at the moment.

Quote
(or noteOn with velocity <10) sets the value to 0.
acording to the MIDI standard it should be:-
noteOn with velocity == 10 sets the value to 0.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes fair enough. That library controls the max7219 that runs the matrix.
Code:
lc.setLed(int deviceID, int col, int row, boolean isOn);

 And good point about not needing the array. That's just habbit from the last project. To put this in context, I have wired up a LED matrix ( as shown here http://arduino.cc/playground/Main/LedControl as a set of Christmas lights for my tree. I currently have them doing various effects and flashing along to a few Christmas carols but I thought why not go all out and put those hours I spent as a child learning Christmas carols to good use and sync it up with my keyboard.


I'll sleep on it and tackle this again after work tomorrow.

Thank you for you help
Logged

Austin, TX
Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to continue to use the MIDI library, you can simplify your code a lot by using the event handlers. To do that, in your setup function:
Code:
  // Initiate MIDI communications, listen to all channels, turn off thru mode
  MIDI.begin(MIDI_CHANNEL_OMNI);
  // By default, the MIDI library sends everything THRU. We do NOT want that!
  MIDI.turnThruOff();

  // Connect MIDI status changes involving a channel to handlers
  MIDI.setHandleNoteOn(HandleNoteOn);
  MIDI.setHandleNoteOff(HandleNoteOff);
Then, create a function for handling note on and note off:

Code:
void HandleNoteOn(byte channel, byte pitch, byte velocity) {
 // check your pitch, velocity, and channel here
 // turn on the LED matching pitch
}
void HandleNoteOff(byte channel, byte pitch, byte velocity) {
 // check your pitch, velocity, and channel here
  // turn off the LED matching pitch
}

Now your loop function is simple:
Code:
void loop() {
   MIDI.read();
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi mymeastro,

Thanks for the post. I tried it that way and it is working much better. Its still not fast enough to keep up with me playing though.

I'm gonna break this down and start from scratch as Im on holidays for 2 weeks in 2 days. Well not from scratch, just simplify the project a little and try to control a couple of LED's like in the original post I looked at. That way I can test the MIDI interface and code a little more accurately rather than stuffing around with the matrix.
Logged

Austin, TX
Offline Offline
Newbie
*
Karma: 0
Posts: 22
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You don't need to refresh the entire grid each time!
Just change the specific LED that relates to the MIDI note. The 7219 will take care of the rest.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks mymeastro,

yes I am aware of that. My note on and note off handlers address the LED's directly.
A note value is converted to an x,y co-ordinate and that single LED is turned on or off.

Still not fast enough,
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 639
Posts: 34726
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I wonder if the on notes and hence LEDs are falling between the cracks. That is if the duration of the note is less than the time it takes to reload the data into the matrix driver.
Just as a test. Comment out the LED off part and see if those notes are actually being caught.
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
Developer of the MIDI library
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

Do you have a mean of measuring the time spent in MIDI.read? (which is the time used to parse MIDI input messages).

I did some tests while developing the new input processing code (parser) that is used since version 2.5 (if I remember correctly), and with a little bit of processing code (like toggling lights), the lag was not a problem. However, if you are doing more in your handling code, it will slow the loop thus slow the speed at which input MIDI is read.

If the read() method is not fast enough for you, I can try and find where the parser is taking too much time, and see if I can do something about it.

Anyway, make sure you are using the latest version of the library (available on Sourceforge).
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 30
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think the issues is the MIDI interface I made or the delay in talking to the LED matrix. Im going to rebuild the interface with one of Grumpy Mike's designs.

Anyway, I am going to try to drive a few LED's directly rather than manipulating the matrix. Simplify and remove possible sources of error. The only issue now is I seems to have blown up my Arduino smiley-razz (or at least the USB or Serial part) Serves me right for trying to re-wire the MIDI interface after a 13 hour day. Plugged the wrong MIDI cable in, smelt smoke, now the Uno doesn't do much but power up.

Guess i'll have to wait for a new one to ship unless Santa is feeling generous.

Thanks for the help on this everyone, I will post back when I have made some more progress.
« Last Edit: December 22, 2011, 09:44:44 am by fireman_sam6986 » Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 639
Posts: 34726
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I have just had a thought, maybe your keyboard is going into the rarely used follow on mode. This is where the note on / note off byte is dropped and just the other two bytes are sent. My code does not cater for this but this modification will:-

Code:
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
     digitalWrite(strobe,LOW);    // clear any previous strobe
   switch (state){
      case 0:
    // look for as status-byte, our channel, note on
    if (incomingByte== (144 | channel)){
         noteDown = HIGH;
         state=1;
        }
    // look for as status-byte, our channel, note off
    if (incomingByte== (128 | channel)){
         noteDown = LOW;
         state=1;
        }
      // consider follow on mode
       if(incomingByte< 128){
        // use same noteDown as last time
        note=incomingByte;
        state=2;
       }
       
       case 1:
       // get the note to play or stop
       if(incomingByte < 128) {
          note=incomingByte;
          state=2;
       }
       else{
       state = 0;  // reset state machine as this should be a note number
       }
       break;
       
       case 2:
       // get the velocity
       if(incomingByte < 128) {
         playNote(note, incomingByte, noteDown); // fire off the solenoid
       }
         state = 0;  // reset state machine to start           
     }
  }
Logged

Pages: [1] 2   Go Up
Jump to: