Jump to content
43oh

MIDI implementation


Recommended Posts

After finishing some of my MIDI projects (Light Controller and Monitor,) I wasn't fully satisfied with the MIDI implementation I did. I wanted to rewrite it but I didn't have proper motivation.

 

Then, oPossum posted his awesome FraunchPad Poly Synth, and that gave me some inspiration and couple of good ideas, great thanks.

 

So here is my first version of (almost) full implementation of MIDI which can be used for any project, as is or simplified.

Any comments? Is anything missing here?

 

#include "msp430g2553.h"

unsigned char status = 0;
unsigned char channel = 0;
unsigned char byteNumber = 1;
unsigned char byteOne = 0;

void handleMIDI(unsigned char rxByte);
void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity);
void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity);
void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value);
void handleAfterTouch(unsigned char channel, unsigned char noteNumber, unsigned char amount);
void handleProgramChange(unsigned char channel, unsigned char number);
void handleChannelPressure(unsigned char channel, unsigned char amount);
void handlePitchWheel(unsigned char channel, unsigned char fine, unsigned char coarse);
void handleSystemRealTime(unsigned char status);

void startSysEx();
void endSysEx();
void addToSysExBuffer(unsigned char data);

void main(void) {}

void handleMIDI(unsigned char rxByte) {
   if(rxByte & BIT7) {							// is it status?
       if((rxByte & 0xF8) == 0xF8) {			// System RealTime category?
           handleSystemRealTime(rxByte);		// does not affect status or byteNumer
       } else if ((rxByte & 0xF0) == 0xF0) {	// System Common category?
           status = rxByte;
           switch(status) {
           case 0xF0:
               startSysEx();
               break;
           case 0xF7:
               endSysEx();						// no data bytes
               break;
           case 0xF6:
               // handleTuneRequest();			// no data bytes
               break;
           }
           byteNumber = 1;
       } else {								// Voice category
           channel = rxByte & 0x0F;
           status = rxByte & 0xF0;
           byteNumber = 1;
       }
   } else {									// it's data byte
       if(byteNumber == 1) {					// is it first data byte?
           switch(status) {
           case 0xC0:
               handleProgramChange(channel, rxByte);
               break;
           case 0xD0:
               handleChannelPressure(channel, rxByte);
               break;
           case 0xF0:
               addToSysExBuffer(rxByte);
               break;
           case 0xF1:
               // handleMTCQuarterFrameMessage(rxByte);
               break;
           case 0xF3:
               // handleSongSelect(rxByte);
               break;
           default:
               byteNumber = 2;						// second byte will follow
               byteOne = rxByte;
           }
       } else {
           byteNumber = 1;					// next byte will be first byte
           switch(status) {
           case 0x90:
               if(byteOne) {
                   handleNoteOn(channel, byteOne, rxByte);
                   break;
               }
           case 0x80:
               handleNoteOff(channel, byteOne, rxByte);
               break;
           case 0xA0:
               handleAfterTouch(channel, byteOne, rxByte);
               break;
           case 0xB0:
               handleControlChange(channel, byteOne, rxByte);
               break;
           case 0xE0:
               handlePitchWheel(channel, byteOne, rxByte);
               break;
           case 0xF2:
               // handleSongPositionPointer(byteOne, rxByte);
               break;
           }
       }
   }
}

void handleNoteOn(unsigned char channel, unsigned char noteNumber, unsigned char velocity) {
   //if(channel == myChannel) soundOn(noteNumber, velocity);
}

void handleNoteOff(unsigned char channel, unsigned char noteNumber, unsigned char velocity) {
   //if(channel == myChannel) soundOff(noteNumber, velocity);
}

void handleControlChange(unsigned char channel, unsigned char controllerNumber, unsigned char value) {
   switch(controllerNumber) {
   case 123: //handleAllNotesOff();
       break;
       // etc.
   }
}

void handleAfterTouch(unsigned char channel, unsigned char noteNumber, unsigned char amount) {
   // handle after touch
}

void handleProgramChange(unsigned char channel, unsigned char number) {
   // handle PC
}
void handleChannelPressure(unsigned char channel, unsigned char amount) {
   // handle pressure
}

void handlePitchWheel(unsigned char channel, unsigned char fine, unsigned char coarse) {
   unsigned int wheel;
   wheel = coarse;
   wheel <<= 7;
   wheel |= fine;
   //handlePitchWheel(wheel);
}

void handleSystemRealTime(unsigned char status) {
   switch(status) {
   case 0xF8: // handleMIDIClock();
       break;
   case 0xFA: // handleMIDIStart();
       break;
   case 0xFB: // handleMIDIContinue();
       break;
   case 0xFC: // handleMIDIStop();
       break;
   case 0xFE: // handleActiveSense();
       break;
   case 0xFF: // handleReset();
       break;
   }
}

void startSysEx() {
   // setup buffer
}

void endSysEx() {
   // process sysex buffer
}

void addToSysExBuffer(unsigned char data) {
   // addToBuffer(data);
}

Link to post
Share on other sites

Here is an extended version of the Fraunchpad synth MIDI parser. This handles all MIDI messages. It uses tables and a state machine for decode and dispatch. A channel bitmask is used to allow one or more channels to be handled.

 

// MIDI message handler function typedef
typedef void(*pfnMIDI)(unsigned cmd, unsigned d1, unsigned d2);

