Ported from Arduino MIDI library Orignal: http://www.arduino.cc/playground/Main/MIDILibrary use Serial (UART)

Dependents:   MIDI_sample MIDI_usb_bridge MIDI_Interpreter midi-timer ... more

Revision:
0:713ef38fead1
--- /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