Midi notes detection

Hello everybody. I am designing a "Note-pitch" controller using MIDI PitchBend messages.
It works like this:

  • Arduino searches the incoming MIDI messages for note C and if Pin 2 is HIGH, sends a PitchBend message to serial output. My sketch works but it doesn't follow the conditions properly. It sends PitchBend on the other notes too. To me, it seems like this not the best way to read the incoming MIDI data from serial and that maybe causing the glitch.
    After many hours of trial and error I gave up and decided to ask you guys for help?
    I will appreciate your suggestions and help.
    Here is my code:
byte commandByte;
byte noteByte;
byte velocityByte;

void setup() {
  Serial.begin(31250); 
  pinMode(2, INPUT); //---C
}

void checkMIDI() {
 
   while (Serial.available()) {
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read second byte
      velocityByte = Serial.read();//read third byte
      if (digitalRead (2) == HIGH  && (noteByte == 36 || noteByte == 48 || noteByte == 60 || noteByte == 72 || noteByte == 84)) { // Octave 1-5 C notes
        MIDImessage(224, 0, 39);
      }
    }
  }



void loop() {
  checkMIDI();
  delay(10);
}
void MIDImessage(int command, int data1, int data2) {
  Serial.write(command);//send command byte
  Serial.write(data1);//send data byte #1
  Serial.write(data2);//send data byte #2
} ]

What kind of Arduino? It's always important to mention that in any question for the forum.

Best for midi, I understand, is Pro Micro, or if you already have one, Leonardo. These can emulate native midi devices, which means you don't need an application (can't remember it's name) which emulates a midi device in software and communicates over serial to an Arduino.

But whatever type of Arduino, I guess debugging is difficult because I don't suppose the Arduino can act as both a native midi device and a serial port, so using serial monitor is not possible?

If that's the case, I suggest getting yourself a usb-serial adaptor module. You can then connect that to the Pro Micro's Rx+TX pins, enabling you to use serial monitor through that.

The micro/Leonardo can do that when USB is used as the MIDI device. Serial and MIDI can be used in parallel over USB.
Because @kooroshi60 uses Serial.begin(31250); I suppose this is a native serial MIDI interface. This cannot be used in parallel to Serial over USB on an UNO. In this case debugging is difficult indeed.
The Micro/Leonardo TX/RX pins are independent of a USB connection but are adressed via Serial1.

What I am missing is the synchronisation with the command bytes. The command byte always has its MSB set. That should be checked.
And you don't check if it's a NoteOn command at all.

2 Likes

The next problem is your while loop. The while gets true, when the first byte is received. But you cannot be sure, that the second byte ( noteByte ) has also been received after reading the first (command ). The serial line is slow compared to the execution times of your program. You must check for every byte if it has been received.
And did you connect a pulldown resistor to your pin2?

1 Like

Thank you MicroBahner. You are absolutely right. Yes I pulled down Pin2 but the problem as you mentioned is my super noob approach to make the loop and reading serial bytes.
So now I'm sure what is causing the failure.

You are right. NoteOn check is absolutely missing in my sketch. Thanks

Apart from the points raised by others, you cannot assume that MIDI messages arrive in multiples of three bytes. MIDI messages can consist of any number of bytes, which your code cannot handle. You have to actually inspect the bytes you read and use a state machine to parse them to know the message boundaries. It's easiest to use a library for that, for example:

#include <Control_Surface.h> // https://github.com/tttapa/Control-Surface
 
HardwareSerialMIDI_Interface midi {Serial};
const pin_t switchPin = 2;
 
struct MyMIDI_Callbacks : FineGrainedMIDI_Callbacks<MyMIDI_Callbacks> {
 
    // Function that is called whenever a MIDI Note On message is received.
    void onNoteOn(Channel channel, uint8_t note, uint8_t velocity, Cable cable) {
        bool isNoteC         = note % 12 == MIDI_Notes::C();
        bool isOctave2to6    = note >= MIDI_Notes::C(2) && note <= MIDI_Notes::C(6);
        bool isButtonPressed = digitalRead(switchPin) == HIGH;
        if (isNoteC && isOctave2to6 && isButtonPressed) {
            midi.sendPitchBend(CHANNEL_1, 39 << 7);
        }
    }
 
} callback;
 
void setup() {
    pinMode(switchPin, INPUT);
    midi.begin();
    midi.setCallbacks(callback);
}
 
void loop() {
    midi.update();
}

For more info, see Control Surface: MIDI Tutorial.

1 Like

Thank you so much. The code you have provided is a huge help for me. I'll definitely try it out.
In the meantime, I did some research and found a more sophisticated way than mine, for reading incoming MIDI messages. I was working on it but now, with your sketch I think I have found the right track.
Here is the serial reading sketch I had found:

void checkMIDI() {

  if (Serial.available()) {
    incomingByte = Serial.read();
    if (incomingByte & 0x80) { //check MSB. MIDI commands have this set
      commandByte = incomingByte;
      while (Serial.available() < 2)
      { }  // wait for two more bytes
      noteByte = Serial.read();
      velocityByte = Serial.read();
    }
    else {
      noteByte = incomingByte;
      while (Serial.available() < 1)
      { }  // wait for one more byte
      velocityByte = Serial.read();
    }
  }
}

That will catch many MIDI commands, taking different numbers of parameters. If you want to detect only noteOn, test explicitly for noteOn.

    if ((incomingByte & 0xF0) == 0x90) { //check for noteOn command

Its more complicated than that though, as a noteOn message with zero velocity is actually a noteOff command in disguise.

There are libraries that can do the work for you and give a call-back for each command type - this might be easier to work with.

1 Like