Go Down

Topic: Arduino dropping midi signals (Read 8 times) previous topic - next topic

hek8981

I'm trying to make an Arduino circuit to accept signals from a midi source (in this case a midi keyboard) and pass (most of) them back out for a little pet project.

The order of devices goes:
keyboard -> Arduino -> commercial midi to USB converter
I'm using kuk's midi-in circuit from here, hooked to the Rx pin of my Arduino Uno.
The midi out is coming from my Uno's Tx pin.

Now, my problem is... When monitoring the midi signals coming in from the midi to USB converter on my wife's Mac (using a program called MIDI monitor), it seems to be dropping signals. It will only register a set of note on/note off commands every 4 seconds or so. If I go any faster than that, the monitor doesn't even see the command. But clock signals and active sense signals seem to stream in no problem.

I am really at a loss here. My baud rate is 31250 as per the spec. At first I thought my optocoupler was too slow (4n28) so I bought 3 other kinds and they all have the same behavior, even though running them through a test circuit verified that the optocoupler itself was working correctly. Then I thought the Arduino MIDI library I was using was flawed, so I tried using straight serial writing. Now I am reading about how the Arduino introduces some serial latency, and I am worried that it means the Arduino Uno is too slow to do what I want to do.

Any help at all would be appreciated. I can show you the code I am using, I can (try to) show my circuit(s), I can show you the whole setup from keyboard to converter. Just... somebody please help me before I go crazy!

RuggedCircuits

OK, show us everything :)   One thing I noticed from the schematic you reference is that the 3.3k resistor is too high. It needs to be about 10 times smaller (330 ohms or so) else the rising and falling edges will be too slow.

We've got a Flexible MIDI Shield product that might simplify your life a little bit (and clean up your wiring). If you look on that page there is also some sample code that works for sure (on a Mac even!). There is really no reason for a 4 second delay....

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino

hek8981

#2
Jun 21, 2011, 05:10 am Last Edit: Jun 21, 2011, 05:16 am by hek8981 Reason: 1
Ok, so, this is one version of the code I've been working on

With the Arduino MIDI library from the playground:
Code: [Select]

#include <Compatibility_v2.5.h>
#include <MIDI.h>

const byte CHANNEL = 0x01;
const int numReadings = 10;

int readings[numReadings];      // the readings from the analog inputint[] averages;
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
unsigned int average = 0;                // the average
int analogInputPin = A5;

void setup() {
 pinMode(13,OUTPUT);
 //initialize array for averaging
 for (int thisReading = 0; thisReading < numReadings; thisReading++) {
   readings[thisReading] = 0;
 }
 MIDI.begin(CHANNEL);
 digitalWrite(13,HIGH);
 MIDI.turnThruOff();
}

void loop() {
 //Read midi input
 //if input {
 if (MIDI.read() && (MIDI.getType()==0x80 || MIDI.getType()==0x90)) {
   // pass it on thru
   byte b1 = MIDI.getData1();
   byte b2 = MIDI.getData2();
   if (MIDI.getType()==0x90) {
       MIDI.sendNoteOn(b1,b2,CHANNEL);
   } else {
       MIDI.sendNoteOff(b1,b2,CHANNEL);
   }
 } else {
 
   // get pedal value
   unsigned int pedalVal = analogRead(analogInputPin);

   //average this reading with the last 10 readings
   averageReadings(pedalVal);
   
   // sendPitchBend(pedalVal,CHANNEL);
   MIDI.sendPitchBend(average, CHANNEL);
   
 }
 
}

void averageReadings(int newVal) {
   // subtract the last reading:
 total= total - readings[index];        
 // read from the sensor:  
 readings[index] = newVal;
 // add the reading to the total:
 total= total + readings[index];      
 // advance to the next position in the array:  
 index = index + 1;                    

 // if we're at the end of the array...
 if (index >= numReadings)              
   // ...wrap around to the beginning:
   index = 0;                          

 // calculate the average:
 average = total / numReadings;    
}


Other code forthcoming. Schematics/current setup coming as time allows.

hek8981

Code I found online that did straight serial writes that looked like what I wanted (mostly. Had to modify some things):
Code: [Select]

