MIDI lib for Gemphet8 localized

Dependents:   Gemphet8

Fork of MIDI by Suga koubou

Files at this revision

API Documentation at this revision

Comitter:
okini3939
Date:
Mon Dec 03 14:20:05 2012 +0000
Child:
1:0eeca7deec08
Commit message:
1st build;

Changed in this revision

MIDI.cpp Show annotated file Show diff for this revision Revisions of this file
MIDI.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MIDI.cpp	Mon Dec 03 14:20:05 2012 +0000
@@ -0,0 +1,1149 @@
+/*!
+ *  @file       MIDI.cpp
+ *  Project     MIDI Library
+ *  @brief      MIDI Library for the Arduino
+ *  @version    3.2
+ *  @author     Francois Best 
+ *  @date       24/02/11
+ *  license     GPL Forty Seven Effects - 2011
+ */
+/*
+ * Ported for mbed by Hiroshi Suga
+ *   Orignal: http://www.arduino.cc/playground/Main/MIDILibrary
+ */
+
+#include "MIDI.h"
+#include "mbed.h"
+
+
+
+/*! \brief Default constructor for MIDI. */
+MIDI::MIDI(PinName p_tx, PinName p_rx) : _midi(p_tx, p_rx)
+{ 
+    
+#if USE_CALLBACKS
+    
+    // Initialise callbacks to NULL pointer
+    mNoteOffCallback                = NULL;
+    mNoteOnCallback                 = NULL;
+    mAfterTouchPolyCallback         = NULL;
+    mControlChangeCallback          = NULL;
+    mProgramChangeCallback          = NULL;
+    mAfterTouchChannelCallback      = NULL;
+    mPitchBendCallback              = NULL;
+    mSystemExclusiveCallback        = NULL;
+    mTimeCodeQuarterFrameCallback   = NULL;
+    mSongPositionCallback           = NULL;
+    mSongSelectCallback             = NULL;
+    mTuneRequestCallback            = NULL;
+    mClockCallback                  = NULL;
+    mStartCallback                  = NULL;
+    mContinueCallback               = NULL;
+    mStopCallback                   = NULL;
+    mActiveSensingCallback          = NULL;
+    mSystemResetCallback            = NULL;
+    
+#endif
+    
+}
+
+
+/*! \brief Default destructor for MIDI.
+ 
+ This is not really useful for the Arduino, as it is never called...
+ */
+MIDI::~MIDI()
+{
+
+}
+
+
+/*! \brief Call the begin method in the setup() function of the Arduino.
+ 
+ All parameters are set to their default values:
+ - Input channel set to 1 if no value is specified
+ - Full thru mirroring
+ */
+void MIDI::begin(const byte inChannel)
+{
+    
+    // Initialise the Serial port
+    USE_SERIAL_PORT.baud(MIDI_BAUDRATE);
+    
+    
+#if COMPILE_MIDI_OUT
+    
+#if USE_RUNNING_STATUS
+    
+    mRunningStatus_TX = InvalidType;
+    
+#endif // USE_RUNNING_STATUS
+    
+#endif // COMPILE_MIDI_OUT
+    
+    
+#if COMPILE_MIDI_IN
+    
+    mInputChannel = inChannel;
+    mRunningStatus_RX = InvalidType;
+    mPendingMessageIndex = 0;
+    mPendingMessageExpectedLenght = 0;
+    
+    mMessage.valid = false;
+    mMessage.type = InvalidType;
+    mMessage.channel = 0;
+    mMessage.data1 = 0;
+    mMessage.data2 = 0;
+    
+#endif // COMPILE_MIDI_IN
+    
+    
+#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
+    
+    mThruFilterMode = Full;
+    mThruActivated = true;
+    
+#endif // Thru
+    
+}
+
+
+#if COMPILE_MIDI_OUT
+
+// Private method for generating a status byte from channel and type
+byte MIDI::genstatus(const kMIDIType inType,
+                                 const byte inChannel) const
+{
+    
+    return ((byte)inType | ((inChannel-1) & 0x0F));
+    
+}
+
+
+/*! \brief Generate and send a MIDI message from the values given.
+ \param type    The message type (see type defines for reference)
+ \param data1   The first data byte.
+ \param data2   The second data byte (if the message contains only 1 data byte, set this one to 0).
+ \param channel The output channel on which the message will be sent (values from 1 to 16). Note: you cannot send to OMNI.
+ 
+ This is an internal method, use it only if you need to send raw data from your code, at your own risks.
+ */
+void MIDI::send(kMIDIType type,
+                      byte data1,
+                      byte data2,
+                      byte channel)
+{
+    
+    // Then test if channel is valid
+    if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) {
+        
+#if USE_RUNNING_STATUS  
+        mRunningStatus_TX = InvalidType;
+#endif 
+        
+        return; // Don't send anything
+    }
+    
+    if (type <= PitchBend) {
+        // Channel messages
+        
+        // Protection: remove MSBs on data
+        data1 &= 0x7F;
+        data2 &= 0x7F;
+        
+        byte statusbyte = genstatus(type,channel);
+        
+#if USE_RUNNING_STATUS
+        // Check Running Status
+        if (mRunningStatus_TX != statusbyte) {
+            // New message, memorise and send header
+            mRunningStatus_TX = statusbyte;
+            USE_SERIAL_PORT.putc(mRunningStatus_TX);
+        }
+#else
+        // Don't care about running status, send the Control byte.
+        USE_SERIAL_PORT.putc(statusbyte);
+#endif
+        
+        // Then send data
+        USE_SERIAL_PORT.putc(data1);
+        if (type != ProgramChange && type != AfterTouchChannel) {
+            USE_SERIAL_PORT.putc(data2);
+        }
+        return;
+    }
+    if (type >= TuneRequest && type <= SystemReset) {
+        // System Real-time and 1 byte.
+        sendRealTime(type);
+    }
+    
+}
+
+
+/*! \brief Send a Note On message 
+ \param NoteNumber  Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n
+ \param Velocity    Note attack velocity (0 to 127). A NoteOn with 0 velocity is considered as a NoteOff.
+ \param Channel     The channel on which the message will be sent (1 to 16). 
+ */
+void MIDI::sendNoteOn(byte NoteNumber,
+                            byte Velocity,
+                            byte Channel)
+{ 
+    
+    send(NoteOn,NoteNumber,Velocity,Channel);
+
+}
+
+
+/*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity)
+ \param NoteNumber  Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n
+ \param Velocity    Release velocity (0 to 127).
+ \param Channel     The channel on which the message will be sent (1 to 16).
+ */
+void MIDI::sendNoteOff(byte NoteNumber,
+                             byte Velocity,
+                             byte Channel)
+{
+    
+    send(NoteOff,NoteNumber,Velocity,Channel);
+
+}
+
+
+/*! \brief Send a Program Change message 
+ \param ProgramNumber   The Program to select (0 to 127).
+ \param Channel         The channel on which the message will be sent (1 to 16).
+ */
+void MIDI::sendProgramChange(byte ProgramNumber,
+                                   byte Channel)
+{
+    
+    send(ProgramChange,ProgramNumber,0,Channel);
+
+}
+
+
+/*! \brief Send a Control Change message 
+ \param ControlNumber   The controller number (0 to 127). See the detailed description here: http://www.somascape.org/midi/tech/spec.html#ctrlnums
+ \param ControlValue    The value for the specified controller (0 to 127).
+ \param Channel         The channel on which the message will be sent (1 to 16). 
+ */
+void MIDI::sendControlChange(byte ControlNumber,
+                                   byte ControlValue,
+                                   byte Channel)
+{
+    
+    send(ControlChange,ControlNumber,ControlValue,Channel);
+
+}
+
+
+/*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note)
+ \param NoteNumber      The note to apply AfterTouch to (0 to 127).
+ \param Pressure        The amount of AfterTouch to apply (0 to 127).
+ \param Channel         The channel on which the message will be sent (1 to 16). 
+ */
+void MIDI::sendPolyPressure(byte NoteNumber,
+                                  byte Pressure,
+                                  byte Channel)
+{
+    
+    send(AfterTouchPoly,NoteNumber,Pressure,Channel);
+
+}
+
+
+/*! \brief Send a MonoPhonic AfterTouch message (applies to all notes)
+ \param Pressure        The amount of AfterTouch to apply to all notes.
+ \param Channel         The channel on which the message will be sent (1 to 16). 
+ */
+void MIDI::sendAfterTouch(byte Pressure,
+                                byte Channel)
+{
+    
+    send(AfterTouchChannel,Pressure,0,Channel);
+
+}
+
+
+/*! \brief Send a Pitch Bend message using a signed integer value.
+ \param PitchValue  The amount of bend to send (in a signed integer format), between -8192 (maximum downwards bend) and 8191 (max upwards bend), center value is 0.
+ \param Channel     The channel on which the message will be sent (1 to 16).
+ */
+void MIDI::sendPitchBend(int PitchValue,
+                               byte Channel)
+{
+    
+    unsigned int bend = PitchValue + 8192;
+    sendPitchBend(bend,Channel);
+    
+}
+
+
+/*! \brief Send a Pitch Bend message using an unsigned integer value.
+ \param PitchValue  The amount of bend to send (in a signed integer format), between 0 (maximum downwards bend) and 16383 (max upwards bend), center value is 8192.
+ \param Channel     The channel on which the message will be sent (1 to 16).
+ */
+void MIDI::sendPitchBend(unsigned int PitchValue,
+                               byte Channel)
+{
+    
+    send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel);
+    
+}
+
+
+/*! \brief Send a Pitch Bend message using a floating point value.
+ \param PitchValue  The amount of bend to send (in a floating point format), between -1.0f (maximum downwards bend) and +1.0f (max upwards bend), center value is 0.0f.
+ \param Channel     The channel on which the message will be sent (1 to 16).
+ */
+void MIDI::sendPitchBend(double PitchValue,
+                               byte Channel)
+{
+    
+    unsigned int pitchval = (PitchValue+1.f)*8192;
+    if (pitchval > 16383) pitchval = 16383;     // overflow protection
+    sendPitchBend(pitchval,Channel);
+    
+}
+
+
+/*! \brief Generate and send a System Exclusive frame.
+ \param length  The size of the array to send
+ \param array   The byte array containing the data to send
+ \param ArrayContainsBoundaries  When set to 'true', 0xF0 & 0xF7 bytes (start & stop SysEx) will NOT be sent (and therefore must be included in the array).
+ default value is set to 'false' for compatibility with previous versions of the library.
+ */
+void MIDI::sendSysEx(int length,
+                           const byte *const array,
+                           bool ArrayContainsBoundaries)
+{
+    
+    if (ArrayContainsBoundaries == false) {
+        
+        USE_SERIAL_PORT.putc(0xF0);
+        
+        for (int i=0;i<length;++i) {
+            
+            USE_SERIAL_PORT.putc(array[i]);
+            
+        }
+        
+        USE_SERIAL_PORT.putc(0xF7);
+        
+    }
+    else {
+        
+        for (int i=0;i<length;++i) {
+            
+            USE_SERIAL_PORT.putc(array[i]);
+            
+        }
+        
+    }
+    
+#if USE_RUNNING_STATUS
+    mRunningStatus_TX = InvalidType;
+#endif
+    
+}
+
+
+/*! \brief Send a Tune Request message. 
+ 
+ When a MIDI unit receives this message, it should tune its oscillators (if equipped with any) 
+ */
+void MIDI::sendTuneRequest()
+{
+    
+    sendRealTime(TuneRequest);
+
+}
+
+
+/*! \brief Send a MIDI Time Code Quarter Frame. 
+ 
+ See MIDI Specification for more information.
+ \param TypeNibble  MTC type
+ \param ValuesNibble    MTC data
+ */
+void MIDI::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble)
+{
+    
+    byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
+    sendTimeCodeQuarterFrame(data);
+    
+}
+
+
+/*! \brief Send a MIDI Time Code Quarter Frame. 
+ 
+ See MIDI Specification for more information.
+ \param data     if you want to encode directly the nibbles in your program, you can send the byte here.
+ */
+void MIDI::sendTimeCodeQuarterFrame(byte data)
+{
+    
+    USE_SERIAL_PORT.putc((byte)TimeCodeQuarterFrame);
+    USE_SERIAL_PORT.putc(data);
+
+#if USE_RUNNING_STATUS
+    mRunningStatus_TX = InvalidType;
+#endif
+    
+}
+
+
+/*! \brief Send a Song Position Pointer message.
+ \param Beats   The number of beats since the start of the song.
+ */
+void MIDI::sendSongPosition(unsigned int Beats)
+{
+    
+    USE_SERIAL_PORT.putc((byte)SongPosition);
+    USE_SERIAL_PORT.putc(Beats & 0x7F);
+    USE_SERIAL_PORT.putc((Beats >> 7) & 0x7F);
+
+#if USE_RUNNING_STATUS
+    mRunningStatus_TX = InvalidType;
+#endif
+    
+}
+
+
+/*! \brief Send a Song Select message */
+void MIDI::sendSongSelect(byte SongNumber)
+{
+    
+    USE_SERIAL_PORT.putc((byte)SongSelect);
+    USE_SERIAL_PORT.putc(SongNumber & 0x7F);
+
+#if USE_RUNNING_STATUS
+    mRunningStatus_TX = InvalidType;
+#endif
+    
+}
+
+
+/*! \brief Send a Real Time (one byte) message. 
+ 
+ \param Type The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset.
+ You can also send a Tune Request with this method.
+ @see kMIDIType
+ */
+void MIDI::sendRealTime(kMIDIType Type)
+{
+    switch (Type) {
+        case TuneRequest: // Not really real-time, but one byte anyway.
+        case Clock:
+        case Start:
+        case Stop:  
+        case Continue:
+        case ActiveSensing:
+        case SystemReset:
+            USE_SERIAL_PORT.putc((byte)Type);
+            break;
+        default:
+            // Invalid Real Time marker
+            break;
+    }
+    
+    // Do not cancel Running Status for real-time messages as they can be interleaved within any message.
+    // Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status.
+#if USE_RUNNING_STATUS
+    if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
+#endif
+    
+}
+
+#endif // COMPILE_MIDI_OUT
+
+
+
+#if COMPILE_MIDI_IN
+
+/*! \brief Read a MIDI message from the serial port using the main input channel (see setInputChannel() for reference).
+ 
+ Returned value: true if any valid message has been stored in the structure, false if not.
+ A valid message is a message that matches the input channel. \n\n
+ If the Thru is enabled and the messages matches the filter, it is sent back on the MIDI output.
+ */
+bool MIDI::read()
+{
+    
+    return read(mInputChannel);
+    
+}
+
+
+/*! \brief Reading/thru-ing method, the same as read() with a given input channel to read on. */
+bool MIDI::read(const byte inChannel)
+{
+    
+    if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled.
+    
+    if (parse(inChannel)) {
+        
+        if (input_filter(inChannel)) {
+            
+#if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
+            thru_filter(inChannel);
+#endif
+            
+#if USE_CALLBACKS
+            launchCallback();
+#endif
+            
+            return true;
+        }
+        
+    }
+    
+    return false;
+    
+}
+
+
+// Private method: MIDI parser
+bool MIDI::parse(byte inChannel)
+{ 
+    
+    const int bytes_available = USE_SERIAL_PORT.readable();
+    
+    if (bytes_available <= 0) {
+        // No data available.
+        return false;
+    }
+    
+    // If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
+    if (bytes_available == 128) {
+//        USE_SERIAL_PORT.flush();
+    }   
+    else {
+        
+        /* Parsing algorithm:
+         Get a byte from the serial buffer.
+         * If there is no pending message to be recomposed, start a new one.
+         - Find type and channel (if pertinent)
+         - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty.
+         * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it.
+         */
+        
+        
+        const byte extracted = USE_SERIAL_PORT.getc();
+        
+        if (mPendingMessageIndex == 0) { // Start a new pending message
+            mPendingMessage[0] = extracted;
+            
+            // Check for running status first
+            switch (getTypeFromStatusByte(mRunningStatus_RX)) {
+                    // Only these types allow Running Status:
+                case NoteOff:
+                case NoteOn:
+                case AfterTouchPoly:
+                case ControlChange:
+                case ProgramChange:
+                case AfterTouchChannel:
+                case PitchBend: 
+                    
+                    // If the status byte is not received, prepend it to the pending message
+                    if (extracted < 0x80) {
+                        mPendingMessage[0] = mRunningStatus_RX;
+                        mPendingMessage[1] = extracted;
+                        mPendingMessageIndex = 1;
+                    }
+                    // Else: well, we received another status byte, so the running status does not apply here.
+                    // It will be updated upon completion of this message.
+                    
+                    break;
+                    
+                default:
+                    // No running status
+                    break;
+            }
+            
+            
+            switch (getTypeFromStatusByte(mPendingMessage[0])) {
+                    
+                    // 1 byte messages
+                case Start:
+                case Continue:
+                case Stop:
+                case Clock:
+                case ActiveSensing:
+                case SystemReset:
+                case TuneRequest:
+                    // Handle the message type directly here.
+                    mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
+                    mMessage.channel = 0;
+                    mMessage.data1 = 0;
+                    mMessage.data2 = 0;
+                    mMessage.valid = true;
+                    
+                    // \fix Running Status broken when receiving Clock messages.
+                    // Do not reset all input attributes, Running Status must remain unchanged.
+                    //reset_input_attributes(); 
+                    
+                    // We still need to reset these
+                    mPendingMessageIndex = 0;
+                    mPendingMessageExpectedLenght = 0;
+                    
+                    return true;
+                    
+                    // 2 bytes messages
+                case ProgramChange:
+                case AfterTouchChannel:
+                case TimeCodeQuarterFrame:
+                case SongSelect:
+                    mPendingMessageExpectedLenght = 2;
+                    break;
+                    
+                    // 3 bytes messages
+                case NoteOn:
+                case NoteOff:
+                case ControlChange:
+                case PitchBend:
+                case AfterTouchPoly:
+                case SongPosition:
+                    mPendingMessageExpectedLenght = 3;
+                    break;
+                    
+                case SystemExclusive:
+                    mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
+                    mRunningStatus_RX = InvalidType;
+                    break;
+                    
+                case InvalidType:
+                default:
+                    // This is obviously wrong. Let's get the hell out'a here.
+                    reset_input_attributes();
+                    return false;
+            }
+            
+            // Then update the index of the pending message.
+            mPendingMessageIndex++;
+            
+#if USE_1BYTE_PARSING
+            // Message is not complete.
+            return false;
+#else
+            // Call the parser recursively
+            // to parse the rest of the message.
+            return parse(inChannel);
+#endif
+            
+        }
+        else { 
+            
+            // First, test if this is a status byte
+            if (extracted >= 0x80) {
+                
+                // Reception of status bytes in the middle of an uncompleted message
+                // are allowed only for interleaved Real Time message or EOX
+                switch (extracted) {
+                    case Clock:
+                    case Start:
+                    case Continue:
+                    case Stop:
+                    case ActiveSensing:
+                    case SystemReset:
+                        
+                        /*
+                         This is tricky. Here we will have to extract the one-byte message,
+                         pass it to the structure for being read outside the MIDI class,
+                         and recompose the message it was interleaved into.
+                         
+                         Oh, and without killing the running status.. 
+                         
+                         This is done by leaving the pending message as is, it will be completed on next calls.
+                         */
+                        
+                        mMessage.type = (kMIDIType)extracted;
+                        mMessage.data1 = 0;
+                        mMessage.data2 = 0;
+                        mMessage.channel = 0;
+                        mMessage.valid = true;
+                        return true;
+                        
+                        // End of Exclusive
+                    case 0xF7:
+                        if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
+                            
+                            // Store System Exclusive array in midimsg structure
+                            for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) {
+                                mMessage.sysex_array[i] = mPendingMessage[i];
+                            }
+                            
+                            mMessage.type = SystemExclusive;
+
+                            // Get length
+                            mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;   
+                            mMessage.data2 = (mPendingMessageIndex+1) >> 8;
+                            
+                            mMessage.channel = 0;
+                            mMessage.valid = true;
+                            
+                            reset_input_attributes();
+                            
+                            return true;
+                        }
+                        else {
+                            // Well well well.. error.
+                            reset_input_attributes();
+                            return false;
+                        }
+                        
+                    default:
+                        break;
+                }
+                
+                
+                
+            }
+            
+            
+            // Add extracted data byte to pending message
+            mPendingMessage[mPendingMessageIndex] = extracted;
+            
+            
+            // Now we are going to check if we have reached the end of the message
+            if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) {
+                
+                // "FML" case: fall down here with an overflown SysEx..
+                // This means we received the last possible data byte that can fit the buffer.
+                // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
+                if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
+                    reset_input_attributes();
+                    return false;
+                }
+                
+                
+                mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
+                mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message
+                
+                mMessage.data1 = mPendingMessage[1];
+                
+                // Save data2 only if applicable
+                if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2];
+                else mMessage.data2 = 0;
+                
+                // Reset local variables
+                mPendingMessageIndex = 0;
+                mPendingMessageExpectedLenght = 0;
+                
+                mMessage.valid = true;
+                
+                // Activate running status (if enabled for the received type)
+                switch (mMessage.type) {
+                    case NoteOff:
+                    case NoteOn:
+                    case AfterTouchPoly:
+                    case ControlChange:
+                    case ProgramChange:
+                    case AfterTouchChannel:
+                    case PitchBend: 
+                        // Running status enabled: store it from received message
+                        mRunningStatus_RX = mPendingMessage[0];
+                        break;
+                        
+                    default:
+                        // No running status
+                        mRunningStatus_RX = InvalidType;
+                        break;
+                }
+                return true;
+            }
+            else {
+                // Then update the index of the pending message.
+                mPendingMessageIndex++;
+                
+#if USE_1BYTE_PARSING
+                // Message is not complete.
+                return false;
+#else
+                // Call the parser recursively
+                // to parse the rest of the message.
+                return parse(inChannel);
+#endif
+                
+            }
+            
+        }
+        
+    }
+    
+    // What are our chances to fall here?
+    return false;
+}
+
+
+// Private method: check if the received message is on the listened channel
+bool MIDI::input_filter(byte inChannel)
+{
+    
+    
+    // This method handles recognition of channel (to know if the message is destinated to the Arduino)
+    
+    
+    if (mMessage.type == InvalidType) return false;
+    
+    
+    // First, check if the received message is Channel
+    if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
+        
+        // Then we need to know if we listen to it
+        if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) {
+            return true;
+            
+        }
+        else {
+            // We don't listen to this channel
+            return false;
+        }
+        
+    }
+    else {
+        
+        // System messages are always received
+        return true;
+    }
+    
+}
+
+
+// Private method: reset input attributes
+void MIDI::reset_input_attributes()
+{
+    
+    mPendingMessageIndex = 0;
+    mPendingMessageExpectedLenght = 0;
+    mRunningStatus_RX = InvalidType;
+    
+}
+
+
+// Getters
+/*! \brief Get the last received message's type
+ 
+ Returns an enumerated type. @see kMIDIType
+ */
+kMIDIType MIDI::getType() const
+{
+    
+    return mMessage.type;
+
+}
+
+
+/*! \brief Get the channel of the message stored in the structure.
+ 
+ Channel range is 1 to 16. For non-channel messages, this will return 0.
+ */
+byte MIDI::getChannel() const
+{
+    
+    return mMessage.channel;
+
+}
+
+
+/*! \brief Get the first data byte of the last received message. */
+byte MIDI::getData1() const
+{
+    
+    return mMessage.data1;
+
+}
+
+
+/*! \brief Get the second data byte of the last received message. */
+byte MIDI::getData2() const
+{ 
+    
+    return mMessage.data2;
+
+}
+
+
+/*! \brief Get the System Exclusive byte array. 
+ 
+ @see getSysExArrayLength to get the array's length in bytes.
+ */
+const byte * MIDI::getSysExArray() const
+{ 
+    
+    return mMessage.sysex_array;
+
+}
+
+/*! \brief Get the lenght of the System Exclusive array.
+ 
+ It is coded using data1 as LSB and data2 as MSB.
+ \return The array's length, in bytes.
+ */
+unsigned int MIDI::getSysExArrayLength() const
+{
+    
+    unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1;
+    
+    return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size;
+    
+}
+
+
+/*! \brief Check if a valid message is stored in the structure. */
+bool MIDI::check() const
+{ 
+    
+    return mMessage.valid;
+
+}
+
+
+// Setters
+/*! \brief Set the value for the input MIDI channel 
+ \param Channel the channel value. Valid values are 1 to 16, 
+ MIDI_CHANNEL_OMNI if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable MIDI input.
+ */
+void MIDI::setInputChannel(const byte Channel)
+{ 
+    
+    mInputChannel = Channel;
+    
+}
+
+
+#if USE_CALLBACKS
+
+void MIDI::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity))         { mNoteOffCallback = fptr; }
+void MIDI::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity))          { mNoteOnCallback = fptr; }
+void MIDI::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure))  { mAfterTouchPolyCallback = fptr; }
+void MIDI::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value))    { mControlChangeCallback = fptr; }
+void MIDI::setHandleProgramChange(void (*fptr)(byte channel, byte number))                { mProgramChangeCallback = fptr; }
+void MIDI::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure))          { mAfterTouchChannelCallback = fptr; }
+void MIDI::setHandlePitchBend(void (*fptr)(byte channel, int bend))                       { mPitchBendCallback = fptr; }
+void MIDI::setHandleSystemExclusive(void (*fptr)(byte * array, byte size))                { mSystemExclusiveCallback = fptr; }
+void MIDI::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data))                         { mTimeCodeQuarterFrameCallback = fptr; }
+void MIDI::setHandleSongPosition(void (*fptr)(unsigned int beats))                        { mSongPositionCallback = fptr; }
+void MIDI::setHandleSongSelect(void (*fptr)(byte songnumber))                             { mSongSelectCallback = fptr; }
+void MIDI::setHandleTuneRequest(void (*fptr)(void))                                       { mTuneRequestCallback = fptr; }
+void MIDI::setHandleClock(void (*fptr)(void))                                             { mClockCallback = fptr; }
+void MIDI::setHandleStart(void (*fptr)(void))                                             { mStartCallback = fptr; }
+void MIDI::setHandleContinue(void (*fptr)(void))                                          { mContinueCallback = fptr; }
+void MIDI::setHandleStop(void (*fptr)(void))                                              { mStopCallback = fptr; }
+void MIDI::setHandleActiveSensing(void (*fptr)(void))                                     { mActiveSensingCallback = fptr; }
+void MIDI::setHandleSystemReset(void (*fptr)(void))                                       { mSystemResetCallback = fptr; }
+
+
+/*! \brief Detach an external function from the given type.
+ 
+ Use this method to cancel the effects of setHandle********.
+ \param Type        The type of message to unbind. When a message of this type is received, no function will be called.
+ */
+void MIDI::disconnectCallbackFromType(kMIDIType Type)
+{
+    
+    switch (Type) {
+        case NoteOff:               mNoteOffCallback = NULL;                break;
+        case NoteOn:                mNoteOnCallback = NULL;                 break;
+        case AfterTouchPoly:        mAfterTouchPolyCallback = NULL;         break;
+        case ControlChange:         mControlChangeCallback = NULL;          break;
+        case ProgramChange:         mProgramChangeCallback = NULL;          break;
+        case AfterTouchChannel:     mAfterTouchChannelCallback = NULL;      break;
+        case PitchBend:             mPitchBendCallback = NULL;              break;
+        case SystemExclusive:       mSystemExclusiveCallback = NULL;        break;
+        case TimeCodeQuarterFrame:  mTimeCodeQuarterFrameCallback = NULL;   break;
+        case SongPosition:          mSongPositionCallback = NULL;           break;
+        case SongSelect:            mSongSelectCallback = NULL;             break;
+        case TuneRequest:           mTuneRequestCallback = NULL;            break;
+        case Clock:                 mClockCallback = NULL;                  break;
+        case Start:                 mStartCallback = NULL;                  break;
+        case Continue:              mContinueCallback = NULL;               break;
+        case Stop:                  mStopCallback = NULL;                   break;
+        case ActiveSensing:         mActiveSensingCallback = NULL;          break;
+        case SystemReset:           mSystemResetCallback = NULL;            break;
+        default:
+            break;
+    }
+    
+}
+
+
+// Private - launch callback function based on received type.
+void MIDI::launchCallback()
+{
+    
+    // The order is mixed to allow frequent messages to trigger their callback faster.
+    
+    switch (mMessage.type) {
+            // Notes
+        case NoteOff:               if (mNoteOffCallback != NULL)               mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2);   break;
+        case NoteOn:                if (mNoteOnCallback != NULL)                mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
+            
+            // Real-time messages
+        case Clock:                 if (mClockCallback != NULL)                 mClockCallback();           break;          
+        case Start:                 if (mStartCallback != NULL)                 mStartCallback();           break;
+        case Continue:              if (mContinueCallback != NULL)              mContinueCallback();        break;
+        case Stop:                  if (mStopCallback != NULL)                  mStopCallback();            break;
+        case ActiveSensing:         if (mActiveSensingCallback != NULL)         mActiveSensingCallback();   break;
+            
+            // Continuous controllers
+        case ControlChange:         if (mControlChangeCallback != NULL)         mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
+        case PitchBend:             if (mPitchBendCallback != NULL)             mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) - 8192); break; // TODO: check this
+        case AfterTouchPoly:        if (mAfterTouchPolyCallback != NULL)        mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
+        case AfterTouchChannel:     if (mAfterTouchChannelCallback != NULL)     mAfterTouchChannelCallback(mMessage.channel,mMessage.data1);    break;
+            
+        case ProgramChange:         if (mProgramChangeCallback != NULL)         mProgramChangeCallback(mMessage.channel,mMessage.data1);    break;
+        case SystemExclusive:       if (mSystemExclusiveCallback != NULL)       mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1);  break;
+            
+            // Occasional messages
+        case TimeCodeQuarterFrame:  if (mTimeCodeQuarterFrameCallback != NULL)  mTimeCodeQuarterFrameCallback(mMessage.data1);  break;
+        case SongPosition:          if (mSongPositionCallback != NULL)          mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break;
+        case SongSelect:            if (mSongSelectCallback != NULL)            mSongSelectCallback(mMessage.data1);    break;
+        case TuneRequest:           if (mTuneRequestCallback != NULL)           mTuneRequestCallback(); break;
+            
+        case SystemReset:           if (mSystemResetCallback != NULL)           mSystemResetCallback(); break;
+        case InvalidType:
+        default:
+            break;
+    }
+    
+}
+
+
+#endif // USE_CALLBACKS
+
+
+#endif // COMPILE_MIDI_IN
+
+
+
+
+#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
+
+/*! \brief Set the filter for thru mirroring
+ \param inThruFilterMode a filter mode
+ 
+ @see kThruFilterMode
+ */
+void MIDI::setThruFilterMode(kThruFilterMode inThruFilterMode)
+{ 
+    
+    mThruFilterMode = inThruFilterMode;
+    if (mThruFilterMode != Off) mThruActivated = true;
+    else mThruActivated = false;
+    
+}
+
+
+/*! \brief Setter method: turn message mirroring on. */
+void MIDI::turnThruOn(kThruFilterMode inThruFilterMode)
+{ 
+    
+    mThruActivated = true;
+    mThruFilterMode = inThruFilterMode;
+    
+}
+
+
+/*! \brief Setter method: turn message mirroring off. */
+void MIDI::turnThruOff()
+{
+    
+    mThruActivated = false; 
+    mThruFilterMode = Off;
+    
+}
+
+
+// This method is called upon reception of a message and takes care of Thru filtering and sending.
+void MIDI::thru_filter(byte inChannel)
+{
+    
+    /*
+     This method handles Soft-Thru filtering.
+     
+     Soft-Thru filtering:
+     - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off
+     - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting
+     
+     */
+    
+    // If the feature is disabled, don't do anything.
+    if (!mThruActivated || (mThruFilterMode == Off)) return;
+    
+    
+    // First, check if the received message is Channel
+    if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
+        
+        const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI));
+        
+        // Now let's pass it to the output
+        switch (mThruFilterMode) {
+            case Full:
+                send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
+                return;
+            case SameChannel:
+                if (filter_condition) {
+                    send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
+                    return;
+                }
+                break;
+            case DifferentChannel:
+                if (!filter_condition) {
+                    send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
+                    return;
+                }
+                break;
+            case Off:
+                // Do nothing. 
+                // Technically it's impossible to get there because the case was already tested earlier.
+                break;
+            default:
+                break;
+        }
+        
+    }
+    else {
+        
+        // Send the message to the output
+        switch (mMessage.type) {
+                // Real Time and 1 byte
+            case Clock:
+            case Start:
+            case Stop:
+            case Continue:
+            case ActiveSensing:
+            case SystemReset:
+            case TuneRequest:   
+                sendRealTime(mMessage.type);
+                return;
+                
+            case SystemExclusive:
+                // Send SysEx (0xF0 and 0xF7 are included in the buffer)
+                sendSysEx(mMessage.data1,mMessage.sysex_array,true); 
+                return;
+                
+            case SongSelect:
+                sendSongSelect(mMessage.data1);
+                return;
+                
+            case SongPosition:
+                sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));
+                return;
+                
+            case TimeCodeQuarterFrame:
+                sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
+                return;
+
+            default:
+                break;
+                
+        }
+        
+    }
+    
+}
+
+
+#endif // Thru
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MIDI.h	Mon Dec 03 14:20:05 2012 +0000
@@ -0,0 +1,319 @@
+/*!
+ *  @file       MIDI.h
+ *  Project     MIDI Library
+ *  @brief      MIDI Library for the Arduino
+ *  Version     3.2
+ *  @author     Francois Best 
+ *  @date       24/02/11
+ *  License     GPL Forty Seven Effects - 2011
+ */
+/*
+ * Ported for mbed by Hiroshi Suga
+ *   Orignal: http://www.arduino.cc/playground/Main/MIDILibrary
+ */
+
+#ifndef LIB_MIDI_H_
+#define LIB_MIDI_H_
+
+#include "mbed.h"
+
+
+/*  
+    ###############################################################
+    #                                                             #
+    #    CONFIGURATION AREA                                       #
+    #                                                             #
+    #    Here are a few settings you can change to customize      #
+    #    the library for your own project. You can for example    #
+    #    choose to compile only parts of it so you gain flash     #
+    #    space and optimise the speed of your sketch.             #
+    #                                                             #
+    ###############################################################
+ */
+
+
+#define COMPILE_MIDI_IN         1           // Set this setting to 1 to use the MIDI input.
+#define COMPILE_MIDI_OUT        1           // Set this setting to 1 to use the MIDI output. 
+#define COMPILE_MIDI_THRU       1           // Set this setting to 1 to use the MIDI Soft Thru feature
+                                            // Please note that the Thru will work only when both COMPILE_MIDI_IN and COMPILE_MIDI_OUT set to 1.
+
+
+#define USE_SERIAL_PORT         _midi      // Change the number (to Serial1 for example) if you want
+                                            // to use a different serial port for MIDI I/O.
+
+
+#define USE_RUNNING_STATUS      1           // Running status enables short messages when sending multiple values
+                                            // of the same type and channel.
+                                            // Set to 0 if you have troubles with controlling you hardware.
+
+
+#define USE_CALLBACKS           1           // Set this to 1 if you want to use callback handlers (to bind your functions to the library).
+                                            // To use the callbacks, you need to have COMPILE_MIDI_IN set to 1
+
+#define USE_1BYTE_PARSING       1           // Each call to MIDI.read will only parse one byte (might be faster).
+
+
+// END OF CONFIGURATION AREA 
+// (do not modify anything under this line unless you know what you are doing)
+
+
+#define MIDI_BAUDRATE           31250
+
+#define MIDI_CHANNEL_OMNI       0
+#define MIDI_CHANNEL_OFF        17          // and over
+
+#define MIDI_SYSEX_ARRAY_SIZE   255         // Maximum size is 65535 bytes.
+
+/*! Type definition for practical use (because "unsigned char" is a bit long to write.. )*/
+typedef uint8_t byte;
+typedef uint16_t word;
+
+/*! Enumeration of MIDI types */
+enum kMIDIType {
+    NoteOff               = 0x80,   ///< Note Off
+    NoteOn                = 0x90,   ///< Note On
+    AfterTouchPoly        = 0xA0,   ///< Polyphonic AfterTouch
+    ControlChange         = 0xB0,   ///< Control Change / Channel Mode
+    ProgramChange         = 0xC0,   ///< Program Change
+    AfterTouchChannel     = 0xD0,   ///< Channel (monophonic) AfterTouch
+    PitchBend             = 0xE0,   ///< Pitch Bend
+    SystemExclusive       = 0xF0,   ///< System Exclusive
+    TimeCodeQuarterFrame  = 0xF1,   ///< System Common - MIDI Time Code Quarter Frame
+    SongPosition          = 0xF2,   ///< System Common - Song Position Pointer
+    SongSelect            = 0xF3,   ///< System Common - Song Select
+    TuneRequest           = 0xF6,   ///< System Common - Tune Request
+    Clock                 = 0xF8,   ///< System Real Time - Timing Clock
+    Start                 = 0xFA,   ///< System Real Time - Start
+    Continue              = 0xFB,   ///< System Real Time - Continue
+    Stop                  = 0xFC,   ///< System Real Time - Stop
+    ActiveSensing         = 0xFE,   ///< System Real Time - Active Sensing
+    SystemReset           = 0xFF,   ///< System Real Time - System Reset
+    InvalidType           = 0x00    ///< For notifying errors
+};
+
+/*! Enumeration of Thru filter modes */
+enum kThruFilterMode {
+    Off                   = 0,  ///< Thru disabled (nothing passes through).
+    Full                  = 1,  ///< Fully enabled Thru (every incoming message is sent back).
+    SameChannel           = 2,  ///< Only the messages on the Input Channel will be sent back.
+    DifferentChannel      = 3   ///< All the messages but the ones on the Input Channel will be sent back.
+};
+
+
+/*! The midimsg structure contains decoded data of a MIDI message read from the serial port with read() or thru(). \n */
+struct midimsg {
+    /*! The MIDI channel on which the message was recieved. \n Value goes from 1 to 16. */
+    byte channel; 
+    /*! The type of the message (see the define section for types reference) */
+    kMIDIType type;
+    /*! The first data byte.\n Value goes from 0 to 127.\n */
+    byte data1;
+    /*! The second data byte. If the message is only 2 bytes long, this one is null.\n Value goes from 0 to 127. */
+    byte data2;
+    /*! System Exclusive dedicated byte array. \n Array length is stocked on 16 bits, in data1 (LSB) and data2 (MSB) */
+    byte sysex_array[MIDI_SYSEX_ARRAY_SIZE];
+    /*! This boolean indicates if the message is valid or not. There is no channel consideration here, validity means the message respects the MIDI norm. */
+    bool valid;
+};
+
+
+
+
+/*! \brief The main class for MIDI handling.\n
+    See member descriptions to know how to use it,
+    or check out the examples supplied with the library.
+ */
+class MIDI {
+    
+    
+public:
+    // Constructor and Destructor
+    MIDI(PinName p_tx, PinName p_rx);
+    ~MIDI();
+    
+    
+    void begin(const byte inChannel = 1);
+    
+    
+    
+    
+/* ####### OUTPUT COMPILATION BLOCK ####### */  
+#if COMPILE_MIDI_OUT
+
+public: 
+    
+    void sendNoteOn(byte NoteNumber,byte Velocity,byte Channel);
+    void sendNoteOff(byte NoteNumber,byte Velocity,byte Channel);
+    void sendProgramChange(byte ProgramNumber,byte Channel);
+    void sendControlChange(byte ControlNumber, byte ControlValue,byte Channel);
+    void sendPitchBend(int PitchValue,byte Channel);
+    void sendPitchBend(unsigned int PitchValue,byte Channel);
+    void sendPitchBend(double PitchValue,byte Channel);
+    void sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel);
+    void sendAfterTouch(byte Pressure,byte Channel);
+    void sendSysEx(int length, const byte *const array,bool ArrayContainsBoundaries = false);   
+    void sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble);
+    void sendTimeCodeQuarterFrame(byte data);
+    void sendSongPosition(unsigned int Beats);
+    void sendSongSelect(byte SongNumber);
+    void sendTuneRequest();
+    void sendRealTime(kMIDIType Type);
+    
+    void send(kMIDIType type, byte param1, byte param2, byte channel);
+    
+private:
+    
+    Serial _midi;
+    byte genstatus(const kMIDIType inType,const byte inChannel) const;
+    
+    
+    // Attributes
+#if USE_RUNNING_STATUS
+    byte            mRunningStatus_TX;
+#endif // USE_RUNNING_STATUS
+
+#endif  // COMPILE_MIDI_OUT
+    
+
+    
+/* ####### INPUT COMPILATION BLOCK ####### */
+#if COMPILE_MIDI_IN 
+    
+public:
+    
+    bool read();
+    bool read(const byte Channel);
+    
+    // Getters
+    kMIDIType getType() const;
+    byte getChannel() const;
+    byte getData1() const;
+    byte getData2() const;
+    const byte * getSysExArray() const;
+    unsigned int getSysExArrayLength() const;
+    bool check() const;
+    
+    byte getInputChannel() const 
+    {
+        return mInputChannel;
+    }
+    
+    // Setters
+    void setInputChannel(const byte Channel);
+    
+    /*! \brief Extract an enumerated MIDI type from a status byte.
+     
+     This is a utility static method, used internally, made public so you can handle kMIDITypes more easily.
+     */
+    static inline kMIDIType getTypeFromStatusByte(const byte inStatus) 
+    {
+        if ((inStatus < 0x80) 
+            || (inStatus == 0xF4) 
+            || (inStatus == 0xF5) 
+            || (inStatus == 0xF9) 
+            || (inStatus == 0xFD)) return InvalidType; // data bytes and undefined.
+        if (inStatus < 0xF0) return (kMIDIType)(inStatus & 0xF0);   // Channel message, remove channel nibble.
+        else return (kMIDIType)inStatus;
+    }
+    
+    
+#if USE_CALLBACKS
+    
+    void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity));
+    void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity));
+    void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure));
+    void setHandleControlChange(void (*fptr)(byte channel, byte number, byte value));
+    void setHandleProgramChange(void (*fptr)(byte channel, byte number));
+    void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure));
+    void setHandlePitchBend(void (*fptr)(byte channel, int bend));
+    void setHandleSystemExclusive(void (*fptr)(byte * array, byte size));
+    void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
+    void setHandleSongPosition(void (*fptr)(unsigned int beats));
+    void setHandleSongSelect(void (*fptr)(byte songnumber));
+    void setHandleTuneRequest(void (*fptr)(void));
+    void setHandleClock(void (*fptr)(void));
+    void setHandleStart(void (*fptr)(void));
+    void setHandleContinue(void (*fptr)(void));
+    void setHandleStop(void (*fptr)(void));
+    void setHandleActiveSensing(void (*fptr)(void));
+    void setHandleSystemReset(void (*fptr)(void));
+    
+    void disconnectCallbackFromType(kMIDIType Type);
+    
+#endif // USE_CALLBACKS
+    
+    
+private:
+    
+    bool input_filter(byte inChannel);
+    bool parse(byte inChannel);
+    void reset_input_attributes();
+    
+    // Attributes
+    byte            mRunningStatus_RX;
+    byte            mInputChannel;
+    
+    byte            mPendingMessage[MIDI_SYSEX_ARRAY_SIZE];
+    unsigned int    mPendingMessageExpectedLenght;
+    unsigned int    mPendingMessageIndex;                   // Extended to unsigned int for larger sysex payloads.
+    
+    midimsg         mMessage;
+    
+#if USE_CALLBACKS
+    
+    void launchCallback();
+    
+    void (*mNoteOffCallback)(byte channel, byte note, byte velocity);
+    void (*mNoteOnCallback)(byte channel, byte note, byte velocity);
+    void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity);
+    void (*mControlChangeCallback)(byte channel, byte, byte);
+    void (*mProgramChangeCallback)(byte channel, byte);
+    void (*mAfterTouchChannelCallback)(byte channel, byte);
+    void (*mPitchBendCallback)(byte channel, int);
+    void (*mSystemExclusiveCallback)(byte * array, byte size);
+    void (*mTimeCodeQuarterFrameCallback)(byte data);
+    void (*mSongPositionCallback)(unsigned int beats);
+    void (*mSongSelectCallback)(byte songnumber);
+    void (*mTuneRequestCallback)(void);
+    void (*mClockCallback)(void);
+    void (*mStartCallback)(void);
+    void (*mContinueCallback)(void);
+    void (*mStopCallback)(void);
+    void (*mActiveSensingCallback)(void);
+    void (*mSystemResetCallback)(void);
+    
+#endif // USE_CALLBACKS
+    
+    
+#endif // COMPILE_MIDI_IN
+    
+
+/* ####### THRU COMPILATION BLOCK ####### */
+#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
+    
+public:
+    
+    // Getters
+    kThruFilterMode getFilterMode() const { return mThruFilterMode; }
+    bool getThruState() const { return mThruActivated; }
+    
+    
+    // Setters
+    void turnThruOn(kThruFilterMode inThruFilterMode = Full);
+    void turnThruOff();
+    
+    void setThruFilterMode(const kThruFilterMode inThruFilterMode);
+    
+    
+private:
+    
+    void thru_filter(byte inChannel);
+    
+    bool                mThruActivated;
+    kThruFilterMode     mThruFilterMode;
+    
+#endif // Thru
+    
+};
+
+#endif // LIB_MIDI_H_