Arduino MIDI Library  Version 5.0.1
MIDI.hpp
Go to the documentation of this file.
1 
28 #pragma once
29 
31 
33 template<class Transport, class Settings, class Platform>
35  : mTransport(inTransport)
36  , mInputChannel(0)
37  , mRunningStatus_RX(InvalidType)
38  , mRunningStatus_TX(InvalidType)
39  , mPendingMessageExpectedLength(0)
40  , mPendingMessageIndex(0)
41  , mCurrentRpnNumber(0xffff)
42  , mCurrentNrpnNumber(0xffff)
43  , mThruActivated(true)
44  , mThruFilterMode(Thru::Full)
45  , mLastMessageSentTime(0)
46  , mLastMessageReceivedTime(0)
47  , mSenderActiveSensingPeriodicity(0)
48  , mReceiverActiveSensingActivated(false)
49  , mLastError(0)
50 {
51  mSenderActiveSensingPeriodicity = Settings::SenderActiveSensingPeriodicity;
52 }
53 
58 template<class Transport, class Settings, class Platform>
60 {
61 }
62 
63 // -----------------------------------------------------------------------------
64 
71 template<class Transport, class Settings, class Platform>
73 {
74  // Initialise the Transport layer
75  mTransport.begin();
76 
77  mInputChannel = inChannel;
78  mRunningStatus_TX = InvalidType;
79  mRunningStatus_RX = InvalidType;
80 
81  mPendingMessageIndex = 0;
82  mPendingMessageExpectedLength = 0;
83 
84  mCurrentRpnNumber = 0xffff;
85  mCurrentNrpnNumber = 0xffff;
86 
87  mLastMessageSentTime = Platform::now();
88 
89  mMessage.valid = false;
90  mMessage.type = InvalidType;
91  mMessage.channel = 0;
92  mMessage.data1 = 0;
93  mMessage.data2 = 0;
94  mMessage.length = 0;
95 
96  mThruFilterMode = Thru::Full;
97  mThruActivated = mTransport.thruActivated;
98 }
99 
100 // -----------------------------------------------------------------------------
101 // Output
102 // -----------------------------------------------------------------------------
103 
117 template<class Transport, class Settings, class Platform>
119 {
120  if (!inMessage.valid)
121  return;
122 
123  if (mTransport.beginTransmission(inMessage.type))
124  {
125  const StatusByte status = getStatus(inMessage.type, inMessage.channel);
126  mTransport.write(status);
127 
128  if (inMessage.type != MidiType::SystemExclusive)
129  {
130  if (inMessage.length > 1) mTransport.write(inMessage.data1);
131  if (inMessage.length > 2) mTransport.write(inMessage.data2);
132  } else
133  {
134  // sysexArray does not contain the start and end tags
135  mTransport.write(MidiType::SystemExclusiveStart);
136 
137  for (size_t i = 0; i < inMessage.getSysExSize(); i++)
138  mTransport.write(inMessage.sysexArray[i]);
139 
140  mTransport.write(MidiType::SystemExclusiveEnd);
141  }
142  }
143  mTransport.endTransmission();
144  UpdateLastSentTime();
145 }
146 
147 
159 template<class Transport, class Settings, class Platform>
161  DataByte inData1,
162  DataByte inData2,
163  Channel inChannel)
164 {
165  if (inType <= PitchBend) // Channel messages
166  {
167  // Then test if channel is valid
168  if (inChannel >= MIDI_CHANNEL_OFF ||
169  inChannel == MIDI_CHANNEL_OMNI ||
170  inType < 0x80)
171  {
172  return; // Don't send anything
173  }
174  // Protection: remove MSBs on data
175  inData1 &= 0x7f;
176  inData2 &= 0x7f;
177 
178  const StatusByte status = getStatus(inType, inChannel);
179 
180  if (mTransport.beginTransmission(inType))
181  {
182  if (Settings::UseRunningStatus)
183  {
184  if (mRunningStatus_TX != status)
185  {
186  // New message, memorise and send header
187  mRunningStatus_TX = status;
188  mTransport.write(mRunningStatus_TX);
189  }
190  }
191  else
192  {
193  // Don't care about running status, send the status byte.
194  mTransport.write(status);
195  }
196 
197  // Then send data
198  mTransport.write(inData1);
199  if (inType != ProgramChange && inType != AfterTouchChannel)
200  {
201  mTransport.write(inData2);
202  }
203 
204  mTransport.endTransmission();
205  UpdateLastSentTime();
206  }
207  }
208  else if (inType >= Clock && inType <= SystemReset)
209  {
210  sendRealTime(inType); // System Real-time and 1 byte.
211  }
212 }
213 
214 // -----------------------------------------------------------------------------
215 
225 template<class Transport, class Settings, class Platform>
227  DataByte inVelocity,
228  Channel inChannel)
229 {
230  send(NoteOn, inNoteNumber, inVelocity, inChannel);
231 }
232 
244 template<class Transport, class Settings, class Platform>
246  DataByte inVelocity,
247  Channel inChannel)
248 {
249  send(NoteOff, inNoteNumber, inVelocity, inChannel);
250 }
251 
256 template<class Transport, class Settings, class Platform>
258  Channel inChannel)
259 {
260  send(ProgramChange, inProgramNumber, 0, inChannel);
261 }
262 
269 template<class Transport, class Settings, class Platform>
271  DataByte inControlValue,
272  Channel inChannel)
273 {
274  send(ControlChange, inControlNumber, inControlValue, inChannel);
275 }
276 
284 template<class Transport, class Settings, class Platform>
286  DataByte inPressure,
287  Channel inChannel)
288 {
289  send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
290 }
291 
296 template<class Transport, class Settings, class Platform>
298  Channel inChannel)
299 {
300  send(AfterTouchChannel, inPressure, 0, inChannel);
301 }
302 
309 template<class Transport, class Settings, class Platform>
311  DataByte inPressure,
312  Channel inChannel)
313 {
314  send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
315 }
316 
323 template<class Transport, class Settings, class Platform>
325  Channel inChannel)
326 {
327  const unsigned bend = unsigned(inPitchValue - int(MIDI_PITCHBEND_MIN));
328  send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
329 }
330 
331 
338 template<class Transport, class Settings, class Platform>
340  Channel inChannel)
341 {
342  const int scale = inPitchValue > 0.0 ? MIDI_PITCHBEND_MAX : MIDI_PITCHBEND_MIN;
343  const int value = int(inPitchValue * double(scale));
344  sendPitchBend(value, inChannel);
345 }
346 
356 template<class Transport, class Settings, class Platform>
358  const byte* inArray,
359  bool inArrayContainsBoundaries)
360 {
361  const bool writeBeginEndBytes = !inArrayContainsBoundaries;
362 
363  if (mTransport.beginTransmission(MidiType::SystemExclusiveStart))
364  {
365  if (writeBeginEndBytes)
366  mTransport.write(MidiType::SystemExclusiveStart);
367 
368  for (unsigned i = 0; i < inLength; ++i)
369  mTransport.write(inArray[i]);
370 
371  if (writeBeginEndBytes)
372  mTransport.write(MidiType::SystemExclusiveEnd);
373 
374  mTransport.endTransmission();
375  UpdateLastSentTime();
376  }
377 
378  if (Settings::UseRunningStatus)
379  mRunningStatus_TX = InvalidType;
380 }
381 
387 template<class Transport, class Settings, class Platform>
389 {
390  sendCommon(TuneRequest);
391 }
392 
399 template<class Transport, class Settings, class Platform>
401  DataByte inValuesNibble)
402 {
403  const byte data = byte((((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f)));
404  sendTimeCodeQuarterFrame(data);
405 }
406 
413 template<class Transport, class Settings, class Platform>
415 {
416  sendCommon(TimeCodeQuarterFrame, inData);
417 }
418 
422 template<class Transport, class Settings, class Platform>
424 {
425  sendCommon(SongPosition, inBeats);
426 }
427 
429 template<class Transport, class Settings, class Platform>
431 {
432  sendCommon(SongSelect, inSongNumber);
433 }
434 
442 template<class Transport, class Settings, class Platform>
444 {
445  switch (inType)
446  {
448  case SongPosition:
449  case SongSelect:
450  case TuneRequest:
451  break;
452  default:
453  // Invalid Common marker
454  return;
455  }
456 
457  if (mTransport.beginTransmission(inType))
458  {
459  mTransport.write((byte)inType);
460  switch (inType)
461  {
463  mTransport.write(inData1);
464  break;
465  case SongPosition:
466  mTransport.write(inData1 & 0x7f);
467  mTransport.write((inData1 >> 7) & 0x7f);
468  break;
469  case SongSelect:
470  mTransport.write(inData1 & 0x7f);
471  break;
472  case TuneRequest:
473  break;
474  default:
475  break; // LCOV_EXCL_LINE - Coverage blind spot
476  }
477  mTransport.endTransmission();
478  UpdateLastSentTime();
479  }
480 
481  if (Settings::UseRunningStatus)
482  mRunningStatus_TX = InvalidType;
483 }
484 
491 template<class Transport, class Settings, class Platform>
493 {
494  // Do not invalidate Running Status for real-time messages
495  // as they can be interleaved within any message.
496 
497  switch (inType)
498  {
499  case Clock:
500  case Start:
501  case Stop:
502  case Continue:
503  case ActiveSensing:
504  case SystemReset:
505  if (mTransport.beginTransmission(inType))
506  {
507  mTransport.write((byte)inType);
508  mTransport.endTransmission();
509  UpdateLastSentTime();
510  }
511  break;
512  default:
513  // Invalid Real Time marker
514  break;
515  }
516 }
517 
522 template<class Transport, class Settings, class Platform>
524  Channel inChannel)
525 {
526  if (mCurrentRpnNumber != inNumber)
527  {
528  const byte numMsb = 0x7f & (inNumber >> 7);
529  const byte numLsb = 0x7f & inNumber;
530  sendControlChange(RPNLSB, numLsb, inChannel);
531  sendControlChange(RPNMSB, numMsb, inChannel);
532  mCurrentRpnNumber = inNumber;
533  }
534 }
535 
540 template<class Transport, class Settings, class Platform>
542  Channel inChannel)
543 {;
544  const byte valMsb = 0x7f & (inValue >> 7);
545  const byte valLsb = 0x7f & inValue;
546  sendControlChange(DataEntryMSB, valMsb, inChannel);
547  sendControlChange(DataEntryLSB, valLsb, inChannel);
548 }
549 
555 template<class Transport, class Settings, class Platform>
557  byte inLsb,
558  Channel inChannel)
559 {
560  sendControlChange(DataEntryMSB, inMsb, inChannel);
561  sendControlChange(DataEntryLSB, inLsb, inChannel);
562 }
563 
564 /* \brief Increment the value of the currently selected RPN number by the specified amount.
565  \param inAmount The amount to add to the currently selected RPN value.
566 */
567 template<class Transport, class Settings, class Platform>
569  Channel inChannel)
570 {
571  sendControlChange(DataIncrement, inAmount, inChannel);
572 }
573 
574 /* \brief Decrement the value of the currently selected RPN number by the specified amount.
575  \param inAmount The amount to subtract to the currently selected RPN value.
576 */
577 template<class Transport, class Settings, class Platform>
579  Channel inChannel)
580 {
581  sendControlChange(DataDecrement, inAmount, inChannel);
582 }
583 
588 template<class Transport, class Settings, class Platform>
590 {
591  sendControlChange(RPNLSB, 0x7f, inChannel);
592  sendControlChange(RPNMSB, 0x7f, inChannel);
593  mCurrentRpnNumber = 0xffff;
594 }
595 
596 
597 
602 template<class Transport, class Settings, class Platform>
604  Channel inChannel)
605 {
606  if (mCurrentNrpnNumber != inNumber)
607  {
608  const byte numMsb = 0x7f & (inNumber >> 7);
609  const byte numLsb = 0x7f & inNumber;
610  sendControlChange(NRPNLSB, numLsb, inChannel);
611  sendControlChange(NRPNMSB, numMsb, inChannel);
612  mCurrentNrpnNumber = inNumber;
613  }
614 }
615 
620 template<class Transport, class Settings, class Platform>
622  Channel inChannel)
623 {;
624  const byte valMsb = 0x7f & (inValue >> 7);
625  const byte valLsb = 0x7f & inValue;
626  sendControlChange(DataEntryMSB, valMsb, inChannel);
627  sendControlChange(DataEntryLSB, valLsb, inChannel);
628 }
629 
635 template<class Transport, class Settings, class Platform>
637  byte inLsb,
638  Channel inChannel)
639 {
640  sendControlChange(DataEntryMSB, inMsb, inChannel);
641  sendControlChange(DataEntryLSB, inLsb, inChannel);
642 }
643 
644 /* \brief Increment the value of the currently selected NRPN number by the specified amount.
645  \param inAmount The amount to add to the currently selected NRPN value.
646 */
647 template<class Transport, class Settings, class Platform>
649  Channel inChannel)
650 {
651  sendControlChange(DataIncrement, inAmount, inChannel);
652 }
653 
654 /* \brief Decrement the value of the currently selected NRPN number by the specified amount.
655  \param inAmount The amount to subtract to the currently selected NRPN value.
656 */
657 template<class Transport, class Settings, class Platform>
659  Channel inChannel)
660 {
661  sendControlChange(DataDecrement, inAmount, inChannel);
662 }
663 
668 template<class Transport, class Settings, class Platform>
670 {
671  sendControlChange(NRPNLSB, 0x7f, inChannel);
672  sendControlChange(NRPNMSB, 0x7f, inChannel);
673  mCurrentNrpnNumber = 0xffff;
674 }
675  // End of doc group MIDI Output
677 
678 // -----------------------------------------------------------------------------
679 
680 template<class Transport, class Settings, class Platform>
682  Channel inChannel) const
683 {
684  return StatusByte(((byte)inType | ((inChannel - 1) & 0x0f)));
685 }
686 
687 // -----------------------------------------------------------------------------
688 // Input
689 // -----------------------------------------------------------------------------
690 
703 template<class Transport, class Settings, class Platform>
705 {
706  return read(mInputChannel);
707 }
708 
711 template<class Transport, class Settings, class Platform>
713 {
714  #ifndef RegionActiveSending
715  // Active Sensing. This message is intended to be sent
716  // repeatedly to tell the receiver that a connection is alive. Use
717  // of this message is optional. When initially received, the
718  // receiver will expect to receive another Active Sensing
719  // message each 300ms (max), and if it does not then it will
720  // assume that the connection has been terminated. At
721  // termination, the receiver will turn off all voices and return to
722  // normal (non- active sensing) operation.
723  if (Settings::UseSenderActiveSensing && (mSenderActiveSensingPeriodicity > 0) && (Platform::now() - mLastMessageSentTime) > mSenderActiveSensingPeriodicity)
724  {
725  sendActiveSensing();
726  mLastMessageSentTime = Platform::now();
727  }
728 
729  if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated && (mLastMessageReceivedTime + ActiveSensingTimeout < Platform::now()))
730  {
731  mReceiverActiveSensingActivated = false;
732 
733  mLastError |= 1UL << ErrorActiveSensingTimeout; // set the ErrorActiveSensingTimeout bit
734  if (mErrorCallback)
735  mErrorCallback(mLastError);
736  }
737  #endif
738 
739  if (inChannel >= MIDI_CHANNEL_OFF)
740  return false; // MIDI Input disabled.
741 
742  if (!parse())
743  return false;
744 
745  #ifndef RegionActiveSending
746 
747  if (Settings::UseReceiverActiveSensing && mMessage.type == ActiveSensing)
748  {
749  // When an ActiveSensing message is received, the time keeping is activated.
750  // When a timeout occurs, an error message is send and time keeping ends.
751  mReceiverActiveSensingActivated = true;
752 
753  // is ErrorActiveSensingTimeout bit in mLastError on
754  if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
755  {
756  mLastError &= ~(1UL << ErrorActiveSensingTimeout); // clear the ErrorActiveSensingTimeout bit
757  if (mErrorCallback)
758  mErrorCallback(mLastError);
759  }
760  }
761 
762  // Keep the time of the last received message, so we can check for the timeout
763  if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
764  mLastMessageReceivedTime = Platform::now();
765 
766  #endif
767 
768  handleNullVelocityNoteOnAsNoteOff();
769 
770  const bool channelMatch = inputFilter(inChannel);
771  if (channelMatch)
772  launchCallback();
773 
774  thruFilter(inChannel);
775 
776  return channelMatch;
777 }
778 
779 // -----------------------------------------------------------------------------
780 
781 // Private method: MIDI parser
782 template<class Transport, class Settings, class Platform>
784 {
785  if (mTransport.available() == 0)
786  return false; // No data available.
787 
788  // clear the ErrorParse bit
789  mLastError &= ~(1UL << ErrorParse);
790 
791  // Parsing algorithm:
792  // Get a byte from the serial buffer.
793  // If there is no pending message to be recomposed, start a new one.
794  // - Find type and channel (if pertinent)
795  // - Look for other bytes in buffer, call parser recursively,
796  // until the message is assembled or the buffer is empty.
797  // Else, add the extracted byte to the pending message, and check validity.
798  // When the message is done, store it.
799 
800  const byte extracted = mTransport.read();
801 
802  // Ignore Undefined
803  if (extracted == Undefined_FD)
804  return (Settings::Use1ByteParsing) ? false : parse();
805 
806  if (mPendingMessageIndex == 0)
807  {
808  // Start a new pending message
809  mPendingMessage[0] = extracted;
810 
811  // Check for running status first
812  if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
813  {
814  // Only these types allow Running Status
815 
816  // If the status byte is not received, prepend it
817  // to the pending message
818  if (extracted < 0x80)
819  {
820  mPendingMessage[0] = mRunningStatus_RX;
821  mPendingMessage[1] = extracted;
822  mPendingMessageIndex = 1;
823  }
824  // Else: well, we received another status byte,
825  // so the running status does not apply here.
826  // It will be updated upon completion of this message.
827  }
828 
829  const MidiType pendingType = getTypeFromStatusByte(mPendingMessage[0]);
830 
831  switch (pendingType)
832  {
833  // 1 byte messages
834  case Start:
835  case Continue:
836  case Stop:
837  case Clock:
838  case Tick:
839  case ActiveSensing:
840  case SystemReset:
841  case TuneRequest:
842  // Handle the message type directly here.
843  mMessage.type = pendingType;
844  mMessage.channel = 0;
845  mMessage.data1 = 0;
846  mMessage.data2 = 0;
847  mMessage.valid = true;
848 
849  // Do not reset all input attributes, Running Status must remain unchanged.
850  // We still need to reset these
851  mPendingMessageIndex = 0;
852  mPendingMessageExpectedLength = 0;
853 
854  return true;
855  break;
856 
857  // 2 bytes messages
858  case ProgramChange:
859  case AfterTouchChannel:
861  case SongSelect:
862  mPendingMessageExpectedLength = 2;
863  break;
864 
865  // 3 bytes messages
866  case NoteOn:
867  case NoteOff:
868  case ControlChange:
869  case PitchBend:
870  case AfterTouchPoly:
871  case SongPosition:
872  mPendingMessageExpectedLength = 3;
873  break;
874 
876  case SystemExclusiveEnd:
877  // The message can be any length
878  // between 3 and MidiMessage::sSysExMaxSize bytes
879  mPendingMessageExpectedLength = MidiMessage::sSysExMaxSize;
880  mRunningStatus_RX = InvalidType;
881  mMessage.sysexArray[0] = pendingType;
882  break;
883 
884  case InvalidType:
885  default:
886  // This is obviously wrong. Let's get the hell out'a here.
887  mLastError |= 1UL << ErrorParse; // set the ErrorParse bit
888  if (mErrorCallback)
889  mErrorCallback(mLastError); // LCOV_EXCL_LINE
890 
891  resetInput();
892  return false;
893  break;
894  }
895 
896  if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
897  {
898  // Reception complete
899  mMessage.type = pendingType;
900  mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
901  mMessage.data1 = mPendingMessage[1];
902  mMessage.data2 = 0; // Completed new message has 1 data byte
903  mMessage.length = 1;
904 
905  mPendingMessageIndex = 0;
906  mPendingMessageExpectedLength = 0;
907  mMessage.valid = true;
908 
909  return true;
910  }
911  else
912  {
913  // Waiting for more data
914  mPendingMessageIndex++;
915  }
916 
917  return (Settings::Use1ByteParsing) ? false : parse();
918  }
919  else
920  {
921  // First, test if this is a status byte
922  if (extracted >= 0x80)
923  {
924  // Reception of status bytes in the middle of an uncompleted message
925  // are allowed only for interleaved Real Time message or EOX
926  switch (extracted)
927  {
928  case Clock:
929  case Start:
930  case Tick:
931  case Continue:
932  case Stop:
933  case ActiveSensing:
934  case SystemReset:
935 
936  // Here we will have to extract the one-byte message,
937  // pass it to the structure for being read outside
938  // the MIDI class, and recompose the message it was
939  // interleaved into. Oh, and without killing the running status..
940  // This is done by leaving the pending message as is,
941  // it will be completed on next calls.
942 
943  mMessage.type = (MidiType)extracted;
944  mMessage.data1 = 0;
945  mMessage.data2 = 0;
946  mMessage.channel = 0;
947  mMessage.length = 1;
948  mMessage.valid = true;
949 
950  return true;
951 
952  // Exclusive
954  case SystemExclusiveEnd:
955  if ((mMessage.sysexArray[0] == SystemExclusiveStart)
956  || (mMessage.sysexArray[0] == SystemExclusiveEnd))
957  {
958  // Store the last byte (EOX)
959  mMessage.sysexArray[mPendingMessageIndex++] = extracted;
960  mMessage.type = SystemExclusive;
961 
962  // Get length
963  mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
964  mMessage.data2 = byte(mPendingMessageIndex >> 8); // MSB
965  mMessage.channel = 0;
966  mMessage.length = mPendingMessageIndex;
967  mMessage.valid = true;
968 
969  resetInput();
970 
971  return true;
972  }
973  else
974  {
975  // Well well well.. error.
976  mLastError |= 1UL << ErrorParse; // set the error bits
977  if (mErrorCallback)
978  mErrorCallback(mLastError); // LCOV_EXCL_LINE
979 
980  resetInput();
981  return false;
982  }
983 
984  default:
985  break; // LCOV_EXCL_LINE - Coverage blind spot
986  }
987  }
988 
989  // Add extracted data byte to pending message
990  if ((mPendingMessage[0] == SystemExclusiveStart)
991  || (mPendingMessage[0] == SystemExclusiveEnd))
992  mMessage.sysexArray[mPendingMessageIndex] = extracted;
993  else
994  mPendingMessage[mPendingMessageIndex] = extracted;
995 
996  // Now we are going to check if we have reached the end of the message
997  if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
998  {
999  // SysEx larger than the allocated buffer size,
1000  // Split SysEx like so:
1001  // first: 0xF0 .... 0xF0
1002  // midlle: 0xF7 .... 0xF0
1003  // last: 0xF7 .... 0xF7
1004  if ((mPendingMessage[0] == SystemExclusiveStart)
1005  || (mPendingMessage[0] == SystemExclusiveEnd))
1006  {
1007  auto lastByte = mMessage.sysexArray[Settings::SysExMaxSize - 1];
1008  mMessage.sysexArray[Settings::SysExMaxSize - 1] = SystemExclusiveStart;
1009  mMessage.type = SystemExclusive;
1010 
1011  // Get length
1012  mMessage.data1 = Settings::SysExMaxSize & 0xff; // LSB
1013  mMessage.data2 = byte(Settings::SysExMaxSize >> 8); // MSB
1014  mMessage.channel = 0;
1015  mMessage.length = Settings::SysExMaxSize;
1016  mMessage.valid = true;
1017 
1018  // No need to check against the inputChannel,
1019  // SysEx ignores input channel
1020  launchCallback();
1021 
1022  mMessage.sysexArray[0] = SystemExclusiveEnd;
1023  mMessage.sysexArray[1] = lastByte;
1024 
1025  mPendingMessageIndex = 2;
1026 
1027  return false;
1028  }
1029 
1030  mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
1031 
1032  if (isChannelMessage(mMessage.type))
1033  mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
1034  else
1035  mMessage.channel = 0;
1036 
1037  mMessage.data1 = mPendingMessage[1];
1038  // Save data2 only if applicable
1039  mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0;
1040 
1041  // Reset local variables
1042  mPendingMessageIndex = 0;
1043  mPendingMessageExpectedLength = 0;
1044 
1045  mMessage.valid = true;
1046 
1047  // Activate running status (if enabled for the received type)
1048  switch (mMessage.type)
1049  {
1050  case NoteOff:
1051  case NoteOn:
1052  case AfterTouchPoly:
1053  case ControlChange:
1054  case ProgramChange:
1055  case AfterTouchChannel:
1056  case PitchBend:
1057  // Running status enabled: store it from received message
1058  mRunningStatus_RX = mPendingMessage[0];
1059  break;
1060 
1061  default:
1062  // No running status
1063  mRunningStatus_RX = InvalidType;
1064  break;
1065  }
1066  return true;
1067  }
1068  else
1069  {
1070  // Then update the index of the pending message.
1071  mPendingMessageIndex++;
1072 
1073  return (Settings::Use1ByteParsing) ? false : parse();
1074  }
1075  }
1076 }
1077 
1078 // Private method, see midi_Settings.h for documentation
1079 template<class Transport, class Settings, class Platform>
1081 {
1082  if (Settings::HandleNullVelocityNoteOnAsNoteOff &&
1083  getType() == NoteOn && getData2() == 0)
1084  {
1085  mMessage.type = NoteOff;
1086  }
1087 }
1088 
1089 // Private method: check if the received message is on the listened channel
1090 template<class Transport, class Settings, class Platform>
1092 {
1093  // This method handles recognition of channel
1094  // (to know if the message is destinated to the Arduino)
1095 
1096  // First, check if the received message is Channel
1097  if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
1098  {
1099  // Then we need to know if we listen to it
1100  if ((mMessage.channel == inChannel) ||
1101  (inChannel == MIDI_CHANNEL_OMNI))
1102  {
1103  return true;
1104  }
1105  else
1106  {
1107  // We don't listen to this channel
1108  return false;
1109  }
1110  }
1111  else
1112  {
1113  // System messages are always received
1114  return true;
1115  }
1116 }
1117 
1118 // Private method: reset input attributes
1119 template<class Transport, class Settings, class Platform>
1121 {
1122  mPendingMessageIndex = 0;
1123  mPendingMessageExpectedLength = 0;
1124  mRunningStatus_RX = InvalidType;
1125 }
1126 
1127 // -----------------------------------------------------------------------------
1128 
1133 template<class Transport, class Settings, class Platform>
1135 {
1136  return mMessage.type;
1137 }
1138 
1144 template<class Transport, class Settings, class Platform>
1146 {
1147  return mMessage.channel;
1148 }
1149 
1151 template<class Transport, class Settings, class Platform>
1153 {
1154  return mMessage.data1;
1155 }
1156 
1158 template<class Transport, class Settings, class Platform>
1160 {
1161  return mMessage.data2;
1162 }
1163 
1168 template<class Transport, class Settings, class Platform>
1170 {
1171  return mMessage.sysexArray;
1172 }
1173 
1179 template<class Transport, class Settings, class Platform>
1181 {
1182  return mMessage.getSysExSize();
1183 }
1184 
1186 template<class Transport, class Settings, class Platform>
1188 {
1189  return mMessage.valid;
1190 }
1191 
1192 // -----------------------------------------------------------------------------
1193 
1194 template<class Transport, class Settings, class Platform>
1196 {
1197  return mInputChannel;
1198 }
1199 
1204 template<class Transport, class Settings, class Platform>
1206 {
1207  mInputChannel = inChannel;
1208 }
1209 
1210 // -----------------------------------------------------------------------------
1211 
1217 template<class Transport, class Settings, class Platform>
1219 {
1220  if ((inStatus < 0x80) ||
1221  (inStatus == Undefined_F4) ||
1222  (inStatus == Undefined_F5) ||
1223  (inStatus == Undefined_FD))
1224  return InvalidType; // Data bytes and undefined.
1225 
1226  if (inStatus < 0xf0)
1227  // Channel message, remove channel nibble.
1228  return MidiType(inStatus & 0xf0);
1229 
1230  return MidiType(inStatus);
1231 }
1232 
1235 template<class Transport, class Settings, class Platform>
1237 {
1238  return Channel((inStatus & 0x0f) + 1);
1239 }
1240 
1241 template<class Transport, class Settings, class Platform>
1243 {
1244  return (inType == NoteOff ||
1245  inType == NoteOn ||
1246  inType == ControlChange ||
1247  inType == AfterTouchPoly ||
1248  inType == AfterTouchChannel ||
1249  inType == PitchBend ||
1250  inType == ProgramChange);
1251 }
1252 
1253 // -----------------------------------------------------------------------------
1254 
1261 template<class Transport, class Settings, class Platform>
1263 {
1264  switch (inType)
1265  {
1266  case NoteOff: mNoteOffCallback = nullptr; break;
1267  case NoteOn: mNoteOnCallback = nullptr; break;
1268  case AfterTouchPoly: mAfterTouchPolyCallback = nullptr; break;
1269  case ControlChange: mControlChangeCallback = nullptr; break;
1270  case ProgramChange: mProgramChangeCallback = nullptr; break;
1271  case AfterTouchChannel: mAfterTouchChannelCallback = nullptr; break;
1272  case PitchBend: mPitchBendCallback = nullptr; break;
1273  case SystemExclusive: mSystemExclusiveCallback = nullptr; break;
1274  case TimeCodeQuarterFrame: mTimeCodeQuarterFrameCallback = nullptr; break;
1275  case SongPosition: mSongPositionCallback = nullptr; break;
1276  case SongSelect: mSongSelectCallback = nullptr; break;
1277  case TuneRequest: mTuneRequestCallback = nullptr; break;
1278  case Clock: mClockCallback = nullptr; break;
1279  case Start: mStartCallback = nullptr; break;
1280  case Tick: mTickCallback = nullptr; break;
1281  case Continue: mContinueCallback = nullptr; break;
1282  case Stop: mStopCallback = nullptr; break;
1283  case ActiveSensing: mActiveSensingCallback = nullptr; break;
1284  case SystemReset: mSystemResetCallback = nullptr; break;
1285  default:
1286  break;
1287  }
1288 }
1289  // End of doc group MIDI Callbacks
1291 
1292 // Private - launch callback function based on received type.
1293 template<class Transport, class Settings, class Platform>
1295 {
1296  if (mMessageCallback != 0) mMessageCallback(mMessage);
1297 
1298  // The order is mixed to allow frequent messages to trigger their callback faster.
1299  switch (mMessage.type)
1300  {
1301  // Notes
1302  case NoteOff: if (mNoteOffCallback != nullptr) mNoteOffCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1303  case NoteOn: if (mNoteOnCallback != nullptr) mNoteOnCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1304 
1305  // Real-time messages
1306  case Clock: if (mClockCallback != nullptr) mClockCallback(); break;
1307  case Start: if (mStartCallback != nullptr) mStartCallback(); break;
1308  case Tick: if (mTickCallback != nullptr) mTickCallback(); break;
1309  case Continue: if (mContinueCallback != nullptr) mContinueCallback(); break;
1310  case Stop: if (mStopCallback != nullptr) mStopCallback(); break;
1311  case ActiveSensing: if (mActiveSensingCallback != nullptr) mActiveSensingCallback(); break;
1312 
1313  // Continuous controllers
1314  case ControlChange: if (mControlChangeCallback != nullptr) mControlChangeCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1315  case PitchBend: if (mPitchBendCallback != nullptr) mPitchBendCallback(mMessage.channel, (int)((mMessage.data1 & 0x7f) | ((mMessage.data2 & 0x7f) << 7)) + MIDI_PITCHBEND_MIN); break;
1316  case AfterTouchPoly: if (mAfterTouchPolyCallback != nullptr) mAfterTouchPolyCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1317  case AfterTouchChannel: if (mAfterTouchChannelCallback != nullptr) mAfterTouchChannelCallback(mMessage.channel, mMessage.data1); break;
1318 
1319  case ProgramChange: if (mProgramChangeCallback != nullptr) mProgramChangeCallback(mMessage.channel, mMessage.data1); break;
1320  case SystemExclusive: if (mSystemExclusiveCallback != nullptr) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.getSysExSize()); break;
1321 
1322  // Occasional messages
1323  case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != nullptr) mTimeCodeQuarterFrameCallback(mMessage.data1); break;
1324  case SongPosition: if (mSongPositionCallback != nullptr) mSongPositionCallback(unsigned((mMessage.data1 & 0x7f) | ((mMessage.data2 & 0x7f) << 7))); break;
1325  case SongSelect: if (mSongSelectCallback != nullptr) mSongSelectCallback(mMessage.data1); break;
1326  case TuneRequest: if (mTuneRequestCallback != nullptr) mTuneRequestCallback(); break;
1327 
1328  case SystemReset: if (mSystemResetCallback != nullptr) mSystemResetCallback(); break;
1329 
1330  case InvalidType:
1331  default:
1332  break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
1333  }
1334 }
1335  // End of doc group MIDI Input
1337 
1338 // -----------------------------------------------------------------------------
1339 // Thru
1340 // -----------------------------------------------------------------------------
1341 
1351 template<class Transport, class Settings, class Platform>
1353 {
1354  mThruFilterMode = inThruFilterMode;
1355  mThruActivated = mThruFilterMode != Thru::Off;
1356 }
1357 
1358 template<class Transport, class Settings, class Platform>
1360 {
1361  return mThruFilterMode;
1362 }
1363 
1364 template<class Transport, class Settings, class Platform>
1366 {
1367  return mThruActivated;
1368 }
1369 
1370 template<class Transport, class Settings, class Platform>
1372 {
1373  mThruActivated = true;
1374  mThruFilterMode = inThruFilterMode;
1375 }
1376 
1377 template<class Transport, class Settings, class Platform>
1379 {
1380  mThruActivated = false;
1381  mThruFilterMode = Thru::Off;
1382 }
1383 
1384 template<class Transport, class Settings, class Platform>
1386 {
1387  if (Settings::UseSenderActiveSensing && mSenderActiveSensingPeriodicity)
1388  mLastMessageSentTime = Platform::now();
1389 }
1390  // End of doc group MIDI Thru
1392 
1393 // This method is called upon reception of a message
1394 // and takes care of Thru filtering and sending.
1395 // - All system messages (System Exclusive, Common and Real Time) are passed
1396 // to output unless filter is set to Off.
1397 // - Channel messages are passed to the output whether their channel
1398 // is matching the input channel and the filter setting
1399 template<class Transport, class Settings, class Platform>
1401 {
1402  // If the feature is disabled, don't do anything.
1403  if (!mThruActivated || (mThruFilterMode == Thru::Off))
1404  return;
1405 
1406  // First, check if the received message is Channel
1407  if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
1408  {
1409  const bool filter_condition = ((mMessage.channel == inChannel) ||
1410  (inChannel == MIDI_CHANNEL_OMNI));
1411 
1412  // Now let's pass it to the output
1413  switch (mThruFilterMode)
1414  {
1415  case Thru::Full:
1416  send(mMessage.type,
1417  mMessage.data1,
1418  mMessage.data2,
1419  mMessage.channel);
1420  break;
1421 
1422  case Thru::SameChannel:
1423  if (filter_condition)
1424  {
1425  send(mMessage.type,
1426  mMessage.data1,
1427  mMessage.data2,
1428  mMessage.channel);
1429  }
1430  break;
1431 
1433  if (!filter_condition)
1434  {
1435  send(mMessage.type,
1436  mMessage.data1,
1437  mMessage.data2,
1438  mMessage.channel);
1439  }
1440  break;
1441 
1442  default:
1443  break;
1444  }
1445  }
1446  else
1447  {
1448  // Send the message to the output
1449  switch (mMessage.type)
1450  {
1451  // Real Time and 1 byte
1452  case Clock:
1453  case Start:
1454  case Stop:
1455  case Continue:
1456  case ActiveSensing:
1457  case SystemReset:
1458  case TuneRequest:
1459  sendRealTime(mMessage.type);
1460  break;
1461 
1462  case SystemExclusive:
1463  // Send SysEx (0xf0 and 0xf7 are included in the buffer)
1464  sendSysEx(getSysExArrayLength(), getSysExArray(), true);
1465  break;
1466 
1467  case SongSelect:
1468  sendSongSelect(mMessage.data1);
1469  break;
1470 
1471  case SongPosition:
1472  sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2 << 7));
1473  break;
1474 
1475  case TimeCodeQuarterFrame:
1476  sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
1477  break;
1478 
1479  default:
1480  break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
1481  }
1482  }
1483 }
1484 
MidiInterface::setInputChannel
void setInputChannel(Channel inChannel)
Set the value for the input MIDI channel.
Definition: MIDI.hpp:1205
Undefined_F4
@ Undefined_F4
Definition: midi_Defs.h:109
MidiInterface::~MidiInterface
~MidiInterface()
Destructor for MidiInterface.
Definition: MIDI.hpp:59
MidiInterface::sendNrpnDecrement
void sendNrpnDecrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:658
Clock
@ Clock
System Real Time - Timing Clock.
Definition: midi_Defs.h:113
Message::getSysExSize
unsigned getSysExSize() const
Definition: midi_Message.h:98
MidiInterface::turnThruOn
void turnThruOn(Thru::Mode inThruFilterMode=Thru::Full)
Definition: MIDI.hpp:1371
SystemReset
@ SystemReset
System Real Time - System Reset.
Definition: midi_Defs.h:121
MidiInterface::getData1
DataByte getData1() const
Get the first data byte of the last received message.
Definition: MIDI.hpp:1152
SystemExclusive
@ SystemExclusive
System Exclusive.
Definition: midi_Defs.h:104
TuneRequest
@ TuneRequest
System Common - Tune Request.
Definition: midi_Defs.h:111
Thru::Mode
Mode
Definition: midi_Defs.h:130
MidiInterface::getData2
DataByte getData2() const
Get the second data byte of the last received message.
Definition: MIDI.hpp:1159
ErrorActiveSensingTimeout
static const uint8_t ErrorActiveSensingTimeout
Definition: midi_Defs.h:64
MidiInterface::check
bool check() const
Check if a valid message is stored in the structure.
Definition: MIDI.hpp:1187
ActiveSensingTimeout
static const uint16_t ActiveSensingTimeout
Definition: midi_Defs.h:51
MidiInterface::sendRpnDecrement
void sendRpnDecrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:578
DataEntryLSB
@ DataEntryLSB
Definition: midi_Defs.h:168
byte
uint8_t byte
Definition: midi_Defs.h:36
Undefined_F5
@ Undefined_F5
Definition: midi_Defs.h:110
MIDI_PITCHBEND_MIN
#define MIDI_PITCHBEND_MIN
Definition: midi_Defs.h:46
MidiInterface::MidiInterface
MidiInterface(Transport &)
Constructor for MidiInterface.
Definition: MIDI.hpp:34
MidiInterface::beginRpn
void beginRpn(unsigned inNumber, Channel inChannel)
Start a Registered Parameter Number frame.
Definition: MIDI.hpp:523
MidiInterface::sendSongSelect
void sendSongSelect(DataByte inSongNumber)
Send a Song Select message.
Definition: MIDI.hpp:430
Message::sysexArray
DataByte sysexArray[sSysExMaxSize]
Definition: midi_Message.h:86
MIDI_CHANNEL_OFF
#define MIDI_CHANNEL_OFF
Definition: midi_Defs.h:44
ProgramChange
@ ProgramChange
Channel Message - Program Change.
Definition: midi_Defs.h:101
END_MIDI_NAMESPACE
#define END_MIDI_NAMESPACE
Definition: midi_Namespace.h:32
Start
@ Start
System Real Time - Start.
Definition: midi_Defs.h:116
InvalidType
@ InvalidType
For notifying errors.
Definition: midi_Defs.h:96
MidiInterface::sendSongPosition
void sendSongPosition(unsigned inBeats)
Send a Song Position Pointer message.
Definition: MIDI.hpp:423
DataIncrement
@ DataIncrement
Definition: midi_Defs.h:200
MidiInterface::sendControlChange
void sendControlChange(DataByte inControlNumber, DataByte inControlValue, Channel inChannel)
Send a Control Change message.
Definition: MIDI.hpp:270
MidiInterface::endNrpn
void endNrpn(Channel inChannel)
Terminate an NRPN frame. This will send a Null Function to deselect the currently selected NRPN.
Definition: MIDI.hpp:669
SystemExclusiveStart
@ SystemExclusiveStart
System Exclusive Start.
Definition: midi_Defs.h:105
MidiInterface::sendCommon
void sendCommon(MidiType inType, unsigned=0)
Send a Common message. Common messages reset the running status.
Definition: MIDI.hpp:443
Thru::Off
@ Off
Thru disabled (nothing passes through).
Definition: midi_Defs.h:131
MidiInterface::sendNoteOn
void sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note On message.
Definition: MIDI.hpp:226
MidiInterface::sendNrpnValue
void sendNrpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected NRPN number.
Definition: MIDI.hpp:621
MidiInterface::getFilterMode
Thru::Mode getFilterMode() const
Definition: MIDI.hpp:1359
MidiInterface::sendRealTime
void sendRealTime(MidiType inType)
Send a Real Time (one byte) message.
Definition: MIDI.hpp:492
AfterTouchPoly
@ AfterTouchPoly
Channel Message - Polyphonic AfterTouch.
Definition: midi_Defs.h:99
MidiInterface::turnThruOff
void turnThruOff()
Definition: MIDI.hpp:1378
MidiInterface::sendTimeCodeQuarterFrame
void sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble)
Send a MIDI Time Code Quarter Frame.
Definition: MIDI.hpp:400
Message::valid
bool valid
Definition: midi_Message.h:92
MidiInterface::sendRpnValue
void sendRpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected RPN number.
Definition: MIDI.hpp:541
MidiInterface::send
void send(const MidiMessage &)
Send a MIDI message.
Definition: MIDI.hpp:118
MidiInterface::beginNrpn
void beginNrpn(unsigned inNumber, Channel inChannel)
Start a Non-Registered Parameter Number frame.
Definition: MIDI.hpp:603
MidiInterface::sendProgramChange
void sendProgramChange(DataByte inProgramNumber, Channel inChannel)
Send a Program Change message.
Definition: MIDI.hpp:257
MidiInterface::sendNoteOff
void sendNoteOff(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note Off message.
Definition: MIDI.hpp:245
MidiInterface::isChannelMessage
static bool isChannelMessage(MidiType inType)
Definition: MIDI.hpp:1242
MidiInterface::getTypeFromStatusByte
static MidiType getTypeFromStatusByte(byte inStatus)
Extract an enumerated MIDI type from a status byte.
Definition: MIDI.hpp:1218
Message::length
unsigned length
Definition: midi_Message.h:96
Thru::SameChannel
@ SameChannel
Only the messages on the Input Channel will be sent back.
Definition: midi_Defs.h:133
TimeCodeQuarterFrame
@ TimeCodeQuarterFrame
System Common - MIDI Time Code Quarter Frame.
Definition: midi_Defs.h:106
Undefined_FD
@ Undefined_FD
Definition: midi_Defs.h:119
StatusByte
byte StatusByte
Definition: midi_Defs.h:56
ErrorParse
static const uint8_t ErrorParse
Definition: midi_Defs.h:63
MidiInterface::getThruState
bool getThruState() const
Definition: MIDI.hpp:1365
MidiInterface
The main class for MIDI handling. It is templated over the type of serial port to provide abstraction...
Definition: MIDI.h:54
RPNLSB
@ RPNLSB
Registered Parameter Number (LSB)
Definition: midi_Defs.h:204
MidiInterface::sendAfterTouch
void sendAfterTouch(DataByte inPressure, Channel inChannel)
Send a MonoPhonic AfterTouch message (applies to all notes)
Definition: MIDI.hpp:297
MidiInterface::getSysExArray
const byte * getSysExArray() const
Get the System Exclusive byte array.
Definition: MIDI.hpp:1169
NoteOn
@ NoteOn
Channel Message - Note On.
Definition: midi_Defs.h:98
Continue
@ Continue
System Real Time - Continue.
Definition: midi_Defs.h:117
MidiInterface::getType
MidiType getType() const
Get the last received message's type.
Definition: MIDI.hpp:1134
MidiInterface::disconnectCallbackFromType
void disconnectCallbackFromType(MidiType inType)
Detach an external function from the given type.
Definition: MIDI.hpp:1262
MidiInterface::sendPolyPressure
void sendPolyPressure(DataByte inNoteNumber, DataByte inPressure, Channel inChannel) __attribute__((deprecated))
Send a Polyphonic AfterTouch message (applies to a specified note)
Definition: MIDI.hpp:285
NoteOff
@ NoteOff
Channel Message - Note Off.
Definition: midi_Defs.h:97
MidiInterface::getSysExArrayLength
unsigned getSysExArrayLength() const
Get the length of the System Exclusive array.
Definition: MIDI.hpp:1180
MidiInterface::begin
void begin(Channel inChannel=1)
Call the begin method in the setup() function of the Arduino.
Definition: MIDI.hpp:72
MidiInterface::endRpn
void endRpn(Channel inChannel)
Terminate an RPN frame. This will send a Null Function to deselect the currently selected RPN.
Definition: MIDI.hpp:589
Message::data2
DataByte data2
Definition: midi_Message.h:80
MidiInterface::setThruFilterMode
void setThruFilterMode(Thru::Mode inThruFilterMode)
Set the filter for thru mirroring.
Definition: MIDI.hpp:1352
MidiInterface::getInputChannel
Channel getInputChannel() const
Definition: MIDI.hpp:1195
SongSelect
@ SongSelect
System Common - Song Select.
Definition: midi_Defs.h:108
ActiveSensing
@ ActiveSensing
System Real Time - Active Sensing.
Definition: midi_Defs.h:120
Message::type
MidiType type
Definition: midi_Message.h:69
MidiType
MidiType
Definition: midi_Defs.h:95
Thru::DifferentChannel
@ DifferentChannel
All the messages but the ones on the Input Channel will be sent back.
Definition: midi_Defs.h:134
BEGIN_MIDI_NAMESPACE
#define BEGIN_MIDI_NAMESPACE
Definition: midi_Namespace.h:31
PitchBend
@ PitchBend
Channel Message - Pitch Bend.
Definition: midi_Defs.h:103
Message::channel
Channel channel
Definition: midi_Message.h:64
Channel
byte Channel
Definition: midi_Defs.h:58
MIDI_CHANNEL_OMNI
#define MIDI_CHANNEL_OMNI
Definition: midi_Defs.h:43
MidiInterface::sendTuneRequest
void sendTuneRequest()
Send a Tune Request message.
Definition: MIDI.hpp:388
Thru::Full
@ Full
Fully enabled Thru (every incoming message is sent back).
Definition: midi_Defs.h:132
Stop
@ Stop
System Real Time - Stop.
Definition: midi_Defs.h:118
Tick
@ Tick
System Real Time - Timing Tick (1 tick = 10 milliseconds)
Definition: midi_Defs.h:115
ControlChange
@ ControlChange
Channel Message - Control Change / Channel Mode.
Definition: midi_Defs.h:100
Thru
Definition: midi_Defs.h:128
NRPNLSB
@ NRPNLSB
Non-Registered Parameter Number (LSB)
Definition: midi_Defs.h:202
SystemExclusiveEnd
@ SystemExclusiveEnd
System Exclusive End.
Definition: midi_Defs.h:112
Message::data1
DataByte data1
Definition: midi_Message.h:74
MidiInterface::sendNrpnIncrement
void sendNrpnIncrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:648
SongPosition
@ SongPosition
System Common - Song Position Pointer.
Definition: midi_Defs.h:107
MidiInterface::getChannel
Channel getChannel() const
Get the channel of the message stored in the structure.
Definition: MIDI.hpp:1145
RPNMSB
@ RPNMSB
Registered Parameter Number (MSB)
Definition: midi_Defs.h:205
MidiInterface::read
bool read()
Read messages from the serial port using the main input channel.
Definition: MIDI.hpp:704
MidiInterface::sendRpnIncrement
void sendRpnIncrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:568
MidiInterface::sendSysEx
void sendSysEx(unsigned inLength, const byte *inArray, bool inArrayContainsBoundaries=false)
Generate and send a System Exclusive frame.
Definition: MIDI.hpp:357
MidiInterface::getChannelFromStatusByte
static Channel getChannelFromStatusByte(byte inStatus)
Returns channel in the range 1-16.
Definition: MIDI.hpp:1236
MidiInterface::sendPitchBend
void sendPitchBend(int inPitchValue, Channel inChannel)
Send a Pitch Bend message using a signed integer value.
Definition: MIDI.hpp:324
DataDecrement
@ DataDecrement
Definition: midi_Defs.h:201
AfterTouchChannel
@ AfterTouchChannel
Channel Message - Channel (monophonic) AfterTouch.
Definition: midi_Defs.h:102
Message< Settings::SysExMaxSize >
MIDI_PITCHBEND_MAX
#define MIDI_PITCHBEND_MAX
Definition: midi_Defs.h:47
NRPNMSB
@ NRPNMSB
Non-Registered Parameter Number (MSB)
Definition: midi_Defs.h:203
DataEntryMSB
@ DataEntryMSB
Definition: midi_Defs.h:153
DataByte
byte DataByte
Definition: midi_Defs.h:57