/* Midi In/out/merge (based on midiin Basic  0.2 // kuki 8.2007) modified OlivarPremier 5.2008
*
* -----------------
* listen for midi serial data, and light leds for individual notes + send midi + merge midi from potentiometer

IMPORTANT:
the optocoupler is powered by pin 10


HARDWARE NOTE:
The Midi Socket is connected to arduino RX through an opto-isolator to invert the midi signal and seperate the circuits of individual instruments.
connect 8 leds to pin2-pin9 on your arduino.

####################################################################################################################################################

*/

//variables setup

byte incomingByte = 255;
byte note = 255;
byte velocity = 255;
byte noteonch = 0;
byte noteoffch = 0;
byte controlch = 0; //byte for control change message ch 1-16
byte control = 255;   //byte for control change number
byte controlval = 0;//byte for control change value
byte programch = 0; //byte for program change message ch 1-16
byte programval = 0;//byte for program change value
byte aftertch = 0;//byte for channel aftertouch
byte aftertval = 0;//byte for channel aftertouch value
byte pitchbch = 0;//byte for channel pitch bend ch1-16
byte pitchbMSB = 255;//byte 1 for pitch bend value
byte pitchbLSB = 255;//byte 2 for pitch bend value

int pot1 = 0;
byte pot1n = 0;

int statusLed = 13;   // select the pin for the LED

int action=2; //0 =note off ; 1=note on ; 2= nada; 3=control change; 4=programchange, 5=channel aftertouch, 6=pitchbend

//setup: declaring iputs and outputs and begin serial
void setup() {
  pinMode(statusLed,OUTPUT);   // declare the LED's pin as output

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

  //Not sure what this is about -hek
  for(int i=176; i<=191; i++)
  {
  Serial.print(i, BYTE);
  Serial.print(123, BYTE);
  }

}

//loop: wait for serial data, and interpret the message or let it pass thru as is and merge message from pot on analogin0
void loop () {
  //send data from arduino
  pot1=analogRead(0);
  pot1=pot1/8;

  //midi merging starts here !
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
    //Serial.print(incomingByte); //debuggin purpose

    // wait for timing clock
    if ( (incomingByte== 248) || (incomingByte== 250)||(incomingByte== 251) || (incomingByte==252)){ // clock messages (24=timing, 250=start, 251=continue, 252=stop)
      Serial.print(incomingByte, BYTE);   
      pitchbch = 224;
      action=2;
    }
    // wait for song position
    if (incomingByte== 242){ // song position message
        pitchbch = 224;
      Serial.print(242, BYTE);
      Serial.print(Serial.read());
      Serial.print(Serial.read());
      action=2;
    }
    // wait for as status-byte, channel 1, note on or off
    if ( (incomingByte>= 144) && (incomingByte<=159)){ // note on message starting starting
     noteonch = incomingByte;
     action=1;
     note=255;
    }
    else if (incomingByte>= 128 && incomingByte<=143){ // note off message starting
      noteoffch = incomingByte;
      action=0;
      note=255;
    }
    else if (incomingByte>= 160 && incomingByte<=175){ // message is key aftertouch
    pitchbch = 224+(incomingByte-160);
    action=2; //nothing happens
    }
    else if (incomingByte>= 176 && incomingByte<=191){ // message is control change
    pitchbch = 224+(incomingByte-176);
    controlch=incomingByte;
    action=3; //control change
    control=255; 
    }
    else if (incomingByte>= 192 && incomingByte<=207){ // message is program change
    pitchbch = 224+(incomingByte-192);
    programch=incomingByte;
    action=4; //control change 
    }
    else if (incomingByte>= 208 && incomingByte<=223){ // message is channel aftertouch
    pitchbch = 224+(incomingByte-208);
    aftertch=incomingByte;
    action=5; //channel aftertouch 
    }
    else if (incomingByte>= 224 && incomingByte<=239){ // message is pitch bend
    pitchbch=incomingByte;
    action=6; //pitch bend
    pitchbMSB=255;
    }
    else if (incomingByte>= 240 && incomingByte<=255){ // message is sysex (240) active sense(254)
    pitchbch = 224+(incomingByte-208);
    action=2; //do nothing 
    }
    //process second byte
     
    else if ( (incomingByte<=127) && (action ==1 || action==0)&&(note==255) ){ // if we received note number message load this byte in the note byte
      note=incomingByte;
    }
    else if ( (incomingByte<=127) && (action ==3)&&(control==255) ){ // if we received controlchange message load this byte in the control number byte
     control=incomingByte;
    }
    else if ( (incomingByte<=127) && (action ==4) ){ // if we received program change message load this byte in the control number byte
     programval=incomingByte;
     //twoBmidiMsg(programch, programval);
     midiMsg(pitchbch, pot1, pot1);
    }
    else if ( (incomingByte<=127) && (action ==5) ){ // if we received program change message load this byte in the control number byte
     aftertval=incomingByte;
     //twoBmidiMsg(aftertch, aftertval);
     midiMsg(pitchbch, pot1, pot1);
    }
    else if ( (incomingByte<=127) && (action ==6)&&(pitchbMSB==255) ){ // if we received pitchbend message load this byte in the MSB byte
     pitchbMSB=incomingByte;
    }
    //process Third byte
   
    else if ( (incomingByte<=127) && (action ==0)&&(note!=255) ){ // if we received note message load this byte in the velocity byte
     velocity=0;
     playNote(note, velocity);
     note=255;
    }
    else if ( (incomingByte<=127) && (action ==1)&&(note!=255) ){ // if we received noteon message load this byte in the velocity byte
     velocity=incomingByte;
     playNote(note, velocity);
     note=255;
    }
    else if ( (incomingByte<=127) && (action ==3)&&(control!=255) ){ // if we received controlchange message load this byte in the control value byte
     controlval=incomingByte;
     //midiMsg(controlch, control, controlval);
     midiMsg(pitchbch, pot1, pot1);
     control=255;
     }
     else if ( (incomingByte<=127) && (action ==6)&&(pitchbMSB!=255) ){ // if we received pitchbend message load this byte in the LSB byte
     pitchbLSB=incomingByte;
     midiMsg(pitchbch, pitchbMSB, pitchbLSB);
     pitchbLSB=255;
    }
    else{
      //nada
     
    }
  }
