Arduino MIDI Library Version 3.2
/Users/franky/Documents/Dropbox/SVN/embedded/toolbox/libraries/MIDILib/trunk/Arduino/MIDI/MIDI.cpp
Go to the documentation of this file.
00001 
00011 #include "MIDI.h"
00012 #include <stdlib.h>
00013 #include "Arduino.h"            // If using an old (pre-1.0) version of Arduino, use WConstants.h instead of Arduino.h
00014 #include "HardwareSerial.h"
00015 
00016 
00018 MIDI_Class MIDI;
00019 
00020 
00022 MIDI_Class::MIDI_Class()
00023 { 
00024     
00025 #if USE_CALLBACKS
00026     
00027     // Initialise callbacks to NULL pointer
00028     mNoteOffCallback                = NULL;
00029     mNoteOnCallback                 = NULL;
00030     mAfterTouchPolyCallback         = NULL;
00031     mControlChangeCallback          = NULL;
00032     mProgramChangeCallback          = NULL;
00033     mAfterTouchChannelCallback      = NULL;
00034     mPitchBendCallback              = NULL;
00035     mSystemExclusiveCallback        = NULL;
00036     mTimeCodeQuarterFrameCallback   = NULL;
00037     mSongPositionCallback           = NULL;
00038     mSongSelectCallback             = NULL;
00039     mTuneRequestCallback            = NULL;
00040     mClockCallback                  = NULL;
00041     mStartCallback                  = NULL;
00042     mContinueCallback               = NULL;
00043     mStopCallback                   = NULL;
00044     mActiveSensingCallback          = NULL;
00045     mSystemResetCallback            = NULL;
00046     
00047 #endif
00048     
00049 }
00050 
00051 
00056 MIDI_Class::~MIDI_Class()
00057 {
00058 
00059 }
00060 
00061 
00068 void MIDI_Class::begin(const byte inChannel)
00069 {
00070     
00071     // Initialise the Serial port
00072     USE_SERIAL_PORT.begin(MIDI_BAUDRATE);
00073     
00074     
00075 #if COMPILE_MIDI_OUT
00076     
00077 #if USE_RUNNING_STATUS
00078     
00079     mRunningStatus_TX = InvalidType;
00080     
00081 #endif // USE_RUNNING_STATUS
00082     
00083 #endif // COMPILE_MIDI_OUT
00084     
00085     
00086 #if COMPILE_MIDI_IN
00087     
00088     mInputChannel = inChannel;
00089     mRunningStatus_RX = InvalidType;
00090     mPendingMessageIndex = 0;
00091     mPendingMessageExpectedLenght = 0;
00092     
00093     mMessage.valid = false;
00094     mMessage.type = InvalidType;
00095     mMessage.channel = 0;
00096     mMessage.data1 = 0;
00097     mMessage.data2 = 0;
00098     
00099 #endif // COMPILE_MIDI_IN
00100     
00101     
00102 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
00103     
00104     mThruFilterMode = Full;
00105     mThruActivated = true;
00106     
00107 #endif // Thru
00108     
00109 }
00110 
00111 
00112 #if COMPILE_MIDI_OUT
00113 
00114 // Private method for generating a status byte from channel and type
00115 const byte MIDI_Class::genstatus(const kMIDIType inType,
00116                                  const byte inChannel) const
00117 {
00118     
00119     return ((byte)inType | ((inChannel-1) & 0x0F));
00120     
00121 }
00122 
00123 
00132 void MIDI_Class::send(kMIDIType type,
00133                       byte data1,
00134                       byte data2,
00135                       byte channel)
00136 {
00137     
00138     // Then test if channel is valid
00139     if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) {
00140         
00141 #if USE_RUNNING_STATUS  
00142         mRunningStatus_TX = InvalidType;
00143 #endif 
00144         
00145         return; // Don't send anything
00146     }
00147     
00148     if (type <= PitchBend) {
00149         // Channel messages
00150         
00151         // Protection: remove MSBs on data
00152         data1 &= 0x7F;
00153         data2 &= 0x7F;
00154         
00155         byte statusbyte = genstatus(type,channel);
00156         
00157 #if USE_RUNNING_STATUS
00158         // Check Running Status
00159         if (mRunningStatus_TX != statusbyte) {
00160             // New message, memorise and send header
00161             mRunningStatus_TX = statusbyte;
00162             USE_SERIAL_PORT.write(mRunningStatus_TX);
00163         }
00164 #else
00165         // Don't care about running status, send the Control byte.
00166         USE_SERIAL_PORT.write(statusbyte);
00167 #endif
00168         
00169         // Then send data
00170         USE_SERIAL_PORT.write(data1);
00171         if (type != ProgramChange && type != AfterTouchChannel) {
00172             USE_SERIAL_PORT.write(data2);
00173         }
00174         return;
00175     }
00176     if (type >= TuneRequest && type <= SystemReset) {
00177         // System Real-time and 1 byte.
00178         sendRealTime(type);
00179     }
00180     
00181 }
00182 
00183 
00189 void MIDI_Class::sendNoteOn(byte NoteNumber,
00190                             byte Velocity,
00191                             byte Channel)
00192 { 
00193     
00194     send(NoteOn,NoteNumber,Velocity,Channel);
00195 
00196 }
00197 
00198 
00204 void MIDI_Class::sendNoteOff(byte NoteNumber,
00205                              byte Velocity,
00206                              byte Channel)
00207 {
00208     
00209     send(NoteOff,NoteNumber,Velocity,Channel);
00210 
00211 }
00212 
00213 
00218 void MIDI_Class::sendProgramChange(byte ProgramNumber,
00219                                    byte Channel)
00220 {
00221     
00222     send(ProgramChange,ProgramNumber,0,Channel);
00223 
00224 }
00225 
00226 
00232 void MIDI_Class::sendControlChange(byte ControlNumber,
00233                                    byte ControlValue,
00234                                    byte Channel)
00235 {
00236     
00237     send(ControlChange,ControlNumber,ControlValue,Channel);
00238 
00239 }
00240 
00241 
00247 void MIDI_Class::sendPolyPressure(byte NoteNumber,
00248                                   byte Pressure,
00249                                   byte Channel)
00250 {
00251     
00252     send(AfterTouchPoly,NoteNumber,Pressure,Channel);
00253 
00254 }
00255 
00256 
00261 void MIDI_Class::sendAfterTouch(byte Pressure,
00262                                 byte Channel)
00263 {
00264     
00265     send(AfterTouchChannel,Pressure,0,Channel);
00266 
00267 }
00268 
00269 
00274 void MIDI_Class::sendPitchBend(int PitchValue,
00275                                byte Channel)
00276 {
00277     
00278     unsigned int bend = PitchValue + 8192;
00279     sendPitchBend(bend,Channel);
00280     
00281 }
00282 
00283 
00288 void MIDI_Class::sendPitchBend(unsigned int PitchValue,
00289                                byte Channel)
00290 {
00291     
00292     send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel);
00293     
00294 }
00295 
00296 
00301 void MIDI_Class::sendPitchBend(double PitchValue,
00302                                byte Channel)
00303 {
00304     
00305     unsigned int pitchval = (PitchValue+1.f)*8192;
00306     if (pitchval > 16383) pitchval = 16383;     // overflow protection
00307     sendPitchBend(pitchval,Channel);
00308     
00309 }
00310 
00311 
00318 void MIDI_Class::sendSysEx(int length,
00319                            const byte *const array,
00320                            bool ArrayContainsBoundaries)
00321 {
00322     
00323     if (ArrayContainsBoundaries == false) {
00324         
00325         USE_SERIAL_PORT.write(0xF0);
00326         
00327         for (int i=0;i<length;++i) {
00328             
00329             USE_SERIAL_PORT.write(array[i]);
00330             
00331         }
00332         
00333         USE_SERIAL_PORT.write(0xF7);
00334         
00335     }
00336     else {
00337         
00338         for (int i=0;i<length;++i) {
00339             
00340             USE_SERIAL_PORT.write(array[i]);
00341             
00342         }
00343         
00344     }
00345     
00346 #if USE_RUNNING_STATUS
00347     mRunningStatus_TX = InvalidType;
00348 #endif
00349     
00350 }
00351 
00352 
00357 void MIDI_Class::sendTuneRequest()
00358 {
00359     
00360     sendRealTime(TuneRequest);
00361 
00362 }
00363 
00364 
00371 void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble)
00372 {
00373     
00374     byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
00375     sendTimeCodeQuarterFrame(data);
00376     
00377 }
00378 
00379 
00385 void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
00386 {
00387     
00388     USE_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
00389     USE_SERIAL_PORT.write(data);
00390 
00391 #if USE_RUNNING_STATUS
00392     mRunningStatus_TX = InvalidType;
00393 #endif
00394     
00395 }
00396 
00397 
00401 void MIDI_Class::sendSongPosition(unsigned int Beats)
00402 {
00403     
00404     USE_SERIAL_PORT.write((byte)SongPosition);
00405     USE_SERIAL_PORT.write(Beats & 0x7F);
00406     USE_SERIAL_PORT.write((Beats >> 7) & 0x7F);
00407 
00408 #if USE_RUNNING_STATUS
00409     mRunningStatus_TX = InvalidType;
00410 #endif
00411     
00412 }
00413 
00414 
00416 void MIDI_Class::sendSongSelect(byte SongNumber)
00417 {
00418     
00419     USE_SERIAL_PORT.write((byte)SongSelect);
00420     USE_SERIAL_PORT.write(SongNumber & 0x7F);
00421 
00422 #if USE_RUNNING_STATUS
00423     mRunningStatus_TX = InvalidType;
00424 #endif
00425     
00426 }
00427 
00428 
00435 void MIDI_Class::sendRealTime(kMIDIType Type)
00436 {
00437     switch (Type) {
00438         case TuneRequest: // Not really real-time, but one byte anyway.
00439         case Clock:
00440         case Start:
00441         case Stop:  
00442         case Continue:
00443         case ActiveSensing:
00444         case SystemReset:
00445             USE_SERIAL_PORT.write((byte)Type);
00446             break;
00447         default:
00448             // Invalid Real Time marker
00449             break;
00450     }
00451     
00452     // Do not cancel Running Status for real-time messages as they can be interleaved within any message.
00453     // Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status.
00454 #if USE_RUNNING_STATUS
00455     if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
00456 #endif
00457     
00458 }
00459 
00460 #endif // COMPILE_MIDI_OUT
00461 
00462 
00463 
00464 #if COMPILE_MIDI_IN
00465 
00472 bool MIDI_Class::read()
00473 {
00474     
00475     return read(mInputChannel);
00476     
00477 }
00478 
00479 
00481 bool MIDI_Class::read(const byte inChannel)
00482 {
00483     
00484     if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled.
00485     
00486     if (parse(inChannel)) {
00487         
00488         if (input_filter(inChannel)) {
00489             
00490 #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
00491             thru_filter(inChannel);
00492 #endif
00493             
00494 #if USE_CALLBACKS
00495             launchCallback();
00496 #endif
00497             
00498             return true;
00499         }
00500         
00501     }
00502     
00503     return false;
00504     
00505 }
00506 
00507 
00508 // Private method: MIDI parser
00509 bool MIDI_Class::parse(byte inChannel)
00510 { 
00511     
00512     const int bytes_available = USE_SERIAL_PORT.available();
00513     
00514     if (bytes_available <= 0) {
00515         // No data available.
00516         return false;
00517     }
00518     
00519     // If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
00520     if (bytes_available == 128) {
00521         USE_SERIAL_PORT.flush();
00522     }   
00523     else {
00524         
00525         /* Parsing algorithm:
00526          Get a byte from the serial buffer.
00527          * If there is no pending message to be recomposed, start a new one.
00528          - Find type and channel (if pertinent)
00529          - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty.
00530          * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it.
00531          */
00532         
00533         
00534         const byte extracted = USE_SERIAL_PORT.read();
00535         
00536         if (mPendingMessageIndex == 0) { // Start a new pending message
00537             mPendingMessage[0] = extracted;
00538             
00539             // Check for running status first
00540             switch (getTypeFromStatusByte(mRunningStatus_RX)) {
00541                     // Only these types allow Running Status:
00542                 case NoteOff:
00543                 case NoteOn:
00544                 case AfterTouchPoly:
00545                 case ControlChange:
00546                 case ProgramChange:
00547                 case AfterTouchChannel:
00548                 case PitchBend: 
00549                     
00550                     // If the status byte is not received, prepend it to the pending message
00551                     if (extracted < 0x80) {
00552                         mPendingMessage[0] = mRunningStatus_RX;
00553                         mPendingMessage[1] = extracted;
00554                         mPendingMessageIndex = 1;
00555                     }
00556                     // Else: well, we received another status byte, so the running status does not apply here.
00557                     // It will be updated upon completion of this message.
00558                     
00559                     break;
00560                     
00561                 default:
00562                     // No running status
00563                     break;
00564             }
00565             
00566             
00567             switch (getTypeFromStatusByte(mPendingMessage[0])) {
00568                     
00569                     // 1 byte messages
00570                 case Start:
00571                 case Continue:
00572                 case Stop:
00573                 case Clock:
00574                 case ActiveSensing:
00575                 case SystemReset:
00576                 case TuneRequest:
00577                     // Handle the message type directly here.
00578                     mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
00579                     mMessage.channel = 0;
00580                     mMessage.data1 = 0;
00581                     mMessage.data2 = 0;
00582                     mMessage.valid = true;
00583                     
00584                     // \fix Running Status broken when receiving Clock messages.
00585                     // Do not reset all input attributes, Running Status must remain unchanged.
00586                     //reset_input_attributes(); 
00587                     
00588                     // We still need to reset these
00589                     mPendingMessageIndex = 0;
00590                     mPendingMessageExpectedLenght = 0;
00591                     
00592                     return true;
00593                     break;
00594                     
00595                     // 2 bytes messages
00596                 case ProgramChange:
00597                 case AfterTouchChannel:
00598                 case TimeCodeQuarterFrame:
00599                 case SongSelect:
00600                     mPendingMessageExpectedLenght = 2;
00601                     break;
00602                     
00603                     // 3 bytes messages
00604                 case NoteOn:
00605                 case NoteOff:
00606                 case ControlChange:
00607                 case PitchBend:
00608                 case AfterTouchPoly:
00609                 case SongPosition:
00610                     mPendingMessageExpectedLenght = 3;
00611                     break;
00612                     
00613                 case SystemExclusive:
00614                     mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
00615                     mRunningStatus_RX = InvalidType;
00616                     break;
00617                     
00618                 case InvalidType:
00619                 default:
00620                     // This is obviously wrong. Let's get the hell out'a here.
00621                     reset_input_attributes();
00622                     return false;
00623                     break;
00624             }
00625             
00626             // Then update the index of the pending message.
00627             mPendingMessageIndex++;
00628             
00629 #if USE_1BYTE_PARSING
00630             // Message is not complete.
00631             return false;
00632 #else
00633             // Call the parser recursively
00634             // to parse the rest of the message.
00635             return parse(inChannel);
00636 #endif
00637             
00638         }
00639         else { 
00640             
00641             // First, test if this is a status byte
00642             if (extracted >= 0x80) {
00643                 
00644                 // Reception of status bytes in the middle of an uncompleted message
00645                 // are allowed only for interleaved Real Time message or EOX
00646                 switch (extracted) {
00647                     case Clock:
00648                     case Start:
00649                     case Continue:
00650                     case Stop:
00651                     case ActiveSensing:
00652                     case SystemReset:
00653                         
00654                         /*
00655                          This is tricky. Here we will have to extract the one-byte message,
00656                          pass it to the structure for being read outside the MIDI class,
00657                          and recompose the message it was interleaved into.
00658                          
00659                          Oh, and without killing the running status.. 
00660                          
00661                          This is done by leaving the pending message as is, it will be completed on next calls.
00662                          */
00663                         
00664                         mMessage.type = (kMIDIType)extracted;
00665                         mMessage.data1 = 0;
00666                         mMessage.data2 = 0;
00667                         mMessage.channel = 0;
00668                         mMessage.valid = true;
00669                         return true;
00670                         
00671                         break;
00672                         
00673                         // End of Exclusive
00674                     case 0xF7:
00675                         if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
00676                             
00677                             // Store System Exclusive array in midimsg structure
00678                             for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) {
00679                                 mMessage.sysex_array[i] = mPendingMessage[i];
00680                             }
00681                             
00682                             mMessage.type = SystemExclusive;
00683 
00684                             // Get length
00685                             mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;   
00686                             mMessage.data2 = (mPendingMessageIndex+1) >> 8;
00687                             
00688                             mMessage.channel = 0;
00689                             mMessage.valid = true;
00690                             
00691                             reset_input_attributes();
00692                             
00693                             return true;
00694                         }
00695                         else {
00696                             // Well well well.. error.
00697                             reset_input_attributes();
00698                             return false;
00699                         }
00700                         
00701                         break;
00702                     default:
00703                         break;
00704                 }
00705                 
00706                 
00707                 
00708             }
00709             
00710             
00711             // Add extracted data byte to pending message
00712             mPendingMessage[mPendingMessageIndex] = extracted;
00713             
00714             
00715             // Now we are going to check if we have reached the end of the message
00716             if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) {
00717                 
00718                 // "FML" case: fall down here with an overflown SysEx..
00719                 // This means we received the last possible data byte that can fit the buffer.
00720                 // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
00721                 if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
00722                     reset_input_attributes();
00723                     return false;
00724                 }
00725                 
00726                 
00727                 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
00728                 mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message
00729                 
00730                 mMessage.data1 = mPendingMessage[1];
00731                 
00732                 // Save data2 only if applicable
00733                 if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2];
00734                 else mMessage.data2 = 0;
00735                 
00736                 // Reset local variables
00737                 mPendingMessageIndex = 0;
00738                 mPendingMessageExpectedLenght = 0;
00739                 
00740                 mMessage.valid = true;
00741                 
00742                 // Activate running status (if enabled for the received type)
00743                 switch (mMessage.type) {
00744                     case NoteOff:
00745                     case NoteOn:
00746                     case AfterTouchPoly:
00747                     case ControlChange:
00748                     case ProgramChange:
00749                     case AfterTouchChannel:
00750                     case PitchBend: 
00751                         // Running status enabled: store it from received message
00752                         mRunningStatus_RX = mPendingMessage[0];
00753                         break;
00754                         
00755                     default:
00756                         // No running status
00757                         mRunningStatus_RX = InvalidType;
00758                         break;
00759                 }
00760                 return true;
00761             }
00762             else {
00763                 // Then update the index of the pending message.
00764                 mPendingMessageIndex++;
00765                 
00766 #if USE_1BYTE_PARSING
00767                 // Message is not complete.
00768                 return false;
00769 #else
00770                 // Call the parser recursively
00771                 // to parse the rest of the message.
00772                 return parse(inChannel);
00773 #endif
00774                 
00775             }
00776             
00777         }
00778         
00779     }
00780     
00781     // What are our chances to fall here?
00782     return false;
00783 }
00784 
00785 
00786 // Private method: check if the received message is on the listened channel
00787 bool MIDI_Class::input_filter(byte inChannel)
00788 {
00789     
00790     
00791     // This method handles recognition of channel (to know if the message is destinated to the Arduino)
00792     
00793     
00794     if (mMessage.type == InvalidType) return false;
00795     
00796     
00797     // First, check if the received message is Channel
00798     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
00799         
00800         // Then we need to know if we listen to it
00801         if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) {
00802             return true;
00803             
00804         }
00805         else {
00806             // We don't listen to this channel
00807             return false;
00808         }
00809         
00810     }
00811     else {
00812         
00813         // System messages are always received
00814         return true;
00815     }
00816     
00817 }
00818 
00819 
00820 // Private method: reset input attributes
00821 void MIDI_Class::reset_input_attributes()
00822 {
00823     
00824     mPendingMessageIndex = 0;
00825     mPendingMessageExpectedLenght = 0;
00826     mRunningStatus_RX = InvalidType;
00827     
00828 }
00829 
00830 
00831 // Getters
00836 kMIDIType MIDI_Class::getType() const
00837 {
00838     
00839     return mMessage.type;
00840 
00841 }
00842 
00843 
00848 byte MIDI_Class::getChannel() const
00849 {
00850     
00851     return mMessage.channel;
00852 
00853 }
00854 
00855 
00857 byte MIDI_Class::getData1() const
00858 {
00859     
00860     return mMessage.data1;
00861 
00862 }
00863 
00864 
00866 byte MIDI_Class::getData2() const
00867 { 
00868     
00869     return mMessage.data2;
00870 
00871 }
00872 
00873 
00878 const byte * MIDI_Class::getSysExArray() const
00879 { 
00880     
00881     return mMessage.sysex_array;
00882 
00883 }
00884 
00890 unsigned int MIDI_Class::getSysExArrayLength() const
00891 {
00892     
00893     unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1;
00894     
00895     return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size;
00896     
00897 }
00898 
00899 
00901 bool MIDI_Class::check() const
00902 { 
00903     
00904     return mMessage.valid;
00905 
00906 }
00907 
00908 
00909 // Setters
00914 void MIDI_Class::setInputChannel(const byte Channel)
00915 { 
00916     
00917     mInputChannel = Channel;
00918     
00919 }
00920 
00921 
00922 #if USE_CALLBACKS
00923 
00924 void MIDI_Class::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity))         { mNoteOffCallback = fptr; }
00925 void MIDI_Class::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity))          { mNoteOnCallback = fptr; }
00926 void MIDI_Class::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure))  { mAfterTouchPolyCallback = fptr; }
00927 void MIDI_Class::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value))    { mControlChangeCallback = fptr; }
00928 void MIDI_Class::setHandleProgramChange(void (*fptr)(byte channel, byte number))                { mProgramChangeCallback = fptr; }
00929 void MIDI_Class::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure))          { mAfterTouchChannelCallback = fptr; }
00930 void MIDI_Class::setHandlePitchBend(void (*fptr)(byte channel, int bend))                       { mPitchBendCallback = fptr; }
00931 void MIDI_Class::setHandleSystemExclusive(void (*fptr)(byte * array, byte size))                { mSystemExclusiveCallback = fptr; }
00932 void MIDI_Class::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data))                         { mTimeCodeQuarterFrameCallback = fptr; }
00933 void MIDI_Class::setHandleSongPosition(void (*fptr)(unsigned int beats))                        { mSongPositionCallback = fptr; }
00934 void MIDI_Class::setHandleSongSelect(void (*fptr)(byte songnumber))                             { mSongSelectCallback = fptr; }
00935 void MIDI_Class::setHandleTuneRequest(void (*fptr)(void))                                       { mTuneRequestCallback = fptr; }
00936 void MIDI_Class::setHandleClock(void (*fptr)(void))                                             { mClockCallback = fptr; }
00937 void MIDI_Class::setHandleStart(void (*fptr)(void))                                             { mStartCallback = fptr; }
00938 void MIDI_Class::setHandleContinue(void (*fptr)(void))                                          { mContinueCallback = fptr; }
00939 void MIDI_Class::setHandleStop(void (*fptr)(void))                                              { mStopCallback = fptr; }
00940 void MIDI_Class::setHandleActiveSensing(void (*fptr)(void))                                     { mActiveSensingCallback = fptr; }
00941 void MIDI_Class::setHandleSystemReset(void (*fptr)(void))                                       { mSystemResetCallback = fptr; }
00942 
00943 
00949 void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
00950 {
00951     
00952     switch (Type) {
00953         case NoteOff:               mNoteOffCallback = NULL;                break;
00954         case NoteOn:                mNoteOnCallback = NULL;                 break;
00955         case AfterTouchPoly:        mAfterTouchPolyCallback = NULL;         break;
00956         case ControlChange:         mControlChangeCallback = NULL;          break;
00957         case ProgramChange:         mProgramChangeCallback = NULL;          break;
00958         case AfterTouchChannel:     mAfterTouchChannelCallback = NULL;      break;
00959         case PitchBend:             mPitchBendCallback = NULL;              break;
00960         case SystemExclusive:       mSystemExclusiveCallback = NULL;        break;
00961         case TimeCodeQuarterFrame:  mTimeCodeQuarterFrameCallback = NULL;   break;
00962         case SongPosition:          mSongPositionCallback = NULL;           break;
00963         case SongSelect:            mSongSelectCallback = NULL;             break;
00964         case TuneRequest:           mTuneRequestCallback = NULL;            break;
00965         case Clock:                 mClockCallback = NULL;                  break;
00966         case Start:                 mStartCallback = NULL;                  break;
00967         case Continue:              mContinueCallback = NULL;               break;
00968         case Stop:                  mStopCallback = NULL;                   break;
00969         case ActiveSensing:         mActiveSensingCallback = NULL;          break;
00970         case SystemReset:           mSystemResetCallback = NULL;            break;
00971         default:
00972             break;
00973     }
00974     
00975 }
00976 
00977 
00978 // Private - launch callback function based on received type.
00979 void MIDI_Class::launchCallback()
00980 {
00981     
00982     // The order is mixed to allow frequent messages to trigger their callback faster.
00983     
00984     switch (mMessage.type) {
00985             // Notes
00986         case NoteOff:               if (mNoteOffCallback != NULL)               mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2);   break;
00987         case NoteOn:                if (mNoteOnCallback != NULL)                mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
00988             
00989             // Real-time messages
00990         case Clock:                 if (mClockCallback != NULL)                 mClockCallback();           break;          
00991         case Start:                 if (mStartCallback != NULL)                 mStartCallback();           break;
00992         case Continue:              if (mContinueCallback != NULL)              mContinueCallback();        break;
00993         case Stop:                  if (mStopCallback != NULL)                  mStopCallback();            break;
00994         case ActiveSensing:         if (mActiveSensingCallback != NULL)         mActiveSensingCallback();   break;
00995             
00996             // Continuous controllers
00997         case ControlChange:         if (mControlChangeCallback != NULL)         mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
00998         case PitchBend:             if (mPitchBendCallback != NULL)             mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) - 8192); break; // TODO: check this
00999         case AfterTouchPoly:        if (mAfterTouchPolyCallback != NULL)        mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2);    break;
01000         case AfterTouchChannel:     if (mAfterTouchChannelCallback != NULL)     mAfterTouchChannelCallback(mMessage.channel,mMessage.data1);    break;
01001             
01002         case ProgramChange:         if (mProgramChangeCallback != NULL)         mProgramChangeCallback(mMessage.channel,mMessage.data1);    break;
01003         case SystemExclusive:       if (mSystemExclusiveCallback != NULL)       mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1);  break;
01004             
01005             // Occasional messages
01006         case TimeCodeQuarterFrame:  if (mTimeCodeQuarterFrameCallback != NULL)  mTimeCodeQuarterFrameCallback(mMessage.data1);  break;
01007         case SongPosition:          if (mSongPositionCallback != NULL)          mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break;
01008         case SongSelect:            if (mSongSelectCallback != NULL)            mSongSelectCallback(mMessage.data1);    break;
01009         case TuneRequest:           if (mTuneRequestCallback != NULL)           mTuneRequestCallback(); break;
01010             
01011         case SystemReset:           if (mSystemResetCallback != NULL)           mSystemResetCallback(); break;
01012         case InvalidType:
01013         default:
01014             break;
01015     }
01016     
01017 }
01018 
01019 
01020 #endif // USE_CALLBACKS
01021 
01022 
01023 #endif // COMPILE_MIDI_IN
01024 
01025 
01026 
01027 
01028 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru
01029 
01035 void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode)
01036 { 
01037     
01038     mThruFilterMode = inThruFilterMode;
01039     if (mThruFilterMode != Off) mThruActivated = true;
01040     else mThruActivated = false;
01041     
01042 }
01043 
01044 
01046 void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode)
01047 { 
01048     
01049     mThruActivated = true;
01050     mThruFilterMode = inThruFilterMode;
01051     
01052 }
01053 
01054 
01056 void MIDI_Class::turnThruOff()
01057 {
01058     
01059     mThruActivated = false; 
01060     mThruFilterMode = Off;
01061     
01062 }
01063 
01064 
01065 // This method is called upon reception of a message and takes care of Thru filtering and sending.
01066 void MIDI_Class::thru_filter(byte inChannel)
01067 {
01068     
01069     /*
01070      This method handles Soft-Thru filtering.
01071      
01072      Soft-Thru filtering:
01073      - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off
01074      - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting
01075      
01076      */
01077     
01078     // If the feature is disabled, don't do anything.
01079     if (!mThruActivated || (mThruFilterMode == Off)) return;
01080     
01081     
01082     // First, check if the received message is Channel
01083     if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
01084         
01085         const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI));
01086         
01087         // Now let's pass it to the output
01088         switch (mThruFilterMode) {
01089             case Full:
01090                 send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
01091                 return;
01092                 break;
01093             case SameChannel:
01094                 if (filter_condition) {
01095                     send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
01096                     return;
01097                 }
01098                 break;
01099             case DifferentChannel:
01100                 if (!filter_condition) {
01101                     send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
01102                     return;
01103                 }
01104                 break;
01105             case Off:
01106                 // Do nothing. 
01107                 // Technically it's impossible to get there because the case was already tested earlier.
01108                 break;
01109             default:
01110                 break;
01111         }
01112         
01113     }
01114     else {
01115         
01116         // Send the message to the output
01117         switch (mMessage.type) {
01118                 // Real Time and 1 byte
01119             case Clock:
01120             case Start:
01121             case Stop:
01122             case Continue:
01123             case ActiveSensing:
01124             case SystemReset:
01125             case TuneRequest:   
01126                 sendRealTime(mMessage.type);
01127                 return;
01128                 break;
01129                 
01130             case SystemExclusive:
01131                 // Send SysEx (0xF0 and 0xF7 are included in the buffer)
01132                 sendSysEx(mMessage.data1,mMessage.sysex_array,true); 
01133                 return;
01134                 break;
01135                 
01136             case SongSelect:
01137                 sendSongSelect(mMessage.data1);
01138                 return;
01139                 break;
01140                 
01141             case SongPosition:
01142                 sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));
01143                 return;
01144                 break;
01145                 
01146             case TimeCodeQuarterFrame:
01147                 sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
01148                 return;
01149                 break;
01150             default:
01151                 break;
01152                 
01153         }
01154         
01155     }
01156     
01157 }
01158 
01159 
01160 #endif // Thru
01161 
01162