Switch case calls function twice

Hi all,

I'm reading incoming MIDI notes via USB then calling a function when notes 45 or 55 are received. However, when each note is received, the function is called twice. I'm getting 2 calls for each case, one almost immediately after the other.

Can anyone see any problems with my code?

#include <frequencyToNote.h>
#include <MIDIUSB.h>
#include <pitchToFrequency.h>
#include <pitchToNote.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

byte rxVar;

void moveStepper() {
  Serial.println("*** Move stepper ***");  
}

void setup() {
  Serial.begin(115200);
  #define SerialUSB Serial
  while(!SerialUSB && millis()<5000) {
  //wait for USB serial to connect or 5 seconds to elapse
  } 
}

void loop() {
  
    midiEventPacket_t rx = MidiUSB.read();;
    rxVar = rx.byte2;

    switch (rxVar) {
    case 45:            //Note On message
        moveStepper();
        Serial.print("note1 = ");
        Serial.println(rxVar);
      break;
    case 55:            //Note On message
        moveStepper();
        Serial.print("note2 = ");
        Serial.println(rxVar);
      break;      
    default:        
        // do nothing
      break;
    }
}

What is returned when you call the MidiUSB.read() function if there is not actually anything to read ?

Is there, perhaps, a MidiUSB.available() function that would allow you to check that data is available to read ?

Why does the comment states OR and the code says AND?

while(!SerialUSB && millis()<5000) {
  //wait for USB serial to connect or 5 seconds to elapse
  }

The two semi colons at the end are one too many :slight_smile: midiEventPacket_t rx = MidiUSB.read();;and you might need to check if read had anything to return

Demo code proceeds this way:

  midiEventPacket_t rx;
  do {
    rx = MidiUSB.read();
    if (rx.header != 0) {
      // i’ve Received something, deal with it
    }
  } while (rx.header != 0); // we emptied the buffer

But that’s not the issue - just “dirty” code because the rx struct is zeroed and not just the header in read when there is nothing to receive so your code would filter that out as you check against 2 specific values.

my guess on why probably your 2 calls:
The key thing is to Remember you will receive for each note a note_ON and note_OFF message -> so you need also to handle that.

Looking at an example it seems that
-> rx.header == 0x9 means it’s note ON
-> rx.header == 0x8 means it’s note OFF

The header can also hold other values - see the MIDIUSB_buzzer.ino example

Indeed some MIDI devices send NoteOn (0x90) commands with velocity 0 rather than NoteOff (0x80) commands and that is permitted in the MIDI spec.

But I can't see where the code does any checking at all for command type. It seems to just assume that any MIDI coming in must be a NoteOn.

Steve

Here is a link to the basics of midi messages - you'll see how the channel is combined with Note On or OFF. if you have only one channel indeed you'll get 0x80 and Ox90 in theory but reality might differ - so always best to print out what you get with your specific device --> use the MIDIUSB_read.ino example for that

Thanks for all the help, it's working now. Yes, it was being triggered by both the note on and note off messages.

Slightly baffled as to why it's returning 144 for note on instead of 90, and 128 for note off. Possibly reading as the wrong data type? I'm guessing here.

Updated code:

#include <MIDIUSB.h>
#include <frequencyToNote.h>
#include <pitchToFrequency.h>
#include <pitchToNote.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

int midiVar;

void moveStepper1() {
  Serial.println("*** Move stepper 1 ***");
}

void moveStepper2() {
  Serial.println("*** Move stepper 2 ***");
}

void setup() {
  Serial.begin(115200);
#define SerialUSB Serial
  while (!SerialUSB || millis() < 5000) {
    //wait for USB serial to connect or 5 seconds to elapse
  }
}

void loop() {

  midiEventPacket_t rx;

  do {

    rx = MidiUSB.read();

    if (rx.byte2 != 0) {

      midiVar = (rx.byte1 + rx.byte2); // create unique number from on/off + note

      switch (midiVar) {
        case 189:            //Note 45
          moveStepper1();
          break;
        case 199:            //Note 55
          moveStepper2();
          break;
        default:
          // do nothing
          break;
      }
    }
  }
  while (rx.header != 0);
}

Well given that 0x90 (hex) = 144 (decimal) and 0x80 (hex) = 128 (decimal) I think you may be more or less right.

Though you would still have trouble with any MIDI device that sends 0x90 with velocity 0 instead of 0x80 for a NoteOff.

Steve

How could I adapt the code to work with hex instead of decimal?

Why bother, they're all numbers? Since you're adding together the command code (0x90/144, normally written in hex) to the note number (45/0x2D, normally written in decimal) something will look strange whatever you do.

You could check for 0xBD or 0xC7 instead of 189 or 199 but it wouldn't do anything different or be any easier to interpret.

Steve

You could define constants with meaningful names for noteON, noteOFF, note45 and note55 and and add them up in the switch/case. The precompiler would still do the math so that it does not happen at run time but it would be readable.

Note that when you add two numbers you don’t do anything unique. 2+5=7 but so does 4+3... In your case 144 + 45 = 189 is noteON+note45 but you can also get to 189 with 128+61 so playing a D4 and releasing it will send noteOFF+61 and your code will see that as noteON+45...

If you want something unique create a uint16_t variable and stick byte1 as Least significant byte and byte2 as most significant one.

LeeMartin:
How could I adapt the code to work with hex instead of decimal?

Code works with values, not denotations. The decimal / hexadecimal notation is purely for the compiler, you can
say 0x80 or 128 and they are utterly the same thing - just different names for the same number.
You can say 0b10000000 too, or 0200 (octal constants start with a zero - a really nasty gotcha if
you don't know about it)

In fact character constants in C are just more ways to name a number. 0x40 == '@' == 64 == 0100 == 0xb1000000.

At runtime its just a number, and happens to be stored in binary at the lowest level.