//Serial.print(incomingByte, BYTE);
}


void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >10){
      value=HIGH;
  }else{
   value=LOW;
  }
 
if(action==0){
  Serial.print(noteoffch, BYTE);
  Serial.print(note, BYTE);
  Serial.print(velocity, BYTE);
  }
  else if(action==1){
  Serial.print(noteonch, BYTE);
  Serial.print(note, BYTE);
  Serial.print(velocity, BYTE);
  }
  else {}
   
/* don't need this -hek
//since we don't want to "play" all notes we wait for a note between 36 & 44
if(note>=36 && note<44 && (noteonch==144 || noteoffch==128)){ //led command on ch1 only
   byte myPin=note-34; // to get a pinnumber between 2 and 9
   digitalWrite(myPin, value);
//send midi message back
 
}
*/
}

// Send a 2 byte MIDI message
void twoBmidiMsg(byte cmd, byte data1) {
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
}
// Send a 3 byte MIDI message
void midiMsg(byte cmd, byte data1, byte data2) {
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
}

hek8981

#4
Jun 21, 2011, 05:16 am Last Edit: Jun 21, 2011, 05:18 am by hek8981 Reason: 1
Ultimately, the point is, if the keyboard sends a note (on or off), send the note through to the midi out. Any other signal, trap it and send out a pitch bend signal, with the pitch bend value being read from an analog input.


OK, show us everything :)   One thing I noticed from the schematic you reference is that the 3.3k resistor is too high. It needs to be about 10 times smaller (330 ohms or so) else the rising and falling edges will be too slow.

Well that certainly made my ears prick up... Too slow? I don't have a 330 ohm resistor, but I have 220s and 100s to put in series. Close enough, right?


We've got a Flexible MIDI Shield product that might simplify your life a little bit (and clean up your wiring). If you look on that page there is also some sample code that works for sure (on a Mac even!). There is really no reason for a 4 second delay....

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino

I would really like to avoid buying a shield for MIDI if possible. I mean, kinda the point of this project was to do it myself... Just a stubbornness I have. But thank you for the suggestion. If push comes to shove, and a head of hair turns into fistfuls of it, I'll definitely keep your shield in mind. Thank you for your help so far.

Go Up