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