Arduino MIDI Library Version 3.2

- Main Page
- Classes
- Files

- File List
- File Members

/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