// Dummy function for unhandled and undefined MIDI messages
static void midiNone(unsigned cmd, unsigned d1, unsigned d2) {}

static void midiNoteOff(unsigned cmd, unsigned d1, unsigned d2)
{
}

static void midiNoteOn(unsigned cmd, unsigned d1, unsigned d2)
{
   if(d2 == 0) { midiNoteOff(cmd, d1, 0); return; }
}

static const pfnMIDI mf[24] = { // --- Array of MIDI function pointers
                       // - System Common
   midiNone,           // F0 Sysex begin
   midiNone,           // F1 Quarter frame
   midiNone,           // F2 Song position
   midiNone,           // F3 Song select
   midiNone,           // F4 Undefined
   midiNone,           // F5 Undefined
   midiNone,           // F6 Tune request
   midiNone,           // F7 Sysex end
                       // - Voice
   midiNoteOff,        // 8x Note off
   midiNoteOn,         // 9x Note on
   midiNone,           // Ax Aftertouch
   midiNone,           // Bx Controller
   midiNone,           // Cx Program change
   midiNone,           // Dx Channel aftertouch
   midiNone,           // Ex Pitch bend
   midiNone,           //    Sysex data (system common)
                       // - System Real Time
   midiNone,           // F8 Clock
   midiNone,           // F9 Tick
   midiNone,           // FA Start
   midiNone,           // FB Continue
   midiNone,           // FC Stop
   midiNone,           // FD Undefined
   midiNone,           // FE Active sense
   midiNone            // FF Reset
};

static void midi(unsigned c)            // --- MIDI Parser ---
{                                       //
                                       // Initial state for message
                                 // F0 F1 F2 F3 F3 F5 F6 F7 8x 9x Ax Bx Cx Dx Ex
   static const unsigned mis[15] = { 4, 5, 6, 5, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2 };
   static unsigned state = 0;          //
   static unsigned rs = 0;             //
   static unsigned d1 = 0;             //
   static unsigned channel;            //
   static unsigned channel_mask = 0x0001; // Channel(s) to respond to
                                       //   
   if(c & 0x80) {                      // First octet of sequence
       channel = c & 0x0F;             // Extract channel from first octet
       if(c <= 0xEF) {                 // --- Handle Voice messages 0x80 -> 0xEF ---
           if(channel_mask & (1 << channel)) { // Check if on our channel(s)
               rs = c;                 // Save first octet (running status)
               state = mis[rs >> 4];   // Set initial state for message type
           } else {                    //
               state = 0;              // Not for us, discard
           }                           //
       } else if(c <= 0xF7) {          // --- Handle System Common messages 0xF0 -> 0xF7 ---
           rs = c;                     // Save command octet
           state = mis[channel];       // Set inital state for message type
           if(state < 5)               // Sysex (state 4) is handled immediately and has state change
               mf[channel](c, 0, 0);   // Handle one octet messages immediately
       } else {                        //
           mf[channel + 8](c, 0, 0);   // --- Handle System Real Time messages 0xF8 -> 0xFF (one octet) ---
           channel = rs & 0x0F;        // Do not reset running status, or change state
       }                               //
   } else {                            // --- State machine for second and following octets ---
       switch(state) {                 //
           case 0:                     // - Discard            
               break;                  //
           case 1:                     // - Second and final octet (voice messages)
               mf[rs >> 4](rs, c, 0);  //
               break;                  //
           case 3:                     // - Third and final octet (voice messages)
               mf[rs >> 4](rs, d1, c); //
               state = 2;              //
               break;                  //
           case 5:                     // - Second and final octet (system common message)
               mf[channel](rs, c, 0);  //
               state = 0;              // No running status for system messages
               break;                  //
           case 7:                     // - Third and final octet (system common message)
               mf[channel](rs, d1, c); //
               state = 0;              // No running status for system messages
               break;                  //
           case 4:                     // - Sysex data
               mf[15](rs, c, 0);       //
               break;                  //
           case 2:                     // - Second octet of three
           case 6:                     //
               d1 = c;                 //
               ++state;                //
               break;                  //
       }                               //
   }                                   //
}

 

Compact version of the parser without all those silly comments. :) (code is the same, just reformatted)

void parse_midi(unsigned c) {
static const unsigned mis[15] = { 4, 5, 6, 5, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2 };
static unsigned state = 0, rs, d1, channel;
static unsigned channel_mask = 0x0001;
if(c & 0x80) {
	channel = c & 0x0F;
	if(c <= 0xEF) {
		if(channel_mask & (1 << channel)) {
			rs = c;	state = mis[rs >> 4];
		} else state = 0;
	} else if(c <= 0xF7) {
		rs = c;	state = mis[channel];
		if(state < 5) mf[channel](c, 0, 0);
	} else {
		 mf[channel + 8](c, 0, 0);
		 channel = rs & 0x0F;
	}
} else switch(state) {
	case 1: mf[rs >> 4](rs, c, 0); break;
	case 3: mf[rs >> 4](rs, d1, c); state = 2; break;
	case 5: mf[channel](rs, c, 0);  state = 0; break;
	case 7: mf[channel](rs, d1, c); state = 0; break;
	case 4: mf[15](rs, c, 0); break;
	case 2: case 6: d1 = c; ++state; break;
}
}

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...