Arduino MIDI Library Version 3.2
|
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