RobG 1,892 Posted June 27, 2011 Share Posted June 27, 2011 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); } zeke and VIPTech 2 Quote Link to post Share on other sites
oPossum 1,083 Posted June 27, 2011 Share Posted June 27, 2011 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; } } zeke, Rickta59 and bluehash 3 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.