Ported from Arduino MIDI library Orignal: http://www.arduino.cc/playground/Main/MIDILibrary use Serial (UART)
Dependents: MIDI_sample MIDI_usb_bridge MIDI_Interpreter midi-timer ... more
MIDI.cpp
00001 /*! 00002 * @file MIDI.cpp 00003 * Project MIDI Library 00004 * @brief MIDI Library for the Arduino 00005 * @version 3.2 00006 * @author Francois Best 00007 * @date 24/02/11 00008 * license GPL Forty Seven Effects - 2011 00009 */ 00010 /* 00011 * Ported for mbed by Hiroshi Suga 00012 * Orignal: http://www.arduino.cc/playground/Main/MIDILibrary 00013 */ 00014 00015 #include "MIDI.h" 00016 #include "mbed.h" 00017 00018 00019 00020 /*! \brief Default constructor for MIDI. */ 00021 MIDI::MIDI(PinName p_tx, PinName p_rx) : _midi(p_tx, p_rx) 00022 { 00023 00024 #if USE_CALLBACKS 00025 00026 // Initialise callbacks to NULL pointer 00027 mNoteOffCallback = NULL; 00028 mNoteOnCallback = NULL; 00029 mAfterTouchPolyCallback = NULL; 00030 mControlChangeCallback = NULL; 00031 mProgramChangeCallback = NULL; 00032 mAfterTouchChannelCallback = NULL; 00033 mPitchBendCallback = NULL; 00034 mSystemExclusiveCallback = NULL; 00035 mTimeCodeQuarterFrameCallback = NULL; 00036 mSongPositionCallback = NULL; 00037 mSongSelectCallback = NULL; 00038 mTuneRequestCallback = NULL; 00039 mClockCallback = NULL; 00040 mStartCallback = NULL; 00041 mContinueCallback = NULL; 00042 mStopCallback = NULL; 00043 mActiveSensingCallback = NULL; 00044 mSystemResetCallback = NULL; 00045 00046 #endif 00047 00048 } 00049 00050 00051 /*! \brief Default destructor for MIDI. 00052 00053 This is not really useful for the Arduino, as it is never called... 00054 */ 00055 MIDI::~MIDI() 00056 { 00057 00058 } 00059 00060 00061 /*! \brief Call the begin method in the setup() function of the Arduino. 00062 00063 All parameters are set to their default values: 00064 - Input channel set to 1 if no value is specified 00065 - Full thru mirroring 00066 */ 00067 void MIDI::begin(const byte inChannel) 00068 { 00069 00070 // Initialise the Serial port 00071 USE_SERIAL_PORT.baud(MIDI_BAUDRATE); 00072 00073 00074 #if COMPILE_MIDI_OUT 00075 00076 #if USE_RUNNING_STATUS 00077 00078 mRunningStatus_TX = InvalidType; 00079 00080 #endif // USE_RUNNING_STATUS 00081 00082 #endif // COMPILE_MIDI_OUT 00083 00084 00085 #if COMPILE_MIDI_IN 00086 00087 mInputChannel = inChannel; 00088 mRunningStatus_RX = InvalidType; 00089 mPendingMessageIndex = 0; 00090 mPendingMessageExpectedLenght = 0; 00091 00092 mMessage.valid = false; 00093 mMessage.type = InvalidType; 00094 mMessage.channel = 0; 00095 mMessage.data1 = 0; 00096 mMessage.data2 = 0; 00097 00098 #endif // COMPILE_MIDI_IN 00099 00100 00101 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru 00102 00103 mThruFilterMode = Full; 00104 mThruActivated = true; 00105 00106 #endif // Thru 00107 00108 } 00109 00110 00111 #if COMPILE_MIDI_OUT 00112 00113 // Private method for generating a status byte from channel and type 00114 byte MIDI::genstatus(const kMIDIType inType, 00115 const byte inChannel) const 00116 { 00117 00118 return ((byte )inType | ((inChannel-1) & 0x0F)); 00119 00120 } 00121 00122 00123 /*! \brief Generate and send a MIDI message from the values given. 00124 \param type The message type (see type defines for reference) 00125 \param data1 The first data byte. 00126 \param data2 The second data byte (if the message contains only 1 data byte, set this one to 0). 00127 \param channel The output channel on which the message will be sent (values from 1 to 16). Note: you cannot send to OMNI. 00128 00129 This is an internal method, use it only if you need to send raw data from your code, at your own risks. 00130 */ 00131 void MIDI::send(kMIDIType type, 00132 byte data1, 00133 byte data2, 00134 byte channel) 00135 { 00136 00137 // Then test if channel is valid 00138 if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) { 00139 00140 #if USE_RUNNING_STATUS 00141 mRunningStatus_TX = InvalidType; 00142 #endif 00143 00144 return; // Don't send anything 00145 } 00146 00147 if (type <= PitchBend) { 00148 // Channel messages 00149 00150 // Protection: remove MSBs on data 00151 data1 &= 0x7F; 00152 data2 &= 0x7F; 00153 00154 byte statusbyte = genstatus(type,channel); 00155 00156 #if USE_RUNNING_STATUS 00157 // Check Running Status 00158 if (mRunningStatus_TX != statusbyte) { 00159 // New message, memorise and send header 00160 mRunningStatus_TX = statusbyte; 00161 USE_SERIAL_PORT.putc(mRunningStatus_TX); 00162 } 00163 #else 00164 // Don't care about running status, send the Control byte. 00165 USE_SERIAL_PORT.putc(statusbyte); 00166 #endif 00167 00168 // Then send data 00169 USE_SERIAL_PORT.putc(data1); 00170 if (type != ProgramChange && type != AfterTouchChannel) { 00171 USE_SERIAL_PORT.putc(data2); 00172 } 00173 return; 00174 } 00175 if (type >= TuneRequest && type <= SystemReset) { 00176 // System Real-time and 1 byte. 00177 sendRealTime(type); 00178 } 00179 00180 } 00181 00182 00183 /*! \brief Send a Note On message 00184 \param NoteNumber Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n 00185 \param Velocity Note attack velocity (0 to 127). A NoteOn with 0 velocity is considered as a NoteOff. 00186 \param Channel The channel on which the message will be sent (1 to 16). 00187 */ 00188 void MIDI::sendNoteOn(byte NoteNumber, 00189 byte Velocity, 00190 byte Channel) 00191 { 00192 00193 send(NoteOn,NoteNumber,Velocity,Channel); 00194 00195 } 00196 00197 00198 /*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity) 00199 \param NoteNumber Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n 00200 \param Velocity Release velocity (0 to 127). 00201 \param Channel The channel on which the message will be sent (1 to 16). 00202 */ 00203 void MIDI::sendNoteOff(byte NoteNumber, 00204 byte Velocity, 00205 byte Channel) 00206 { 00207 00208 send(NoteOff,NoteNumber,Velocity,Channel); 00209 00210 } 00211 00212 00213 /*! \brief Send a Program Change message 00214 \param ProgramNumber The Program to select (0 to 127). 00215 \param Channel The channel on which the message will be sent (1 to 16). 00216 */ 00217 void MIDI::sendProgramChange(byte ProgramNumber, 00218 byte Channel) 00219 { 00220 00221 send(ProgramChange,ProgramNumber,0,Channel); 00222 00223 } 00224 00225 00226 /*! \brief Send a Control Change message 00227 \param ControlNumber The controller number (0 to 127). See the detailed description here: http://www.somascape.org/midi/tech/spec.html#ctrlnums 00228 \param ControlValue The value for the specified controller (0 to 127). 00229 \param Channel The channel on which the message will be sent (1 to 16). 00230 */ 00231 void MIDI::sendControlChange(byte ControlNumber, 00232 byte ControlValue, 00233 byte Channel) 00234 { 00235 00236 send(ControlChange,ControlNumber,ControlValue,Channel); 00237 00238 } 00239 00240 00241 /*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note) 00242 \param NoteNumber The note to apply AfterTouch to (0 to 127). 00243 \param Pressure The amount of AfterTouch to apply (0 to 127). 00244 \param Channel The channel on which the message will be sent (1 to 16). 00245 */ 00246 void MIDI::sendPolyPressure(byte NoteNumber, 00247 byte Pressure, 00248 byte Channel) 00249 { 00250 00251 send(AfterTouchPoly,NoteNumber,Pressure,Channel); 00252 00253 } 00254 00255 00256 /*! \brief Send a MonoPhonic AfterTouch message (applies to all notes) 00257 \param Pressure The amount of AfterTouch to apply to all notes. 00258 \param Channel The channel on which the message will be sent (1 to 16). 00259 */ 00260 void MIDI::sendAfterTouch(byte Pressure, 00261 byte Channel) 00262 { 00263 00264 send(AfterTouchChannel,Pressure,0,Channel); 00265 00266 } 00267 00268 00269 /*! \brief Send a Pitch Bend message using a signed integer value. 00270 \param PitchValue The amount of bend to send (in a signed integer format), between -8192 (maximum downwards bend) and 8191 (max upwards bend), center value is 0. 00271 \param Channel The channel on which the message will be sent (1 to 16). 00272 */ 00273 void MIDI::sendPitchBend(int PitchValue, 00274 byte Channel) 00275 { 00276 00277 unsigned int bend = PitchValue + 8192; 00278 sendPitchBend(bend,Channel); 00279 00280 } 00281 00282 00283 /*! \brief Send a Pitch Bend message using an unsigned integer value. 00284 \param PitchValue The amount of bend to send (in a signed integer format), between 0 (maximum downwards bend) and 16383 (max upwards bend), center value is 8192. 00285 \param Channel The channel on which the message will be sent (1 to 16). 00286 */ 00287 void MIDI::sendPitchBend(unsigned int PitchValue, 00288 byte Channel) 00289 { 00290 00291 send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel); 00292 00293 } 00294 00295 00296 /*! \brief Send a Pitch Bend message using a floating point value. 00297 \param PitchValue The amount of bend to send (in a floating point format), between -1.0f (maximum downwards bend) and +1.0f (max upwards bend), center value is 0.0f. 00298 \param Channel The channel on which the message will be sent (1 to 16). 00299 */ 00300 void MIDI::sendPitchBend(double PitchValue, 00301 byte Channel) 00302 { 00303 00304 unsigned int pitchval = (PitchValue+1.f)*8192; 00305 if (pitchval > 16383) pitchval = 16383; // overflow protection 00306 sendPitchBend(pitchval,Channel); 00307 00308 } 00309 00310 00311 /*! \brief Generate and send a System Exclusive frame. 00312 \param length The size of the array to send 00313 \param array The byte array containing the data to send 00314 \param ArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes (start & stop SysEx) will NOT be sent (and therefore must be included in the array). 00315 default value is set to 'false' for compatibility with previous versions of the library. 00316 */ 00317 void MIDI::sendSysEx(int length, 00318 const byte *const array, 00319 bool ArrayContainsBoundaries) 00320 { 00321 00322 if (ArrayContainsBoundaries == false) { 00323 00324 USE_SERIAL_PORT.putc(0xF0); 00325 00326 for (int i=0;i<length;++i) { 00327 00328 USE_SERIAL_PORT.putc(array[i]); 00329 00330 } 00331 00332 USE_SERIAL_PORT.putc(0xF7); 00333 00334 } 00335 else { 00336 00337 for (int i=0;i<length;++i) { 00338 00339 USE_SERIAL_PORT.putc(array[i]); 00340 00341 } 00342 00343 } 00344 00345 #if USE_RUNNING_STATUS 00346 mRunningStatus_TX = InvalidType; 00347 #endif 00348 00349 } 00350 00351 00352 /*! \brief Send a Tune Request message. 00353 00354 When a MIDI unit receives this message, it should tune its oscillators (if equipped with any) 00355 */ 00356 void MIDI::sendTuneRequest() 00357 { 00358 00359 sendRealTime(TuneRequest); 00360 00361 } 00362 00363 00364 /*! \brief Send a MIDI Time Code Quarter Frame. 00365 00366 See MIDI Specification for more information. 00367 \param TypeNibble MTC type 00368 \param ValuesNibble MTC data 00369 */ 00370 void MIDI::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) 00371 { 00372 00373 byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) ); 00374 sendTimeCodeQuarterFrame(data); 00375 00376 } 00377 00378 00379 /*! \brief Send a MIDI Time Code Quarter Frame. 00380 00381 See MIDI Specification for more information. 00382 \param data if you want to encode directly the nibbles in your program, you can send the byte here. 00383 */ 00384 void MIDI::sendTimeCodeQuarterFrame(byte data) 00385 { 00386 00387 USE_SERIAL_PORT.putc((byte )TimeCodeQuarterFrame); 00388 USE_SERIAL_PORT.putc(data); 00389 00390 #if USE_RUNNING_STATUS 00391 mRunningStatus_TX = InvalidType; 00392 #endif 00393 00394 } 00395 00396 00397 /*! \brief Send a Song Position Pointer message. 00398 \param Beats The number of beats since the start of the song. 00399 */ 00400 void MIDI::sendSongPosition(unsigned int Beats) 00401 { 00402 00403 USE_SERIAL_PORT.putc((byte )SongPosition); 00404 USE_SERIAL_PORT.putc(Beats & 0x7F); 00405 USE_SERIAL_PORT.putc((Beats >> 7) & 0x7F); 00406 00407 #if USE_RUNNING_STATUS 00408 mRunningStatus_TX = InvalidType; 00409 #endif 00410 00411 } 00412 00413 00414 /*! \brief Send a Song Select message */ 00415 void MIDI::sendSongSelect(byte SongNumber) 00416 { 00417 00418 USE_SERIAL_PORT.putc((byte )SongSelect); 00419 USE_SERIAL_PORT.putc(SongNumber & 0x7F); 00420 00421 #if USE_RUNNING_STATUS 00422 mRunningStatus_TX = InvalidType; 00423 #endif 00424 00425 } 00426 00427 00428 /*! \brief Send a Real Time (one byte) message. 00429 00430 \param Type The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset. 00431 You can also send a Tune Request with this method. 00432 @see kMIDIType 00433 */ 00434 void MIDI::sendRealTime(kMIDIType Type) 00435 { 00436 switch (Type) { 00437 case TuneRequest: // Not really real-time, but one byte anyway. 00438 case Clock: 00439 case Start: 00440 case Stop: 00441 case Continue: 00442 case ActiveSensing: 00443 case SystemReset: 00444 USE_SERIAL_PORT.putc((byte )Type); 00445 break; 00446 default: 00447 // Invalid Real Time marker 00448 break; 00449 } 00450 00451 // Do not cancel Running Status for real-time messages as they can be interleaved within any message. 00452 // Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status. 00453 #if USE_RUNNING_STATUS 00454 if (Type == TuneRequest) mRunningStatus_TX = InvalidType; 00455 #endif 00456 00457 } 00458 00459 #endif // COMPILE_MIDI_OUT 00460 00461 00462 00463 #if COMPILE_MIDI_IN 00464 00465 /*! \brief Read a MIDI message from the serial port using the main input channel (see setInputChannel() for reference). 00466 00467 Returned value: true if any valid message has been stored in the structure, false if not. 00468 A valid message is a message that matches the input channel. \n\n 00469 If the Thru is enabled and the messages matches the filter, it is sent back on the MIDI output. 00470 */ 00471 bool MIDI::read() 00472 { 00473 00474 return read(mInputChannel); 00475 00476 } 00477 00478 00479 /*! \brief Reading/thru-ing method, the same as read() with a given input channel to read on. */ 00480 bool MIDI::read(const byte inChannel) 00481 { 00482 00483 if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. 00484 00485 if (parse(inChannel)) { 00486 00487 if (input_filter(inChannel)) { 00488 00489 #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) 00490 thru_filter(inChannel); 00491 #endif 00492 00493 #if USE_CALLBACKS 00494 launchCallback(); 00495 #endif 00496 00497 return true; 00498 } 00499 00500 } 00501 00502 return false; 00503 00504 } 00505 00506 00507 // Private method: MIDI parser 00508 bool MIDI::parse(byte inChannel) 00509 { 00510 00511 const int bytes_available = USE_SERIAL_PORT.readable(); 00512 00513 if (bytes_available <= 0) { 00514 // No data available. 00515 return false; 00516 } 00517 00518 // If the buffer is full -> Don't Panic! Call the Vogons to destroy it. 00519 if (bytes_available == 128) { 00520 // USE_SERIAL_PORT.flush(); 00521 } 00522 else { 00523 00524 /* Parsing algorithm: 00525 Get a byte from the serial buffer. 00526 * If there is no pending message to be recomposed, start a new one. 00527 - Find type and channel (if pertinent) 00528 - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty. 00529 * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it. 00530 */ 00531 00532 00533 const byte extracted = USE_SERIAL_PORT.getc(); 00534 00535 if (mPendingMessageIndex == 0) { // Start a new pending message 00536 mPendingMessage[0] = extracted; 00537 00538 // Check for running status first 00539 switch (getTypeFromStatusByte(mRunningStatus_RX)) { 00540 // Only these types allow Running Status: 00541 case NoteOff: 00542 case NoteOn: 00543 case AfterTouchPoly: 00544 case ControlChange: 00545 case ProgramChange: 00546 case AfterTouchChannel: 00547 case PitchBend: 00548 00549 // If the status byte is not received, prepend it to the pending message 00550 if (extracted < 0x80) { 00551 mPendingMessage[0] = mRunningStatus_RX; 00552 mPendingMessage[1] = extracted; 00553 mPendingMessageIndex = 1; 00554 } 00555 // Else: well, we received another status byte, so the running status does not apply here. 00556 // It will be updated upon completion of this message. 00557 00558 break; 00559 00560 default: 00561 // No running status 00562 break; 00563 } 00564 00565 00566 switch (getTypeFromStatusByte(mPendingMessage[0])) { 00567 00568 // 1 byte messages 00569 case Start: 00570 case Continue: 00571 case Stop: 00572 case Clock: 00573 case ActiveSensing: 00574 case SystemReset: 00575 case TuneRequest: 00576 // Handle the message type directly here. 00577 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); 00578 mMessage.channel = 0; 00579 mMessage.data1 = 0; 00580 mMessage.data2 = 0; 00581 mMessage.valid = true; 00582 00583 // \fix Running Status broken when receiving Clock messages. 00584 // Do not reset all input attributes, Running Status must remain unchanged. 00585 //reset_input_attributes(); 00586 00587 // We still need to reset these 00588 mPendingMessageIndex = 0; 00589 mPendingMessageExpectedLenght = 0; 00590 00591 return true; 00592 00593 // 2 bytes messages 00594 case ProgramChange: 00595 case AfterTouchChannel: 00596 case TimeCodeQuarterFrame: 00597 case SongSelect: 00598 mPendingMessageExpectedLenght = 2; 00599 break; 00600 00601 // 3 bytes messages 00602 case NoteOn: 00603 case NoteOff: 00604 case ControlChange: 00605 case PitchBend: 00606 case AfterTouchPoly: 00607 case SongPosition: 00608 mPendingMessageExpectedLenght = 3; 00609 break; 00610 00611 case SystemExclusive: 00612 mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes 00613 mRunningStatus_RX = InvalidType; 00614 break; 00615 00616 case InvalidType: 00617 default: 00618 // This is obviously wrong. Let's get the hell out'a here. 00619 reset_input_attributes(); 00620 return false; 00621 } 00622 00623 // Then update the index of the pending message. 00624 mPendingMessageIndex++; 00625 00626 #if USE_1BYTE_PARSING 00627 // Message is not complete. 00628 return false; 00629 #else 00630 // Call the parser recursively 00631 // to parse the rest of the message. 00632 return parse(inChannel); 00633 #endif 00634 00635 } 00636 else { 00637 00638 // First, test if this is a status byte 00639 if (extracted >= 0x80) { 00640 00641 // Reception of status bytes in the middle of an uncompleted message 00642 // are allowed only for interleaved Real Time message or EOX 00643 switch (extracted) { 00644 case Clock: 00645 case Start: 00646 case Continue: 00647 case Stop: 00648 case ActiveSensing: 00649 case SystemReset: 00650 00651 /* 00652 This is tricky. Here we will have to extract the one-byte message, 00653 pass it to the structure for being read outside the MIDI class, 00654 and recompose the message it was interleaved into. 00655 00656 Oh, and without killing the running status.. 00657 00658 This is done by leaving the pending message as is, it will be completed on next calls. 00659 */ 00660 00661 mMessage.type = (kMIDIType )extracted; 00662 mMessage.data1 = 0; 00663 mMessage.data2 = 0; 00664 mMessage.channel = 0; 00665 mMessage.valid = true; 00666 return true; 00667 00668 // End of Exclusive 00669 case 0xF7: 00670 if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { 00671 00672 // Store System Exclusive array in midimsg structure 00673 for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) { 00674 mMessage.sysex_array [i] = mPendingMessage[i]; 00675 } 00676 00677 mMessage.type = SystemExclusive; 00678 00679 // Get length 00680 mMessage.data1 = (mPendingMessageIndex+1) & 0xFF; 00681 mMessage.data2 = (mPendingMessageIndex+1) >> 8; 00682 00683 mMessage.channel = 0; 00684 mMessage.valid = true; 00685 00686 reset_input_attributes(); 00687 00688 return true; 00689 } 00690 else { 00691 // Well well well.. error. 00692 reset_input_attributes(); 00693 return false; 00694 } 00695 00696 default: 00697 break; 00698 } 00699 00700 00701 00702 } 00703 00704 00705 // Add extracted data byte to pending message 00706 mPendingMessage[mPendingMessageIndex] = extracted; 00707 00708 00709 // Now we are going to check if we have reached the end of the message 00710 if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { 00711 00712 // "FML" case: fall down here with an overflown SysEx.. 00713 // This means we received the last possible data byte that can fit the buffer. 00714 // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. 00715 if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { 00716 reset_input_attributes(); 00717 return false; 00718 } 00719 00720 00721 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); 00722 mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message 00723 00724 mMessage.data1 = mPendingMessage[1]; 00725 00726 // Save data2 only if applicable 00727 if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; 00728 else mMessage.data2 = 0; 00729 00730 // Reset local variables 00731 mPendingMessageIndex = 0; 00732 mPendingMessageExpectedLenght = 0; 00733 00734 mMessage.valid = true; 00735 00736 // Activate running status (if enabled for the received type) 00737 switch (mMessage.type ) { 00738 case NoteOff: 00739 case NoteOn: 00740 case AfterTouchPoly: 00741 case ControlChange: 00742 case ProgramChange: 00743 case AfterTouchChannel: 00744 case PitchBend: 00745 // Running status enabled: store it from received message 00746 mRunningStatus_RX = mPendingMessage[0]; 00747 break; 00748 00749 default: 00750 // No running status 00751 mRunningStatus_RX = InvalidType; 00752 break; 00753 } 00754 return true; 00755 } 00756 else { 00757 // Then update the index of the pending message. 00758 mPendingMessageIndex++; 00759 00760 #if USE_1BYTE_PARSING 00761 // Message is not complete. 00762 return false; 00763 #else 00764 // Call the parser recursively 00765 // to parse the rest of the message. 00766 return parse(inChannel); 00767 #endif 00768 00769 } 00770 00771 } 00772 00773 } 00774 00775 // What are our chances to fall here? 00776 return false; 00777 } 00778 00779 00780 // Private method: check if the received message is on the listened channel 00781 bool MIDI::input_filter(byte inChannel) 00782 { 00783 00784 00785 // This method handles recognition of channel (to know if the message is destinated to the Arduino) 00786 00787 00788 if (mMessage.type == InvalidType) return false; 00789 00790 00791 // First, check if the received message is Channel 00792 if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { 00793 00794 // Then we need to know if we listen to it 00795 if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { 00796 return true; 00797 00798 } 00799 else { 00800 // We don't listen to this channel 00801 return false; 00802 } 00803 00804 } 00805 else { 00806 00807 // System messages are always received 00808 return true; 00809 } 00810 00811 } 00812 00813 00814 // Private method: reset input attributes 00815 void MIDI::reset_input_attributes() 00816 { 00817 00818 mPendingMessageIndex = 0; 00819 mPendingMessageExpectedLenght = 0; 00820 mRunningStatus_RX = InvalidType; 00821 00822 } 00823 00824 00825 // Getters 00826 /*! \brief Get the last received message's type 00827 00828 Returns an enumerated type. @see kMIDIType 00829 */ 00830 kMIDIType MIDI::getType() const 00831 { 00832 00833 return mMessage.type ; 00834 00835 } 00836 00837 00838 /*! \brief Get the channel of the message stored in the structure. 00839 00840 Channel range is 1 to 16. For non-channel messages, this will return 0. 00841 */ 00842 byte MIDI::getChannel() const 00843 { 00844 00845 return mMessage.channel ; 00846 00847 } 00848 00849 00850 /*! \brief Get the first data byte of the last received message. */ 00851 byte MIDI::getData1() const 00852 { 00853 00854 return mMessage.data1 ; 00855 00856 } 00857 00858 00859 /*! \brief Get the second data byte of the last received message. */ 00860 byte MIDI::getData2() const 00861 { 00862 00863 return mMessage.data2 ; 00864 00865 } 00866 00867 00868 /*! \brief Get the System Exclusive byte array. 00869 00870 @see getSysExArrayLength to get the array's length in bytes. 00871 */ 00872 const byte * MIDI::getSysExArray() const 00873 { 00874 00875 return mMessage.sysex_array ; 00876 00877 } 00878 00879 /*! \brief Get the lenght of the System Exclusive array. 00880 00881 It is coded using data1 as LSB and data2 as MSB. 00882 \return The array's length, in bytes. 00883 */ 00884 unsigned int MIDI::getSysExArrayLength() const 00885 { 00886 00887 unsigned int coded_size = ((unsigned int)(mMessage.data2 ) << 8) | mMessage.data1 ; 00888 00889 return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size; 00890 00891 } 00892 00893 00894 /*! \brief Check if a valid message is stored in the structure. */ 00895 bool MIDI::check() const 00896 { 00897 00898 return mMessage.valid ; 00899 00900 } 00901 00902 00903 // Setters 00904 /*! \brief Set the value for the input MIDI channel 00905 \param Channel the channel value. Valid values are 1 to 16, 00906 MIDI_CHANNEL_OMNI if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable MIDI input. 00907 */ 00908 void MIDI::setInputChannel(const byte Channel) 00909 { 00910 00911 mInputChannel = Channel; 00912 00913 } 00914 00915 00916 #if USE_CALLBACKS 00917 00918 void MIDI::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; } 00919 void MIDI::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; } 00920 void MIDI::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; } 00921 void MIDI::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; } 00922 void MIDI::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } 00923 void MIDI::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } 00924 void MIDI::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } 00925 void MIDI::setHandleSystemExclusive(void (*fptr)(byte * array, byte size)) { mSystemExclusiveCallback = fptr; } 00926 void MIDI::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } 00927 void MIDI::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; } 00928 void MIDI::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } 00929 void MIDI::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } 00930 void MIDI::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } 00931 void MIDI::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; } 00932 void MIDI::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; } 00933 void MIDI::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; } 00934 void MIDI::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; } 00935 void MIDI::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } 00936 00937 00938 /*! \brief Detach an external function from the given type. 00939 00940 Use this method to cancel the effects of setHandle********. 00941 \param Type The type of message to unbind. When a message of this type is received, no function will be called. 00942 */ 00943 void MIDI::disconnectCallbackFromType(kMIDIType Type) 00944 { 00945 00946 switch (Type) { 00947 case NoteOff: mNoteOffCallback = NULL; break; 00948 case NoteOn: mNoteOnCallback = NULL; break; 00949 case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break; 00950 case ControlChange: mControlChangeCallback = NULL; break; 00951 case ProgramChange: mProgramChangeCallback = NULL; break; 00952 case AfterTouchChannel: mAfterTouchChannelCallback = NULL; break; 00953 case PitchBend: mPitchBendCallback = NULL; break; 00954 case SystemExclusive: mSystemExclusiveCallback = NULL; break; 00955 case TimeCodeQuarterFrame: mTimeCodeQuarterFrameCallback = NULL; break; 00956 case SongPosition: mSongPositionCallback = NULL; break; 00957 case SongSelect: mSongSelectCallback = NULL; break; 00958 case TuneRequest: mTuneRequestCallback = NULL; break; 00959 case Clock: mClockCallback = NULL; break; 00960 case Start: mStartCallback = NULL; break; 00961 case Continue: mContinueCallback = NULL; break; 00962 case Stop: mStopCallback = NULL; break; 00963 case ActiveSensing: mActiveSensingCallback = NULL; break; 00964 case SystemReset: mSystemResetCallback = NULL; break; 00965 default: 00966 break; 00967 } 00968 00969 } 00970 00971 00972 // Private - launch callback function based on received type. 00973 void MIDI::launchCallback() 00974 { 00975 00976 // The order is mixed to allow frequent messages to trigger their callback faster. 00977 00978 switch (mMessage.type ) { 00979 // Notes 00980 case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel ,mMessage.data1 ,mMessage.data2 ); break; 00981 case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel ,mMessage.data1 ,mMessage.data2 ); break; 00982 00983 // Real-time messages 00984 case Clock: if (mClockCallback != NULL) mClockCallback(); break; 00985 case Start: if (mStartCallback != NULL) mStartCallback(); break; 00986 case Continue: if (mContinueCallback != NULL) mContinueCallback(); break; 00987 case Stop: if (mStopCallback != NULL) mStopCallback(); break; 00988 case ActiveSensing: if (mActiveSensingCallback != NULL) mActiveSensingCallback(); break; 00989 00990 // Continuous controllers 00991 case ControlChange: if (mControlChangeCallback != NULL) mControlChangeCallback(mMessage.channel ,mMessage.data1 ,mMessage.data2 ); break; 00992 case PitchBend: if (mPitchBendCallback != NULL) mPitchBendCallback(mMessage.channel ,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) - 8192); break; // TODO: check this 00993 case AfterTouchPoly: if (mAfterTouchPolyCallback != NULL) mAfterTouchPolyCallback(mMessage.channel ,mMessage.data1 ,mMessage.data2 ); break; 00994 case AfterTouchChannel: if (mAfterTouchChannelCallback != NULL) mAfterTouchChannelCallback(mMessage.channel ,mMessage.data1 ); break; 00995 00996 case ProgramChange: if (mProgramChangeCallback != NULL) mProgramChangeCallback(mMessage.channel ,mMessage.data1 ); break; 00997 case SystemExclusive: if (mSystemExclusiveCallback != NULL) mSystemExclusiveCallback(mMessage.sysex_array ,mMessage.data1 ); break; 00998 00999 // Occasional messages 01000 case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != NULL) mTimeCodeQuarterFrameCallback(mMessage.data1 ); break; 01001 case SongPosition: if (mSongPositionCallback != NULL) mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break; 01002 case SongSelect: if (mSongSelectCallback != NULL) mSongSelectCallback(mMessage.data1 ); break; 01003 case TuneRequest: if (mTuneRequestCallback != NULL) mTuneRequestCallback(); break; 01004 01005 case SystemReset: if (mSystemResetCallback != NULL) mSystemResetCallback(); break; 01006 case InvalidType: 01007 default: 01008 break; 01009 } 01010 01011 } 01012 01013 01014 #endif // USE_CALLBACKS 01015 01016 01017 #endif // COMPILE_MIDI_IN 01018 01019 01020 01021 01022 #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru 01023 01024 /*! \brief Set the filter for thru mirroring 01025 \param inThruFilterMode a filter mode 01026 01027 @see kThruFilterMode 01028 */ 01029 void MIDI::setThruFilterMode(kThruFilterMode inThruFilterMode) 01030 { 01031 01032 mThruFilterMode = inThruFilterMode; 01033 if (mThruFilterMode != Off) mThruActivated = true; 01034 else mThruActivated = false; 01035 01036 } 01037 01038 01039 /*! \brief Setter method: turn message mirroring on. */ 01040 void MIDI::turnThruOn(kThruFilterMode inThruFilterMode) 01041 { 01042 01043 mThruActivated = true; 01044 mThruFilterMode = inThruFilterMode; 01045 01046 } 01047 01048 01049 /*! \brief Setter method: turn message mirroring off. */ 01050 void MIDI::turnThruOff() 01051 { 01052 01053 mThruActivated = false; 01054 mThruFilterMode = Off; 01055 01056 } 01057 01058 01059 // This method is called upon reception of a message and takes care of Thru filtering and sending. 01060 void MIDI::thru_filter(byte inChannel) 01061 { 01062 01063 /* 01064 This method handles Soft-Thru filtering. 01065 01066 Soft-Thru filtering: 01067 - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off 01068 - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting 01069 01070 */ 01071 01072 // If the feature is disabled, don't do anything. 01073 if (!mThruActivated || (mThruFilterMode == Off)) return; 01074 01075 01076 // First, check if the received message is Channel 01077 if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { 01078 01079 const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)); 01080 01081 // Now let's pass it to the output 01082 switch (mThruFilterMode) { 01083 case Full: 01084 send(mMessage.type ,mMessage.data1 ,mMessage.data2 ,mMessage.channel ); 01085 return; 01086 case SameChannel: 01087 if (filter_condition) { 01088 send(mMessage.type ,mMessage.data1 ,mMessage.data2 ,mMessage.channel ); 01089 return; 01090 } 01091 break; 01092 case DifferentChannel: 01093 if (!filter_condition) { 01094 send(mMessage.type ,mMessage.data1 ,mMessage.data2 ,mMessage.channel ); 01095 return; 01096 } 01097 break; 01098 case Off: 01099 // Do nothing. 01100 // Technically it's impossible to get there because the case was already tested earlier. 01101 break; 01102 default: 01103 break; 01104 } 01105 01106 } 01107 else { 01108 01109 // Send the message to the output 01110 switch (mMessage.type ) { 01111 // Real Time and 1 byte 01112 case Clock: 01113 case Start: 01114 case Stop: 01115 case Continue: 01116 case ActiveSensing: 01117 case SystemReset: 01118 case TuneRequest: 01119 sendRealTime(mMessage.type ); 01120 return; 01121 01122 case SystemExclusive: 01123 // Send SysEx (0xF0 and 0xF7 are included in the buffer) 01124 sendSysEx(mMessage.data1 ,mMessage.sysex_array ,true); 01125 return; 01126 01127 case SongSelect: 01128 sendSongSelect(mMessage.data1 ); 01129 return; 01130 01131 case SongPosition: 01132 sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2 <<7)); 01133 return; 01134 01135 case TimeCodeQuarterFrame: 01136 sendTimeCodeQuarterFrame(mMessage.data1 ,mMessage.data2 ); 01137 return; 01138 01139 default: 01140 break; 01141 01142 } 01143 01144 } 01145 01146 } 01147 01148 01149 #endif // Thru
Generated on Tue Jul 12 2022 20:13:45 by 1.7.2