Pages: 1 [2]   Go Down
Author Topic: MIDI-IN questions  (Read 2747 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Offline Offline
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Pretty cool. smiley
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
God Member
*****
Karma: 6
Posts: 521
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 618
Posts: 33945
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.)
« Last Edit: October 14, 2013, 08:18:07 am by pulykamell » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
God Member
*****
Karma: 6
Posts: 521
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
God Member
*****
Karma: 6
Posts: 521
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
// 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:
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".
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 25
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

Pages: 1 [2]   Go Up
Jump to: