Arduino MIDI Library Version 3.1.1
/Users/franky/Documents/Dropbox/SVN/embedded/toolbox/libraries/MIDILib/trunk/Arduino/MIDI.cpp
Go to the documentation of this file.
00001 
00011 #include "MIDI.h"
00012 #include <stdlib.h>
00013 #include "WConstants.h" 
00014 #include "HardwareSerial.h"
00015 
00016 
00018 MIDI_Class MIDI;
00019 
00020 
00022 MIDI_Class::MIDI_Class() { 
00023 #if USE_CALLBACKS
00024     // Initialise callbacks to NULL pointer
00025     mNoteOffCallback = NULL;
00026     mNoteOnCallback = NULL;
00027     mAfterTouchPolyCallback = NULL;
00028     mControlChangeCallback = NULL;
00029     mProgramChangeCallback = NULL;
00030     mAfterTouchChannelCallback = NULL;
00031     mPitchBendCallback = NULL;
00032     mSystemExclusiveCallback = NULL;
00033     mTimeCodeQuarterFrameCallback = NULL;
00034     mSongPositionCallback = NULL;
00035     mSongSelectCallback = NULL;
00036     mTuneRequestCallback = NULL;
00037     mClockCallback = NULL;
00038     mStartCallback = NULL;
00039     mContinueCallback = NULL;
00040     mStopCallback = NULL;
00041     mActiveSensingCallback = NULL;
00042     mSystemResetCallback = NULL;
00043 #endif
00044 }
00049 MIDI_Class::~MIDI_Class() { }
00050 
00051 
00058 void MIDI_Class::begin(const byte inChannel) {
00059     
00060     // Initialise the Serial port
00061     USE_SERIAL_PORT.begin(MIDI_BAUDRATE);
00062     
00063     
00064 #if COMPILE_MIDI_OUT
00065     
00066 #if USE_RUNNING_STATUS
00067     mRunningStatus_TX = InvalidType;
00068 #endif // USE_RUNNING_STATUS
00069     
00070 #endif // COMPILE_MIDI_OUT
00071     
00072     
00073 #if COMPILE_MIDI_IN
00074     
00075     mInputChannel = inChannel;
00076     mRunningStatus_RX = InvalidType;
00077     mPendingMessageIndex = 0;
00078     mPendingMessageExpectedLenght = 0;
00079     
00080     mMessage.valid = false;
00081     mMessage.type = InvalidType;
00082     mMessage.channel = 0;
00083     mMessage.data1 = 0;
00084     mMessage.data2 = 0;
00085     
00086 #endif // COMPILE_MIDI_IN
00087     
00088     
00089 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
00090     
00091     mThruFilterMode = Full;
00092     mThruActivated = true;
00093     
00094 #endif // Thru
00095     
00096 }
00097 
00098 
00099 #if COMPILE_MIDI_OUT
00100 
00101 // Private method for generating a status byte from channel and type
00102 const byte MIDI_Class::genstatus(const kMIDIType inType,const byte inChannel) {
00103     return ((byte)inType | ((inChannel-1) & 0x0F));
00104 }
00105 
00114 void MIDI_Class::send(kMIDIType type, byte data1, byte data2, byte channel) {
00115     
00116     // Then test if channel is valid
00117     if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) {
00118         
00119 #if USE_RUNNING_STATUS  
00120         mRunningStatus_TX = InvalidType;
00121 #endif 
00122         
00123         return; // Don't send anything
00124     }
00125     
00126     if (type <= PitchBend) {
00127         // Channel messages
00128         
00129         // Protection: remove MSBs on data
00130         data1 &= 0x7F;
00131         data2 &= 0x7F;
00132         
00133         byte statusbyte = genstatus(type,channel);
00134         
00135 #if USE_RUNNING_STATUS
00136         // Check Running Status
00137         if (mRunningStatus_TX != statusbyte) {
00138             // New message, memorise and send header
00139             mRunningStatus_TX = statusbyte;
00140             USE_SERIAL_PORT.write(mRunningStatus_TX);
00141         }
00142 #else
00143         // Don't care about running status, send the Control byte.
00144         USE_SERIAL_PORT.write(statusbyte);
00145 #endif
00146         
00147         // Then send data
00148         USE_SERIAL_PORT.write(data1);
00149         if (type != ProgramChange && type != AfterTouchChannel) {
00150             USE_SERIAL_PORT.write(data2);
00151         }
00152         return;
00153     }
00154     if (type >= TuneRequest && type <= SystemReset) {
00155         // System Real-time and 1 byte.
00156         sendRealTime(type);
00157     }
00158     
00159 }
00160 
00166 void MIDI_Class::sendNoteOn(byte NoteNumber,byte Velocity,byte Channel) { send(NoteOn,NoteNumber,Velocity,Channel); }
00167 
00173 void MIDI_Class::sendNoteOff(byte NoteNumber,byte Velocity,byte Channel) { send(NoteOff,NoteNumber,Velocity,Channel); }
00174 
00179 void MIDI_Class::sendProgramChange(byte ProgramNumber,byte Channel) { send(ProgramChange,ProgramNumber,0,Channel); }
00180 
00186 void MIDI_Class::sendControlChange(byte ControlNumber, byte ControlValue,byte Channel) { send(ControlChange,ControlNumber,ControlValue,Channel); }
00187 
00193 void MIDI_Class::sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel) { send(AfterTouchPoly,NoteNumber,Pressure,Channel); }
00194 
00199 void MIDI_Class::sendAfterTouch(byte Pressure,byte Channel) { send(AfterTouchChannel,Pressure,0,Channel); }
00200 
00205 void MIDI_Class::sendPitchBend(int PitchValue,byte Channel) {
00206     
00207     unsigned int bend = PitchValue + 8192;
00208     sendPitchBend(bend,Channel);
00209     
00210 }
00215 void MIDI_Class::sendPitchBend(unsigned int PitchValue,byte Channel) {
00216     
00217     send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel);
00218     
00219 }
00224 void MIDI_Class::sendPitchBend(double PitchValue,byte Channel) {
00225     
00226     unsigned int pitchval = (PitchValue+1.f)*8192;
00227     if (pitchval > 16383) pitchval = 16383;     // overflow protection
00228     sendPitchBend(pitchval,Channel);
00229     
00230 }
00231 
00238 void MIDI_Class::sendSysEx(byte length, byte * array, bool ArrayContainsBoundaries) {
00239     if (!ArrayContainsBoundaries) USE_SERIAL_PORT.write(0xF0);
00240     for (byte i=0;i<length;i++) USE_SERIAL_PORT.write(array[i]);
00241     if (!ArrayContainsBoundaries) USE_SERIAL_PORT.write(0xF7);
00242 #if USE_RUNNING_STATUS
00243     mRunningStatus_TX = InvalidType;
00244 #endif
00245 }
00246 
00251 void MIDI_Class::sendTuneRequest() { sendRealTime(TuneRequest); }
00252 
00259 void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) {
00260     
00261     byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
00262     sendTimeCodeQuarterFrame(data);
00263     
00264 }
00265 
00271 void MIDI_Class::sendTimeCodeQuarterFrame(byte data) {
00272     
00273     USE_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
00274     USE_SERIAL_PORT.write(data);
00275 #if USE_RUNNING_STATUS
00276     mRunningStatus_TX = InvalidType;
00277 #endif
00278 }
00279 
00283 void MIDI_Class::sendSongPosition(unsigned int Beats) {
00284     
00285     USE_SERIAL_PORT.write((byte)SongPosition);
00286     USE_SERIAL_PORT.write(Beats & 0x7F);
00287     USE_SERIAL_PORT.write((Beats >> 7) & 0x7F);
00288 #if USE_RUNNING_STATUS
00289     mRunningStatus_TX = InvalidType;
00290 #endif
00291 }
00292 
00294 void MIDI_Class::sendSongSelect(byte SongNumber) {
00295     
00296     USE_SERIAL_PORT.write((byte)SongSelect);
00297     USE_SERIAL_PORT.write(SongNumber & 0x7F);
00298 #if USE_RUNNING_STATUS
00299     mRunningStatus_TX = InvalidType;
00300 #endif
00301 }
00302 
00309 void MIDI_Class::sendRealTime(kMIDIType Type) {
00310     switch (Type) {
00311         case TuneRequest: // Not really real-time, but one byte anyway.
00312         case Clock:
00313         case Start:
00314         case Stop:  
00315         case Continue:
00316         case ActiveSensing:
00317         case SystemReset:
00318             USE_SERIAL_PORT.write((byte)Type);
00319             break;
00320         default:
00321             // Invalid Real Time marker
00322             break;
00323     }
00324     
00325     // Do not cancel Running Status for real-time messages as they can be interleaved within any message.
00326     // Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status.
00327 #if USE_RUNNING_STATUS
00328     if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
00329 #endif
00330     
00331 }
00332 
00333 #endif // COMPILE_MIDI_OUT
00334 
00335 
00336 
00337 #if COMPILE_MIDI_IN
00338 
00345 bool MIDI_Class::read() {
00346     return read(mInputChannel);
00347 }
00348 
00350 bool MIDI_Class::read(const byte inChannel) {
00351     
00352     if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled.
00353     
00354     
00355     if (parse(inChannel)) {
00356         if (input_filter(inChannel)) {
00357             
00358 #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
00359             thru_filter(inChannel);
00360 #endif
00361             
00362 #if USE_CALLBACKS
00363             launchCallback();
00364 #endif
00365             
00366             return true;
00367         }
00368     }
00369     
00370     return false;
00371 }
00372 
00373 // Private method: MIDI parser
00374 bool MIDI_Class::parse(byte inChannel) { 
00375     
00376     // If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
00377     if (USE_SERIAL_PORT.available() == 128) {
00378         USE_SERIAL_PORT.flush();
00379     }   
00380     
00381     if (USE_SERIAL_PORT.available() <= 0) {
00382         // No data available.
00383         return false;
00384     }
00385     else {
00386         
00387         /* Parsing algorithm:
00388          Get a byte from the serial buffer.
00389          * If there is no pending message to be recomposed, start a new one.
00390          - Find type and channel (if pertinent)
00391          - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty.
00392          * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it.
00393          */
00394         
00395         
00396         byte extracted = USE_SERIAL_PORT.read();
00397         
00398         if (mPendingMessageIndex == 0) { // Start a new pending message
00399             mPendingMessage[0] = extracted;
00400             
00401             // Check for running status first
00402             switch (getTypeFromStatusByte(mRunningStatus_RX)) {
00403                     // Only these types allow Running Status:
00404                 case NoteOff:
00405                 case NoteOn:
00406                 case AfterTouchPoly:
00407                 case ControlChange:
00408                 case ProgramChange:
00409                 case AfterTouchChannel:
00410                 case PitchBend: 
00411                     
00412                     // If the status byte is not received, prepend it to the pending message
00413                     if (extracted < 0x80) {
00414                         mPendingMessage[0] = mRunningStatus_RX;
00415                         mPendingMessage[1] = extracted;
00416                         mPendingMessageIndex = 1;
00417                     }
00418                     // Else: well, we received another status byte, so the running status does not apply here.
00419                     // It will be updated upon completion of this message.
00420                     
00421                     break;
00422                     
00423                 default:
00424                     // No running status
00425                     break;
00426             }
00427             
00428             
00429             switch (getTypeFromStatusByte(mPendingMessage[0])) {
00430                     
00431                     // 1 byte messages
00432                 case Start:
00433                 case Continue:
00434                 case Stop:
00435                 case Clock:
00436                 case ActiveSensing:
00437                 case SystemReset:
00438                 case TuneRequest:
00439                     // Handle the message type directly here.
00440                     mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
00441                     mMessage.channel = 0;
00442                     mMessage.data1 = 0;
00443                     mMessage.data2 = 0;
00444                     mMessage.valid = true;
00445                     reset_input_attributes();
00446                     return true;
00447                     break;
00448                     
00449                     // 2 bytes messages
00450                 case ProgramChange:
00451                 case AfterTouchChannel:
00452                 case TimeCodeQuarterFrame:
00453                 case SongSelect:
00454                     mPendingMessageExpectedLenght = 2;
00455                     break;
00456                     
00457                     // 3 bytes messages
00458                 case NoteOn:
00459                 case NoteOff:
00460                 case ControlChange:
00461                 case PitchBend:
00462                 case AfterTouchPoly:
00463                 case SongPosition:
00464                     mPendingMessageExpectedLenght = 3;
00465                     break;
00466                     
00467                 case SystemExclusive:
00468                     mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
00469                     mRunningStatus_RX = InvalidType;
00470                     break;
00471                     
00472                 case InvalidType:
00473                 default:
00474                     // This is obviously wrong. Let's get the hell out'a here.
00475                     reset_input_attributes();
00476                     return false;
00477                     break;
00478             }
00479             
00480             // Then update the index of the pending message.
00481             mPendingMessageIndex++;
00482             
00483             // And call the parser, again.
00484             return parse(inChannel);
00485             
00486         }
00487         else { 
00488             
00489             
00490             // First, test if this is a status byte
00491             if (extracted >= 0x80) {
00492                 
00493                 // Reception of status bytes in the middle of an uncompleted message
00494                 // are allowed only for interleaved Real Time message or EOX
00495                 switch (extracted) {
00496                     case Clock:
00497                     case Start:
00498                     case Continue:
00499                     case Stop:
00500                     case ActiveSensing:
00501                     case SystemReset:
00502                         
00503                         /*
00504                          This is tricky. Here we will have to extract the one-byte message,
00505                          pass it to the structure for being read outside the MIDI class,
00506                          and recompose the message it was interleaved into.
00507                          
00508                          Oh, and without killing the running status.. 
00509                          
00510                          This is done by leaving the pending message as is, it will be completed on next calls.
00511                          */
00512                         
00513                         mMessage.type = (kMIDIType)extracted;
00514                         mMessage.data1 = 0;
00515                         mMessage.data2 = 0;
00516                         mMessage.channel = 0;
00517                         mMessage.valid = true;
00518                         return true;
00519                         
00520                         break;
00521                         
00522                         // End of Exclusive
00523                     case 0xF7:
00524                         if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
00525                             
00526                             // Store System Exclusive array in midimsg structure
00527                             for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) {
00528                                 mMessage.sysex_array[i] = mPendingMessage[i];
00529                             }
00530                             
00531                             mMessage.type = SystemExclusive;
00532                             mMessage.data1 = mPendingMessageIndex+1;    // Get length
00533                             mMessage.data2 = 0;
00534                             mMessage.channel = 0;
00535                             mMessage.valid = true;
00536                             
00537                             reset_input_attributes();
00538                             
00539                             return true;
00540                         }
00541                         else {
00542                             // Well well well.. error.
00543                             reset_input_attributes();
00544                             return false;
00545                         }
00546                         
00547                         break;
00548                     default:
00549                         break;
00550                 }
00551                 
00552                 
00553                 
00554             }
00555             
00556             
00557             // Add extracted data byte to pending message
00558             mPendingMessage[mPendingMessageIndex] = extracted;
00559             
00560             
00561             // Now we are going to check if we have reached the end of the message
00562             if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) {
00563                 
00564                 // "FML" case: fall down here with an overflown SysEx..
00565                 // This means we received the last possible data byte that can fit the buffer.
00566                 // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
00567                 if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
00568                     reset_input_attributes();
00569                     return false;
00570                 }
00571                 
00572                 
00573                 
00574                 
00575                 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
00576                 mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message
00577                 
00578                 mMessage.data1 = mPendingMessage[1];
00579                 
00580                 // Save data2 only if applicable
00581                 if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2];
00582                 else mMessage.data2 = 0;
00583                 
00584                 // Reset local variables
00585                 mPendingMessageIndex = 0;
00586                 mPendingMessageExpectedLenght = 0;
00587                 
00588                 mMessage.valid = true;
00589                 
00590                 // Activate running status (if enabled for the received type)
00591                 switch (mMessage.type) {
00592                     case NoteOff:
00593                     case NoteOn:
00594                     case AfterTouchPoly:
00595                     case ControlChange:
00596                     case ProgramChange:
00597                     case AfterTouchChannel:
00598                     case PitchBend: 
00599                         // Running status enabled: store it from received message
00600                         mRunningStatus_RX = mPendingMessage[0];
00601                         break;
00602                         
00603                     default:
00604                         // No running status
00605                         mRunningStatus_RX = InvalidType;
00606                         break;
00607                 }
00608                 return true;
00609             }
00610             else {
00611                 // Then update the index of the pending message.
00612                 mPendingMessageIndex++;
00613                 
00614                 // And call the parser, again.
00615                 return parse(inChannel);
00616             }
00617             
00618         }
00619         
00620         
00621     }
00622     
00623     // What are our chances to fall here?
00624     return false;
00625 }
00626 
00627 
00628 // Private method: check if the received message is on the listened channel
00629 bool MIDI_Class::input_filter(byte inChannel) {
00630     
00631     
00632     // This method handles recognition of channel (to know if the message is destinated to the Arduino)
00633     
00634     
00635     if (mMessage.type == InvalidType) return false;
00636     
00637     
00638     // First, check if the received message is Channel
00639     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
00640         
00641         // Then we need to know if we listen to it
00642         if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) {
00643             return true;
00644             
00645         }
00646         else {
00647             // We don't listen to this channel
00648             return false;
00649         }
00650         
00651     }
00652     else {
00653         
00654         // System messages are always received
00655         return true;
00656     }
00657     
00658 }
00659 
00660 // Private method: reset input attributes
00661 void MIDI_Class::reset_input_attributes() {
00662     mPendingMessageIndex = 0;
00663     mPendingMessageExpectedLenght = 0;
00664     mRunningStatus_RX = InvalidType;
00665 }
00666 
00667 // Getters
00672 kMIDIType MIDI_Class::getType() { return mMessage.type; }
00673 
00678 byte MIDI_Class::getChannel() { return mMessage.channel; }
00679 
00684 byte MIDI_Class::getData1() { return mMessage.data1; }
00685 
00687 byte MIDI_Class::getData2() { return mMessage.data2; }
00688 
00693 byte * MIDI_Class::getSysExArray() { return mMessage.sysex_array; }
00694 
00696 bool MIDI_Class::check() { return mMessage.valid; }
00697 
00698 // Setters
00703 void MIDI_Class::setInputChannel(const byte Channel) { mInputChannel = Channel; }
00704 
00705 
00706 #if USE_CALLBACKS
00707 
00708 void MIDI_Class::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity))         { mNoteOffCallback = fptr; }
00709 void MIDI_Class::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity))          { mNoteOnCallback = fptr; }
00710 void MIDI_Class::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure))  { mAfterTouchPolyCallback = fptr; }
00711 void MIDI_Class::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value))    { mControlChangeCallback = fptr; }
00712 void MIDI_Class::setHandleProgramChange(void (*fptr)(byte channel, byte number))                { mProgramChangeCallback = fptr; }
00713 void MIDI_Class::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure))          { mAfterTouchChannelCallback = fptr; }
00714 void MIDI_Class::setHandlePitchBend(void (*fptr)(byte channel, int bend))                       { mPitchBendCallback = fptr; }
00715 void MIDI_Class::setHandleSystemExclusive(void (*fptr)(byte * array, byte size))                { mSystemExclusiveCallback = fptr; }
00716 void MIDI_Class::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data))                         { mTimeCodeQuarterFrameCallback = fptr; }
00717 void MIDI_Class::setHandleSongPosition(void (*fptr)(unsigned int beats))                        { mSongPositionCallback = fptr; }
00718 void MIDI_Class::setHandleSongSelect(void (*fptr)(byte songnumber))                             { mSongSelectCallback = fptr; }
00719 void MIDI_Class::setHandleTuneRequest(void (*fptr)(void))                                       { mTuneRequestCallback = fptr; }
00720 void MIDI_Class::setHandleClock(void (*fptr)(void))                                             { mClockCallback = fptr; }
00721 void MIDI_Class::setHandleStart(void (*fptr)(void))                                             { mStartCallback = fptr; }
00722 void MIDI_Class::setHandleContinue(void (*fptr)(void))                                          { mContinueCallback = fptr; }
00723 void MIDI_Class::setHandleStop(void (*fptr)(void))                                              { mStopCallback = fptr; }
00724 void MIDI_Class::setHandleActiveSensing(void (*fptr)(void))                                     { mActiveSensingCallback = fptr; }
00725 void MIDI_Class::setHandleSystemReset(void (*fptr)(void))                                       { mSystemResetCallback = fptr; }
00726 
00727 
00733 void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) {
00734     
00735     switch (Type) {
00736         case NoteOff:               mNoteOffCallback = NULL;                break;
00737         case NoteOn:                mNoteOnCallback = NULL;                 break;
00738         case AfterTouchPoly:        mAfterTouchPolyCallback = NULL;         break;
00739         case ControlChange:         mControlChangeCallback = NULL;          break;
00740         case ProgramChange:         mProgramChangeCallback = NULL;          break;
00741         case AfterTouchChannel:     mAfterTouchChannelCallback = NULL;      break;
00742         case PitchBend:             mPitchBendCallback = NULL;              break;
00743         case SystemExclusive:       mSystemExclusiveCallback = NULL;        break;
00744         case TimeCodeQuarterFrame:  mTimeCodeQuarterFrameCallback = NULL;   break;
00745         case SongPosition:          mSongPositionCallback = NULL;           break;
00746         case SongSelect:            mSongSelectCallback = NULL;             break;
00747         case TuneRequest:           mTuneRequestCallback = NULL;            break;
00748         case Clock:                 mClockCallback = NULL;                  break;
00749         case Start:                 mStartCallback = NULL;                  break;
00750         case Continue:              mContinueCallback = NULL;               break;
00751         case Stop:                  mStopCallback = NULL;                   break;
00752         case ActiveSensing:         mActiveSensingCallback = NULL;          break;
00753         case SystemReset:           mSystemResetCallback = NULL;            break;
00754         default:
00755             break;
00756     }
00757     
00758 }
00759 
00760 // Private - launch callback function based on received type.
00761 void MIDI_Class::launchCallback() {
00762     
00763     // The order is mixed to allow frequent messages to trigger their callback faster.
00764     
00765     switch (mMessage.type) {
00766             // Notes
00767         case NoteOff:               if (mNoteOffCallback != NULL)               mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2);   break;
00768         case NoteOn:                if (mNoteOnCallback != NULL)                mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
00769             
00770             // Real-time messages
00771         case Clock:                 if (mClockCallback != NULL)                 mClockCallback();           break;          
00772         case Start:                 if (mStartCallback != NULL)                 mStartCallback();           break;
00773         case Continue:              if (mContinueCallback != NULL)              mContinueCallback();        break;
00774         case Stop:                  if (mStopCallback != NULL)                  mStopCallback();            break;
00775         case ActiveSensing:         if (mActiveSensingCallback != NULL)         mActiveSensingCallback();   break;
00776             
00777             // Continuous controllers
00778         case ControlChange:         if (mControlChangeCallback != NULL)         mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
00779         case PitchBend:             if (mPitchBendCallback != NULL)             mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) - 8192); break; // TODO: check this
00780         case AfterTouchPoly:        if (mAfterTouchPolyCallback != NULL)        mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
00781         case AfterTouchChannel:     if (mAfterTouchChannelCallback != NULL)     mAfterTouchChannelCallback(mMessage.channel,mMessage.data1);    break;
00782             
00783         case ProgramChange:         if (mProgramChangeCallback != NULL)         mProgramChangeCallback(mMessage.channel,mMessage.data1);    break;
00784         case SystemExclusive:       if (mSystemExclusiveCallback != NULL)       mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1);  break;
00785             
00786             // Occasional messages
00787         case TimeCodeQuarterFrame:  if (mTimeCodeQuarterFrameCallback != NULL)  mTimeCodeQuarterFrameCallback(mMessage.data1);  break;
00788         case SongPosition:          if (mSongPositionCallback != NULL)          mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break;
00789         case SongSelect:            if (mSongSelectCallback != NULL)            mSongSelectCallback(mMessage.data1);    break;
00790         case TuneRequest:           if (mTuneRequestCallback != NULL)           mTuneRequestCallback(); break;
00791             
00792         case SystemReset:           if (mSystemResetCallback != NULL)           mSystemResetCallback(); break;
00793         case InvalidType:
00794         default:
00795             break;
00796     }
00797     
00798 }
00799 
00800 
00801 #endif // USE_CALLBACKS
00802 
00803 
00804 #endif // COMPILE_MIDI_IN
00805 
00806 
00807 
00808 
00809 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
00810 
00816 void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) { 
00817     mThruFilterMode = inThruFilterMode;
00818     if (mThruFilterMode != Off) mThruActivated = true;
00819     else mThruActivated = false;
00820 }
00821 
00822 
00824 void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode) { 
00825     mThruActivated = true;
00826     mThruFilterMode = inThruFilterMode;
00827 }
00829 void MIDI_Class::turnThruOff() {
00830     mThruActivated = false; 
00831     mThruFilterMode = Off;
00832 }
00833 
00834 // This method is called upon reception of a message and takes care of Thru filtering and sending.
00835 void MIDI_Class::thru_filter(byte inChannel) {
00836     
00837     /*
00838      This method handles Soft-Thru filtering.
00839      
00840      Soft-Thru filtering:
00841      - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off
00842      - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting
00843      
00844      */
00845     
00846     // If the feature is disabled, don't do anything.
00847     if (!mThruActivated || (mThruFilterMode == Off)) return;
00848     
00849     
00850     // First, check if the received message is Channel
00851     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
00852         
00853         
00854         bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI));
00855         
00856         // Now let's pass it to the output
00857         switch (mThruFilterMode) {
00858             case Full:
00859                 send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
00860                 return;
00861                 break;
00862             case SameChannel:
00863                 if (filter_condition) {
00864                     send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
00865                     return;
00866                 }
00867                 break;
00868             case DifferentChannel:
00869                 if (!filter_condition) {
00870                     send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
00871                     return;
00872                 }
00873             case Off:
00874                 // Do nothing. 
00875                 // Technically it's impossible to get there because the case was already tested earlier.
00876                 break;
00877             default:
00878                 break;
00879         }
00880         
00881     }
00882     else {
00883         
00884         // Send the message to the output
00885         switch (mMessage.type) {
00886                 // Real Time and 1 byte
00887             case Clock:
00888             case Start:
00889             case Stop:
00890             case Continue:
00891             case ActiveSensing:
00892             case SystemReset:
00893             case TuneRequest:   
00894                 sendRealTime(mMessage.type);
00895                 return;
00896                 break;
00897                 
00898             case SystemExclusive:
00899                 // Send SysEx (0xF0 and 0xF7 are included in the buffer)
00900                 sendSysEx(mMessage.data1,mMessage.sysex_array,true); 
00901                 return;
00902                 break;
00903                 
00904             case SongSelect:
00905                 sendSongSelect(mMessage.data1);
00906                 return;
00907                 break;
00908                 
00909             case SongPosition:
00910                 sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));
00911                 return;
00912                 break;
00913                 
00914             case TimeCodeQuarterFrame:
00915                 sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
00916                 return;
00917                 break;
00918             default:
00919                 break;
00920         }
00921         
00922         
00923     }
00924     
00925 }
00926 
00927 
00928 #endif // Thru
00929 
00930