MIDI parsing algorithms

gstopp at fibermux.com gstopp at fibermux.com
Sat Nov 23 00:49:15 CET 1996


     John Speth wrote:
     
     >
     >I wrote such a MIDI parser in C for a MIDI-to-Prophet controller for 
     >my el-cheapo 16 Mhz 286 machine which has no problem keeping up.  But 
     >the code quickly turned into something I wasn't very proud of 
     >(spaghetti code but it worked!).  In hindsight, I would have put alot 
     >more effort up front into a real sleek event dispatcher (kinda like a 
     >Windows 3.1 style thing).  Then the actual parsing code would have 
     >been alot simpler.  Better yet, a multitasking kernel would be best.  
     >Instead I chose a dumb loop that waits for a byte then enters this 
     >big huge switch statement.  It was all downhill from there. Enuf 
     >said!
     >
     
     The code described above sounds remarkably like a description of my 
     code! So - do great minds think alike, or should I be also ashamed of 
     my spaghetti code too? :) Anyway here's a description of my approach. 
     It's only one way of doing it, and it may not even be the best way, 
     but it does work pretty good:
     
     The parsing of MIDI data is pretty straightforward, but there are two 
     things in the 1.0 spec that cause some trouble. The first is the 
     System Real Time message, and the second is Running Status.
     
     In the MIDI protocol, valid messages can be one, two, or three bytes 
     long. Therefore it is tempting for the first-time writer of parser 
     code to grab messages from the MIDI line according to how long they 
     should be, and then once a complete message is received then go off to 
     some subroutine and do the appropriate task and return to the message 
     grabber. System Real Time messages are one byte long, but they can 
     occur at any time, including between any two bytes of an otherwise 
     valid two- or three-byte message. This will turn a three-byte message 
     for example into a four-byte message! Our simplistic hypothetical 
     grabber will choke on this. So the thing that needs to be done is to 
     have the Real Time messages stripped out and dealt with before they 
     ever reach the message grabber.
     
     Now it seems that we have saved our grabber from the possibility of 
     losing track of where it's at. But now we introduce Running Status - 
     this concept allows any of the channel messages to have a length that 
     is indeterminate! In other words, a valid three-byte message can have 
     multiple instances of the last two bytes tagged on to the end of it. 
     For example a three-note chord played on the MIDI transmitter can 
     cause a single seven-byte note-on message rather than three three-byte 
     note-on messages. This destroys our grabber idea and forces us to have 
     program control jump around from function to function, calling for the 
     next byte, depending on the context of things. This is why we need the 
     state machine approach.
     
     So - we have two fundamental requirements that are not obvious from 
     the start: first, we need to have a "get-a-byte" function that can be 
     called from anywhere, that deals with Real-Time messages. Second, we 
     need to call that function from several places in the program, not 
     just from one central place. Exactly where we are in the program at 
     any given time is determined by a handful of global variables that can 
     be updated from several places.
     
     Another advantage to the state machine approach is that it allows you 
     to add some simple tests to reset everything if an invalid message 
     comes in. In other words, if a three-byte message is missing the third 
     byte, a state machine can detect this and abort the message. In fact, 
     the state machine can act on the first two bytes as they come in, so 
     that a note-on for example can turn a note on, change the note number, 
     yet leave the velocity at its last valid value. This means that 
     yanking a MIDI cable cannot possibly lock up your parser.
     
     As soon as I can dig up my C listing I can post fragments of it. I can 
     post parts of the QuickBasic version right away but I suspect that C 
     would be a better language to share code with.
     
     - Gene
     gstopp at fibermux.com
     




More information about the Synth-diy mailing list