MIDI-IN questions

You check if at least one byte is ready to be read and then you proceed to read three bytes. Spot anything wrong with that?

OK, I think I see what you're saying. I did read Gammon's article but thought I could get around it if I knew there were supposed to be 2 or 3 bytes to read. But I suppose what you're saying is that you can't even assume that, because the second byte might still be en-route, so if you do successive reads like that, if it's faster than the transmission rate, you'll get garbage.
So, conceptually, what is happening when I put in that delay is that it allows enough time for those two bytes to come through and sit in the buffer, ready to be read.
Is that correct? I like making these kinds of mistakes, because I learn/remember them better than if I had it right all along. So, is that conceptually what is happening and why this is screwing up?

ETA: No, it looks like I didn't read the whole article last night. I clicked through to the post saying that it's wrong unless you test specifically for X number of bytes, but I didn't click on the link on that page to another thread further explaining the issues.

I also suppose that I could leave this delay in, and it will work, but that it's a "hackish" way of doing things and sloppy programming, right?

I was just about to ask whether it would make sense to put in an empty block like while (!Serial.available()) { }, but I think that's what Gammon is doing in his code. In other words, since I know a noteOn command is going to be followed by two bytes, one for note, one for velocity, would it be acceptable to just add the "while (!Serial.available()) {}" before every Serial.read() in my code? It seems to work okay, but is there something I'm not considering?

In my code I specifically made the function to get a single byte "block". Thus the loop waiting for available before each read.

I don't generally recommend that, as while it is blocking you can't do other stuff (like test switches, flash LEDs etc.). In the case of that particular sketch I didn't care (there were no switches or LEDs).

Adding in the delay is not recommended, that will not necessarily work.

When I leave the delay(1) in, everything works great. All my values display correctly. I don't know what possessed me to try to put a delay in there, but it works. I just can't figure out WHY it works.

It works in this case because after the command byte the other two bytes are likely to arrive soon after, and the delay gives them time to arrive. At 31250 baud each byte will take 1/3125 seconds to arrive (320 µS) so one mS delay is enough time for 3 bytes to arrive.

More reliable, if you don't mind it blocking (and the delay blocks after all) is to check the byte(s) actually arrive. eg.

    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();
      }

Thanks for your help and patience. I just made a function/method called waitForByte() that is just an empty while block and that just waits for a byte to become available on the serial port, but after looking at your code, I might have it accept an argument so I can wait for two bytes where appropriate instead of making two calls to waitForByte. For what I'm doing right now, I don't foresee that little no operation loop to be problematic as I don't think I need the program to be doing anything else in the meantime.

Thanks again. With your help, I was able to get the MIDI stuff down and interface it with the SID chip. Right now, I only have control of keyboard notes and the sliders set to control some elements of the envelope (attack, decay, sustain, release), but I've got it up and running!

Video here.

This content is currently unavailable
The page you requested cannot be displayed right now. It may be temporarily unavailable, the link you clicked on may have expired, or you may not have permission to view this page.

Sadly I cancelled my Facebook account. :frowning:

OK, I'll upload it to Youtube:

Here you go

Pretty cool. :slight_smile:

Thanks. I'm stoked that it worked, and the community here has been very helpful in seeing my project along. I've never really done this kind of stuff before. Now to incorporate all three notes of polyphony into the next iteration of the project (although I have a couple extra SID chips now, so I can make it 9 notes of polyphony), and then the more complicated stuff like vibrato, filtering (including sweeps) and all that kind of stuff.

Yes, it is what MIDI defines as "Running Status".
It helps when transmittning chords.

You only read 1byte of the serial.
If it has the top bit set, it is a status change and you store it as the current status and read 2 more bytes.

If it has the top bit cleared, it is an event with the same status you already have stored and you read just one more byte.

Tricky but very common.

it is an event with the same status you already have stored and you read just one more byte.

Make that two more bytes, note number and velocity.

janost:
Yes, it is what MIDI defines as "Running Status".
It helps when transmittning chords.

You only read 1byte of the serial.
If it has the top bit set, it is a status change and you store it as the current status and read 2 more bytes.

If it has the top bit cleared, it is an event with the same status you already have stored and you read just one more byte.

Tricky but very common.

There's actually a couple more caveats I discovered if you want to be true to MIDI spec -- I haven't implemented any of these in the code yet but if the upper four bits are set in a MIDI message, running status behaves differently. System common category messages (0xF0-F7) cancel running status. But that shouldn't be a problem, as a status message should follow. However, real time category messages (0xF8-0xFF) don't affect running status, so should not update your status value.

Anyhow, there's a good run-down here about it, which djbouche posted earlier.

I don't think it's anything I should run into with the keyboard project, but I'll probably implement it in code just to be safe.

