Go Down

Topic: MIDI-IN questions (Read 3334 times) previous topic - next topic

Nick Gammon

Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pulykamell

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.

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.

Grumpy_Mike

Quote
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.

pulykamell

#19
Oct 14, 2013, 03:13 pm Last Edit: Oct 14, 2013, 03:18 pm by pulykamell Reason: 1

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

pulykamell


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

janost

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.

janost

Quote

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.

pulykamell

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

Quote

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?

pulykamell

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?

Nick Gammon

According to the 529 page spec I found:

Quote

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:

Quote

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:

Code: [Select]

// 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:

Code: [Select]

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".
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pulykamell

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.


Go Up