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

wow! This sketch detects the notes precisely without any glitch. But sends the PitchBend message twice! Once after NoteOn and Once after Note off. I added the following line to the sketch in order to bring the PitchBend back to center position (lsb 0, msb 64) after NoteOff is received, But it doesn't run as I expect and keeps running like before.
Here is the line I added:

 // Function that is called whenever a MIDI Note Off message is received.
  void onNoteOff(Channel channel, uint8_t note, uint8_t velocity, Cable cable) {
    midi.sendPitchBend(CHANNEL_1, 64 << 7);
  }

On my MIDI monitor
I expect: Note on , Pitchbend 0,39 , Note off, Pitchbend 0,64
I receive: Note on , Pitchbend 0,39 , Note off, Pitchbend 0,39

P.S. In my setup Arduino only generates the PitchBend messages, Note_On/Note_off are generated on another gear and later merged with Arduino MIDI out.

The device you're communicating with probably sends a Note On message with velocity 0 instead of a Note Off message, see:

You could do something like this:

...
    void onNoteOn(Channel channel, uint8_t note, uint8_t velocity, Cable cable) {
        if (velocity == 0)
            return onNoteOff(channel, note, velocity, 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);
        }
    }
...
1 Like

Actually its even more complicated than that as MIDI command bytes can be omitted if the next command byte is the same as the previous - probably much better to use a library to handle all the nuances of MIDI parsing and generation - fun though it is to write a MIDI parser, its probably not what you are trying to achieve!

Thanks to your code I finally made it to work. I really appreciate your attention and support. Actually this is a practical way to tune a particular note on keyboard but I have a final question about it.
Although I know that Pitch Bend is a "Per channel" message, is it possible to filter out some notes so that they are not affected when a Pitch Bend message is received?
My concept is, constantly sending a filtered Pitchbend message that only affects a certain note.

MIDI pitch bend applies to a whole channel, so can only be made note-specific by using different channels for "pitch bendable" and "straight through". So you might have to warp channels on notes to achieve this. But in principle you can do this as you have the original note and channel in your hand and can relay it on a different channel if you want.

1 Like

It's not quite clear to me what you want to do. You receive a note on message from somewhere. A midi keyboard? You receive pitch bend messages, too. From same device? And you have a push button on pin 2. And you want to send the note on back to the keyboard or further to an other synth, which needs to play and bend if it's a C, otherwise only play.

You could set up your synth to react to polyphonic aftertouch messages. So when your Arduino receives a note on, if it's a C and the button is pressed, it reads the pitch bend and sends the note on further together with an aftertouch for that note.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.