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.)
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.
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?
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.