(And, of course, if the top bit is set, this doesn't necessarily mean two bytes of data are coming along. Different MIDI status changes have different parameters, from 0 - 2.)

Grumpy_Mike:

it is an event with the same status you already have stored and you read just one more byte.

Make that two more bytes, note number and velocity.

I think what is meant is that you just have to read one more, the velocity, as the one you just read was the note number (instead of the status byte, as you might have expected if you weren't using running status.)

Bytes with the high bit set Always reset the state machine to keep it in sync.

And yes, you need to check for statusbytes that are not Note on/off and handle them separatly.

There's actually a couple more caveats I discovered if you want to be true to MIDI spec -- I haven't implemented any of these in the code yet but if the upper four bits are set in a MIDI message, running status behaves differently. System common category messages (0xF0-F7) cancel running status. But that shouldn't be a problem, as a status message should follow. However, real time category messages (0xF8-0xFF) don't affect running status, so should not update your status value.

That is NOT true to the MIDI specs.
All status bytes should and will cancel running status.

Any statusbyte does cancel running status.
How will you otherwise keep track and sync?

So, if Clock (F8) is sent 48 times/sec that would be bad for running status?
Or the timeout Clock (FF) sent every 300ms?

Keep with the rule that all statusbytes cancel running status.
Those are the MIDI rules.

I guess I'm a little confused. This is what that page about MIDI says:

RealTime Category messages (ie, Status of 0xF8 to 0xFF) do not effect running status in any way. Because a RealTime message consists of only 1 byte, and it may be received at any time, including interspersed with another message, it should be handled transparently. For example, if a 0xF8 byte was received inbetween any 2 bytes of the above examples, the 0xF8 should be processed immediately, and then the device should resume processing the example streams exactly as it would have otherwise. Because RealTime messages only consist of a Status, running status obviously can't be implemented on RealTime messages.

Is that wrong or am I misunderstanding it?

What it sounds to me it's saying is that 0xF8 can come along at any time, so one should explicitly check for it at every Serial.read(), lest it come after a status byte and before a data byte, no? And at that Serial.read(), one should not update the "current status" if it receives a status byte of 0xF8 or higher.

In other words, let's take my initial example:

144, 60, 80
60, 0

The way I have it implemented now is that when a status byte is received, it gets put in the status buffer. So upon reading 144, 60, 80, 144 goes into the status byte buffer. When 60 and 0 is received, since status hasn't changed, it assumes 144, 60, 0 as the MIDI command. If an 0xF8 (248) timing byte ends up in the middle there (let us suppose a stream of 144, 60, 80, 248/0xF8, 60, 0), the status byte gets updated to 248 (with the way the code is currently written) and everything gets screwed up. The 0xF8 should be processed on its own, and then the next two bytes that are data bytes should be read and processed as note and velocity bytes for the status of 144 (noteOn/channel 1).

I have no reason to plan for this contingency, as I'm not going to be using the device in such a manner, but is my understanding incorrect?

According to the 529 page spec I found:

The MIDI spec allows for a MIDI message to be sent without its Status byte (ie, just its
data bytes are sent) as long as the previous, transmitted message had the same Status. This is referred to as running status. Running status is simply a clever scheme to maximize the efficiency of MIDI transmission (by removing extraneous Status bytes). The basic philosophy of running status is that a device must always remember the last Status byte that it received (except for RealTime), and if it doesn't receive a Status byte when expected (on subsequent messages), it should assume that it's dealing with a running status situation.

Note that the only exception is RealTime. However:

Each RealTime Category message (ie, Status of 0xF8 to 0xFF) consists of only 1 byte, the Status. These messages are primarily concerned with timing/syncing functions which means that they must be sent and received at specific times without any delays. Because of this, MIDI allows a RealTime message to be sent at any time, even interspersed within some other MIDI message. For example, a RealTime message could be sent inbetween the two data bytes of a Note On message. A device should always be prepared to handle such a situation; processing the 1 byte RealTime message, and then subsequently resume processing the previously interrupted message as if the RealTime message had never occurred.

So it would appear you are right. Not only do 0xF8 to 0xFF not affect running status, they should be detected (and discarded or whatever) even in between reading other bytes.

Therefore my own MIDI decoder is wrong in that respect (although it worked with the devices I tested it on).

My relevant code was:

// get next byte from serial (blocks)
int getNext ()
  {

  if (lookAhead != noLookAhead)
    {
    int c = lookAhead;
    // finished with look ahead
    lookAhead = noLookAhead;
    return c;
    }
    
  while (midi.available () == 0)
    {}
  return midi.read ();
  }

...

void loop() 
{
  byte c = getNext ();

  if (((c & 0x80) == 0) && (lastCommand & 0x80))
    {
    lookAhead = c;
    c = lastCommand; 
    }
    
  // channel is in low order bits
  int channel = (c & 0x0F) + 1;
  
  // ignore stuff that keeps churning out
  if (c == 0xF8 ||  // timing clock
      c == 0xFE)    // active sensing
    return;  // too much information

However really it should be more like:

void RealTimeMessage (const byte msg)
  {
    
  } // end of RealTimeMessage

// get next byte from serial (blocks)
int getNext ()
  {

  if (lookAhead != noLookAhead)
    {
    int c = lookAhead;
    // finished with look ahead
    lookAhead = noLookAhead;
    return c;
    }
    
  while (true)
    {
    while (midi.available () == 0)
      {}
    byte c = midi.read ();
    if (c >= 0xF8)  // RealTime messages
      RealTimeMessage (c);
    else
      return c;
    }
  } // end of getNext

Then the test for 0xF8 later on could be omitted.

Of course, in the code above the variable "lookAhead" should really be called "runningStatus".

Cool. I just wanted to make sure I was understanding that correctly. I can't see how I would run into those types of messages with the usage I have in mind, but I might just program it in anyhow to keep it to spec.

Thanks again.