Arduino MIDI Library  Version 4.3.1
MIDI.hpp
Go to the documentation of this file.
1 
28 #pragma once
29 
31 
33 template<class SerialPort, class Settings>
35  : mSerial(inSerial)
36  , mInputChannel(0)
37  , mRunningStatus_RX(InvalidType)
38  , mRunningStatus_TX(InvalidType)
39  , mPendingMessageExpectedLenght(0)
40  , mPendingMessageIndex(0)
41  , mCurrentRpnNumber(0xffff)
42  , mCurrentNrpnNumber(0xffff)
43  , mThruActivated(true)
44  , mThruFilterMode(Thru::Full)
45 {
46  mNoteOffCallback = 0;
47  mNoteOnCallback = 0;
48  mAfterTouchPolyCallback = 0;
49  mControlChangeCallback = 0;
50  mProgramChangeCallback = 0;
51  mAfterTouchChannelCallback = 0;
52  mPitchBendCallback = 0;
53  mSystemExclusiveCallback = 0;
54  mTimeCodeQuarterFrameCallback = 0;
55  mSongPositionCallback = 0;
56  mSongSelectCallback = 0;
57  mTuneRequestCallback = 0;
58  mClockCallback = 0;
59  mStartCallback = 0;
60  mContinueCallback = 0;
61  mStopCallback = 0;
62  mActiveSensingCallback = 0;
63  mSystemResetCallback = 0;
64 }
65 
70 template<class SerialPort, class Settings>
72 {
73 }
74 
75 // -----------------------------------------------------------------------------
76 
83 template<class SerialPort, class Settings>
85 {
86  // Initialise the Serial port
87 #if defined(FSE_AVR)
88  mSerial. template open<Settings::BaudRate>();
89 #else
90  mSerial.begin(Settings::BaudRate);
91 #endif
92 
93  mInputChannel = inChannel;
94  mRunningStatus_TX = InvalidType;
95  mRunningStatus_RX = InvalidType;
96 
97  mPendingMessageIndex = 0;
98  mPendingMessageExpectedLenght = 0;
99 
100  mCurrentRpnNumber = 0xffff;
101  mCurrentNrpnNumber = 0xffff;
102 
103  mMessage.valid = false;
104  mMessage.type = InvalidType;
105  mMessage.channel = 0;
106  mMessage.data1 = 0;
107  mMessage.data2 = 0;
108 
109  mThruFilterMode = Thru::Full;
110  mThruActivated = true;
111 }
112 
113 // -----------------------------------------------------------------------------
114 // Output
115 // -----------------------------------------------------------------------------
116 
132 template<class SerialPort, class Settings>
134  DataByte inData1,
135  DataByte inData2,
136  Channel inChannel)
137 {
138  // Then test if channel is valid
139  if (inChannel >= MIDI_CHANNEL_OFF ||
140  inChannel == MIDI_CHANNEL_OMNI ||
141  inType < 0x80)
142  {
143  return; // Don't send anything
144  }
145 
146  if (inType <= PitchBend) // Channel messages
147  {
148  // Protection: remove MSBs on data
149  inData1 &= 0x7f;
150  inData2 &= 0x7f;
151 
152  const StatusByte status = getStatus(inType, inChannel);
153 
154  if (Settings::UseRunningStatus)
155  {
156  if (mRunningStatus_TX != status)
157  {
158  // New message, memorise and send header
159  mRunningStatus_TX = status;
160  mSerial.write(mRunningStatus_TX);
161  }
162  }
163  else
164  {
165  // Don't care about running status, send the status byte.
166  mSerial.write(status);
167  }
168 
169  // Then send data
170  mSerial.write(inData1);
171  if (inType != ProgramChange && inType != AfterTouchChannel)
172  {
173  mSerial.write(inData2);
174  }
175  }
176  else if (inType >= Clock && inType <= SystemReset)
177  {
178  sendRealTime(inType); // System Real-time and 1 byte.
179  }
180 }
181 
182 // -----------------------------------------------------------------------------
183 
193 template<class SerialPort, class Settings>
195  DataByte inVelocity,
196  Channel inChannel)
197 {
198  send(NoteOn, inNoteNumber, inVelocity, inChannel);
199 }
200 
212 template<class SerialPort, class Settings>
214  DataByte inVelocity,
215  Channel inChannel)
216 {
217  send(NoteOff, inNoteNumber, inVelocity, inChannel);
218 }
219 
224 template<class SerialPort, class Settings>
226  Channel inChannel)
227 {
228  send(ProgramChange, inProgramNumber, 0, inChannel);
229 }
230 
237 template<class SerialPort, class Settings>
239  DataByte inControlValue,
240  Channel inChannel)
241 {
242  send(ControlChange, inControlNumber, inControlValue, inChannel);
243 }
244 
252 template<class SerialPort, class Settings>
254  DataByte inPressure,
255  Channel inChannel)
256 {
257  send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
258 }
259 
264 template<class SerialPort, class Settings>
266  Channel inChannel)
267 {
268  send(AfterTouchChannel, inPressure, 0, inChannel);
269 }
270 
277 template<class SerialPort, class Settings>
279  DataByte inPressure,
280  Channel inChannel)
281 {
282  send(AfterTouchPoly, inNoteNumber, inPressure, inChannel);
283 }
284 
291 template<class SerialPort, class Settings>
293  Channel inChannel)
294 {
295  const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN;
296  send(PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
297 }
298 
299 
306 template<class SerialPort, class Settings>
308  Channel inChannel)
309 {
310  const int scale = inPitchValue > 0.0 ? MIDI_PITCHBEND_MAX : MIDI_PITCHBEND_MIN;
311  const int value = int(inPitchValue * double(scale));
312  sendPitchBend(value, inChannel);
313 }
314 
324 template<class SerialPort, class Settings>
326  const byte* inArray,
327  bool inArrayContainsBoundaries)
328 {
329  const bool writeBeginEndBytes = !inArrayContainsBoundaries;
330 
331  if (writeBeginEndBytes)
332  {
333  mSerial.write(0xf0);
334  }
335 
336  for (unsigned i = 0; i < inLength; ++i)
337  {
338  mSerial.write(inArray[i]);
339  }
340 
341  if (writeBeginEndBytes)
342  {
343  mSerial.write(0xf7);
344  }
345 
346  if (Settings::UseRunningStatus)
347  {
348  mRunningStatus_TX = InvalidType;
349  }
350 }
351 
357 template<class SerialPort, class Settings>
359 {
360  mSerial.write(TuneRequest);
361 
362  if (Settings::UseRunningStatus)
363  {
364  mRunningStatus_TX = InvalidType;
365  }
366 }
367 
374 template<class SerialPort, class Settings>
376  DataByte inValuesNibble)
377 {
378  const byte data = (((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f));
380 }
381 
388 template<class SerialPort, class Settings>
390 {
391  mSerial.write((byte)TimeCodeQuarterFrame);
392  mSerial.write(inData);
393 
394  if (Settings::UseRunningStatus)
395  {
396  mRunningStatus_TX = InvalidType;
397  }
398 }
399 
403 template<class SerialPort, class Settings>
405 {
406  mSerial.write((byte)SongPosition);
407  mSerial.write(inBeats & 0x7f);
408  mSerial.write((inBeats >> 7) & 0x7f);
409 
410  if (Settings::UseRunningStatus)
411  {
412  mRunningStatus_TX = InvalidType;
413  }
414 }
415 
417 template<class SerialPort, class Settings>
419 {
420  mSerial.write((byte)SongSelect);
421  mSerial.write(inSongNumber & 0x7f);
422 
423  if (Settings::UseRunningStatus)
424  {
425  mRunningStatus_TX = InvalidType;
426  }
427 }
428 
435 template<class SerialPort, class Settings>
437 {
438  // Do not invalidate Running Status for real-time messages
439  // as they can be interleaved within any message.
440 
441  switch (inType)
442  {
443  case Clock:
444  case Start:
445  case Stop:
446  case Continue:
447  case ActiveSensing:
448  case SystemReset:
449  mSerial.write((byte)inType);
450  break;
451  default:
452  // Invalid Real Time marker
453  break;
454  }
455 }
456 
461 template<class SerialPort, class Settings>
462 inline void MidiInterface<SerialPort, Settings>::beginRpn(unsigned inNumber,
463  Channel inChannel)
464 {
465  if (mCurrentRpnNumber != inNumber)
466  {
467  const byte numMsb = 0x7f & (inNumber >> 7);
468  const byte numLsb = 0x7f & inNumber;
469  sendControlChange(RPNLSB, numLsb, inChannel);
470  sendControlChange(RPNMSB, numMsb, inChannel);
471  mCurrentRpnNumber = inNumber;
472  }
473 }
474 
479 template<class SerialPort, class Settings>
481  Channel inChannel)
482 {;
483  const byte valMsb = 0x7f & (inValue >> 7);
484  const byte valLsb = 0x7f & inValue;
485  sendControlChange(DataEntryMSB, valMsb, inChannel);
486  sendControlChange(DataEntryLSB, valLsb, inChannel);
487 }
488 
494 template<class SerialPort, class Settings>
496  byte inLsb,
497  Channel inChannel)
498 {
499  sendControlChange(DataEntryMSB, inMsb, inChannel);
500  sendControlChange(DataEntryLSB, inLsb, inChannel);
501 }
502 
503 /* \brief Increment the value of the currently selected RPN number by the specified amount.
504  \param inAmount The amount to add to the currently selected RPN value.
505 */
506 template<class SerialPort, class Settings>
508  Channel inChannel)
509 {
510  sendControlChange(DataIncrement, inAmount, inChannel);
511 }
512 
513 /* \brief Decrement the value of the currently selected RPN number by the specified amount.
514  \param inAmount The amount to subtract to the currently selected RPN value.
515 */
516 template<class SerialPort, class Settings>
518  Channel inChannel)
519 {
520  sendControlChange(DataDecrement, inAmount, inChannel);
521 }
522 
527 template<class SerialPort, class Settings>
529 {
530  sendControlChange(RPNLSB, 0x7f, inChannel);
531  sendControlChange(RPNMSB, 0x7f, inChannel);
532  mCurrentRpnNumber = 0xffff;
533 }
534 
535 
536 
541 template<class SerialPort, class Settings>
543  Channel inChannel)
544 {
545  if (mCurrentNrpnNumber != inNumber)
546  {
547  const byte numMsb = 0x7f & (inNumber >> 7);
548  const byte numLsb = 0x7f & inNumber;
549  sendControlChange(NRPNLSB, numLsb, inChannel);
550  sendControlChange(NRPNMSB, numMsb, inChannel);
551  mCurrentNrpnNumber = inNumber;
552  }
553 }
554 
559 template<class SerialPort, class Settings>
561  Channel inChannel)
562 {;
563  const byte valMsb = 0x7f & (inValue >> 7);
564  const byte valLsb = 0x7f & inValue;
565  sendControlChange(DataEntryMSB, valMsb, inChannel);
566  sendControlChange(DataEntryLSB, valLsb, inChannel);
567 }
568 
574 template<class SerialPort, class Settings>
576  byte inLsb,
577  Channel inChannel)
578 {
579  sendControlChange(DataEntryMSB, inMsb, inChannel);
580  sendControlChange(DataEntryLSB, inLsb, inChannel);
581 }
582 
583 /* \brief Increment the value of the currently selected NRPN number by the specified amount.
584  \param inAmount The amount to add to the currently selected NRPN value.
585 */
586 template<class SerialPort, class Settings>
588  Channel inChannel)
589 {
590  sendControlChange(DataIncrement, inAmount, inChannel);
591 }
592 
593 /* \brief Decrement the value of the currently selected NRPN number by the specified amount.
594  \param inAmount The amount to subtract to the currently selected NRPN value.
595 */
596 template<class SerialPort, class Settings>
598  Channel inChannel)
599 {
600  sendControlChange(DataDecrement, inAmount, inChannel);
601 }
602 
607 template<class SerialPort, class Settings>
609 {
610  sendControlChange(NRPNLSB, 0x7f, inChannel);
611  sendControlChange(NRPNMSB, 0x7f, inChannel);
612  mCurrentNrpnNumber = 0xffff;
613 }
614  // End of doc group MIDI Output
616 
617 // -----------------------------------------------------------------------------
618 
619 template<class SerialPort, class Settings>
621  Channel inChannel) const
622 {
623  return ((byte)inType | ((inChannel - 1) & 0x0f));
624 }
625 
626 // -----------------------------------------------------------------------------
627 // Input
628 // -----------------------------------------------------------------------------
629 
642 template<class SerialPort, class Settings>
644 {
645  return read(mInputChannel);
646 }
647 
650 template<class SerialPort, class Settings>
652 {
653  if (inChannel >= MIDI_CHANNEL_OFF)
654  return false; // MIDI Input disabled.
655 
656  if (!parse())
657  return false;
658 
659  handleNullVelocityNoteOnAsNoteOff();
660  const bool channelMatch = inputFilter(inChannel);
661 
662  if (channelMatch)
663  {
664  launchCallback();
665  }
666 
667  thruFilter(inChannel);
668 
669  return channelMatch;
670 }
671 
672 // -----------------------------------------------------------------------------
673 
674 // Private method: MIDI parser
675 template<class SerialPort, class Settings>
677 {
678  if (mSerial.available() == 0)
679  // No data available.
680  return false;
681 
682  // Parsing algorithm:
683  // Get a byte from the serial buffer.
684  // If there is no pending message to be recomposed, start a new one.
685  // - Find type and channel (if pertinent)
686  // - Look for other bytes in buffer, call parser recursively,
687  // until the message is assembled or the buffer is empty.
688  // Else, add the extracted byte to the pending message, and check validity.
689  // When the message is done, store it.
690 
691  const byte extracted = mSerial.read();
692 
693  // Ignore Undefined
694  if (extracted == 0xf9 || extracted == 0xfd)
695  {
696  if (Settings::Use1ByteParsing)
697  {
698  return false;
699  }
700  else
701  {
702  return parse();
703  }
704  }
705 
706  if (mPendingMessageIndex == 0)
707  {
708  // Start a new pending message
709  mPendingMessage[0] = extracted;
710 
711  // Check for running status first
712  if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
713  {
714  // Only these types allow Running Status
715 
716  // If the status byte is not received, prepend it
717  // to the pending message
718  if (extracted < 0x80)
719  {
720  mPendingMessage[0] = mRunningStatus_RX;
721  mPendingMessage[1] = extracted;
722  mPendingMessageIndex = 1;
723  }
724  // Else: well, we received another status byte,
725  // so the running status does not apply here.
726  // It will be updated upon completion of this message.
727  }
728 
729  switch (getTypeFromStatusByte(mPendingMessage[0]))
730  {
731  // 1 byte messages
732  case Start:
733  case Continue:
734  case Stop:
735  case Clock:
736  case ActiveSensing:
737  case SystemReset:
738  case TuneRequest:
739  // Handle the message type directly here.
740  mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
741  mMessage.channel = 0;
742  mMessage.data1 = 0;
743  mMessage.data2 = 0;
744  mMessage.valid = true;
745 
746  // Do not reset all input attributes, Running Status must remain unchanged.
747  // We still need to reset these
748  mPendingMessageIndex = 0;
749  mPendingMessageExpectedLenght = 0;
750 
751  return true;
752  break;
753 
754  // 2 bytes messages
755  case ProgramChange:
756  case AfterTouchChannel:
758  case SongSelect:
759  mPendingMessageExpectedLenght = 2;
760  break;
761 
762  // 3 bytes messages
763  case NoteOn:
764  case NoteOff:
765  case ControlChange:
766  case PitchBend:
767  case AfterTouchPoly:
768  case SongPosition:
769  mPendingMessageExpectedLenght = 3;
770  break;
771 
772  case SystemExclusive:
773  // The message can be any lenght
774  // between 3 and MidiMessage::sSysExMaxSize bytes
775  mPendingMessageExpectedLenght = MidiMessage::sSysExMaxSize;
776  mRunningStatus_RX = InvalidType;
777  mMessage.sysexArray[0] = SystemExclusive;
778  break;
779 
780  case InvalidType:
781  default:
782  // This is obviously wrong. Let's get the hell out'a here.
783  resetInput();
784  return false;
785  break;
786  }
787 
788  if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
789  {
790  // Reception complete
791  mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
792  mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
793  mMessage.data1 = mPendingMessage[1];
794  mMessage.data2 = 0; // Completed new message has 1 data byte
795 
796  mPendingMessageIndex = 0;
797  mPendingMessageExpectedLenght = 0;
798  mMessage.valid = true;
799  return true;
800  }
801  else
802  {
803  // Waiting for more data
804  mPendingMessageIndex++;
805  }
806 
807  if (Settings::Use1ByteParsing)
808  {
809  // Message is not complete.
810  return false;
811  }
812  else
813  {
814  // Call the parser recursively
815  // to parse the rest of the message.
816  return parse();
817  }
818  }
819  else
820  {
821  // First, test if this is a status byte
822  if (extracted >= 0x80)
823  {
824  // Reception of status bytes in the middle of an uncompleted message
825  // are allowed only for interleaved Real Time message or EOX
826  switch (extracted)
827  {
828  case Clock:
829  case Start:
830  case Continue:
831  case Stop:
832  case ActiveSensing:
833  case SystemReset:
834 
835  // Here we will have to extract the one-byte message,
836  // pass it to the structure for being read outside
837  // the MIDI class, and recompose the message it was
838  // interleaved into. Oh, and without killing the running status..
839  // This is done by leaving the pending message as is,
840  // it will be completed on next calls.
841 
842  mMessage.type = (MidiType)extracted;
843  mMessage.data1 = 0;
844  mMessage.data2 = 0;
845  mMessage.channel = 0;
846  mMessage.valid = true;
847  return true;
848 
849  // End of Exclusive
850  case 0xf7:
851  if (mMessage.sysexArray[0] == SystemExclusive)
852  {
853  // Store the last byte (EOX)
854  mMessage.sysexArray[mPendingMessageIndex++] = 0xf7;
855  mMessage.type = SystemExclusive;
856 
857  // Get length
858  mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
859  mMessage.data2 = mPendingMessageIndex >> 8; // MSB
860  mMessage.channel = 0;
861  mMessage.valid = true;
862 
863  resetInput();
864  return true;
865  }
866  else
867  {
868  // Well well well.. error.
869  resetInput();
870  return false;
871  }
872 
873  default:
874  break; // LCOV_EXCL_LINE - Coverage blind spot
875  }
876  }
877 
878  // Add extracted data byte to pending message
879  if (mPendingMessage[0] == SystemExclusive)
880  mMessage.sysexArray[mPendingMessageIndex] = extracted;
881  else
882  mPendingMessage[mPendingMessageIndex] = extracted;
883 
884  // Now we are going to check if we have reached the end of the message
885  if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
886  {
887  // "FML" case: fall down here with an overflown SysEx..
888  // This means we received the last possible data byte that can fit
889  // the buffer. If this happens, try increasing MidiMessage::sSysExMaxSize.
890  if (mPendingMessage[0] == SystemExclusive)
891  {
892  resetInput();
893  return false;
894  }
895 
896  mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
897 
898  if (isChannelMessage(mMessage.type))
899  mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
900  else
901  mMessage.channel = 0;
902 
903  mMessage.data1 = mPendingMessage[1];
904 
905  // Save data2 only if applicable
906  mMessage.data2 = mPendingMessageExpectedLenght == 3 ? mPendingMessage[2] : 0;
907 
908  // Reset local variables
909  mPendingMessageIndex = 0;
910  mPendingMessageExpectedLenght = 0;
911 
912  mMessage.valid = true;
913 
914  // Activate running status (if enabled for the received type)
915  switch (mMessage.type)
916  {
917  case NoteOff:
918  case NoteOn:
919  case AfterTouchPoly:
920  case ControlChange:
921  case ProgramChange:
922  case AfterTouchChannel:
923  case PitchBend:
924  // Running status enabled: store it from received message
925  mRunningStatus_RX = mPendingMessage[0];
926  break;
927 
928  default:
929  // No running status
930  mRunningStatus_RX = InvalidType;
931  break;
932  }
933  return true;
934  }
935  else
936  {
937  // Then update the index of the pending message.
938  mPendingMessageIndex++;
939 
940  if (Settings::Use1ByteParsing)
941  {
942  // Message is not complete.
943  return false;
944  }
945  else
946  {
947  // Call the parser recursively to parse the rest of the message.
948  return parse();
949  }
950  }
951  }
952 }
953 
954 // Private method, see midi_Settings.h for documentation
955 template<class SerialPort, class Settings>
957 {
958  if (Settings::HandleNullVelocityNoteOnAsNoteOff &&
959  getType() == NoteOn && getData2() == 0)
960  {
961  mMessage.type = NoteOff;
962  }
963 }
964 
965 // Private method: check if the received message is on the listened channel
966 template<class SerialPort, class Settings>
968 {
969  // This method handles recognition of channel
970  // (to know if the message is destinated to the Arduino)
971 
972  // First, check if the received message is Channel
973  if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
974  {
975  // Then we need to know if we listen to it
976  if ((mMessage.channel == inChannel) ||
977  (inChannel == MIDI_CHANNEL_OMNI))
978  {
979  return true;
980  }
981  else
982  {
983  // We don't listen to this channel
984  return false;
985  }
986  }
987  else
988  {
989  // System messages are always received
990  return true;
991  }
992 }
993 
994 // Private method: reset input attributes
995 template<class SerialPort, class Settings>
997 {
998  mPendingMessageIndex = 0;
999  mPendingMessageExpectedLenght = 0;
1000  mRunningStatus_RX = InvalidType;
1001 }
1002 
1003 // -----------------------------------------------------------------------------
1004 
1009 template<class SerialPort, class Settings>
1011 {
1012  return mMessage.type;
1013 }
1014 
1020 template<class SerialPort, class Settings>
1022 {
1023  return mMessage.channel;
1024 }
1025 
1027 template<class SerialPort, class Settings>
1029 {
1030  return mMessage.data1;
1031 }
1032 
1034 template<class SerialPort, class Settings>
1036 {
1037  return mMessage.data2;
1038 }
1039 
1044 template<class SerialPort, class Settings>
1046 {
1047  return mMessage.sysexArray;
1048 }
1049 
1055 template<class SerialPort, class Settings>
1057 {
1058  return mMessage.getSysExSize();
1059 }
1060 
1062 template<class SerialPort, class Settings>
1064 {
1065  return mMessage.valid;
1066 }
1067 
1068 // -----------------------------------------------------------------------------
1069 
1070 template<class SerialPort, class Settings>
1072 {
1073  return mInputChannel;
1074 }
1075 
1080 template<class SerialPort, class Settings>
1082 {
1083  mInputChannel = inChannel;
1084 }
1085 
1086 // -----------------------------------------------------------------------------
1087 
1093 template<class SerialPort, class Settings>
1095 {
1096  if ((inStatus < 0x80) ||
1097  (inStatus == 0xf4) ||
1098  (inStatus == 0xf5) ||
1099  (inStatus == 0xf9) ||
1100  (inStatus == 0xfD))
1101  {
1102  // Data bytes and undefined.
1103  return InvalidType;
1104  }
1105  if (inStatus < 0xf0)
1106  {
1107  // Channel message, remove channel nibble.
1108  return MidiType(inStatus & 0xf0);
1109  }
1110 
1111  return MidiType(inStatus);
1112 }
1113 
1116 template<class SerialPort, class Settings>
1118 {
1119  return (inStatus & 0x0f) + 1;
1120 }
1121 
1122 template<class SerialPort, class Settings>
1124 {
1125  return (inType == NoteOff ||
1126  inType == NoteOn ||
1127  inType == ControlChange ||
1128  inType == AfterTouchPoly ||
1129  inType == AfterTouchChannel ||
1130  inType == PitchBend ||
1131  inType == ProgramChange);
1132 }
1133 
1134 // -----------------------------------------------------------------------------
1135 
1140 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; }
1141 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; }
1142 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; }
1143 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; }
1144 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; }
1145 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; }
1146 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; }
1147 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSystemExclusive(void (*fptr)(byte* array, unsigned size)) { mSystemExclusiveCallback = fptr; }
1148 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; }
1149 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; }
1150 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; }
1151 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; }
1152 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; }
1153 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; }
1154 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; }
1155 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; }
1156 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; }
1157 template<class SerialPort, class Settings> void MidiInterface<SerialPort, Settings>::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; }
1158 
1165 template<class SerialPort, class Settings>
1167 {
1168  switch (inType)
1169  {
1170  case NoteOff: mNoteOffCallback = 0; break;
1171  case NoteOn: mNoteOnCallback = 0; break;
1172  case AfterTouchPoly: mAfterTouchPolyCallback = 0; break;
1173  case ControlChange: mControlChangeCallback = 0; break;
1174  case ProgramChange: mProgramChangeCallback = 0; break;
1175  case AfterTouchChannel: mAfterTouchChannelCallback = 0; break;
1176  case PitchBend: mPitchBendCallback = 0; break;
1177  case SystemExclusive: mSystemExclusiveCallback = 0; break;
1178  case TimeCodeQuarterFrame: mTimeCodeQuarterFrameCallback = 0; break;
1179  case SongPosition: mSongPositionCallback = 0; break;
1180  case SongSelect: mSongSelectCallback = 0; break;
1181  case TuneRequest: mTuneRequestCallback = 0; break;
1182  case Clock: mClockCallback = 0; break;
1183  case Start: mStartCallback = 0; break;
1184  case Continue: mContinueCallback = 0; break;
1185  case Stop: mStopCallback = 0; break;
1186  case ActiveSensing: mActiveSensingCallback = 0; break;
1187  case SystemReset: mSystemResetCallback = 0; break;
1188  default:
1189  break;
1190  }
1191 }
1192  // End of doc group MIDI Callbacks
1194 
1195 // Private - launch callback function based on received type.
1196 template<class SerialPort, class Settings>
1198 {
1199  // The order is mixed to allow frequent messages to trigger their callback faster.
1200  switch (mMessage.type)
1201  {
1202  // Notes
1203  case NoteOff: if (mNoteOffCallback != 0) mNoteOffCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1204  case NoteOn: if (mNoteOnCallback != 0) mNoteOnCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1205 
1206  // Real-time messages
1207  case Clock: if (mClockCallback != 0) mClockCallback(); break;
1208  case Start: if (mStartCallback != 0) mStartCallback(); break;
1209  case Continue: if (mContinueCallback != 0) mContinueCallback(); break;
1210  case Stop: if (mStopCallback != 0) mStopCallback(); break;
1211  case ActiveSensing: if (mActiveSensingCallback != 0) mActiveSensingCallback(); break;
1212 
1213  // Continuous controllers
1214  case ControlChange: if (mControlChangeCallback != 0) mControlChangeCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1215  case PitchBend: if (mPitchBendCallback != 0) mPitchBendCallback(mMessage.channel, (int)((mMessage.data1 & 0x7f) | ((mMessage.data2 & 0x7f) << 7)) + MIDI_PITCHBEND_MIN); break; // TODO: check this
1216  case AfterTouchPoly: if (mAfterTouchPolyCallback != 0) mAfterTouchPolyCallback(mMessage.channel, mMessage.data1, mMessage.data2); break;
1217  case AfterTouchChannel: if (mAfterTouchChannelCallback != 0) mAfterTouchChannelCallback(mMessage.channel, mMessage.data1); break;
1218 
1219  case ProgramChange: if (mProgramChangeCallback != 0) mProgramChangeCallback(mMessage.channel, mMessage.data1); break;
1220  case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.getSysExSize()); break;
1221 
1222  // Occasional messages
1223  case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break;
1224  case SongPosition: if (mSongPositionCallback != 0) mSongPositionCallback((mMessage.data1 & 0x7f) | ((mMessage.data2 & 0x7f) << 7)); break;
1225  case SongSelect: if (mSongSelectCallback != 0) mSongSelectCallback(mMessage.data1); break;
1226  case TuneRequest: if (mTuneRequestCallback != 0) mTuneRequestCallback(); break;
1227 
1228  case SystemReset: if (mSystemResetCallback != 0) mSystemResetCallback(); break;
1229 
1230  case InvalidType:
1231  default:
1232  break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
1233  }
1234 }
1235  // End of doc group MIDI Input
1237 
1238 // -----------------------------------------------------------------------------
1239 // Thru
1240 // -----------------------------------------------------------------------------
1241 
1251 template<class SerialPort, class Settings>
1253 {
1254  mThruFilterMode = inThruFilterMode;
1255  mThruActivated = mThruFilterMode != Thru::Off;
1256 }
1257 
1258 template<class SerialPort, class Settings>
1260 {
1261  return mThruFilterMode;
1262 }
1263 
1264 template<class SerialPort, class Settings>
1266 {
1267  return mThruActivated;
1268 }
1269 
1270 template<class SerialPort, class Settings>
1272 {
1273  mThruActivated = true;
1274  mThruFilterMode = inThruFilterMode;
1275 }
1276 
1277 template<class SerialPort, class Settings>
1279 {
1280  mThruActivated = false;
1281  mThruFilterMode = Thru::Off;
1282 }
1283  // End of doc group MIDI Thru
1285 
1286 // This method is called upon reception of a message
1287 // and takes care of Thru filtering and sending.
1288 // - All system messages (System Exclusive, Common and Real Time) are passed
1289 // to output unless filter is set to Off.
1290 // - Channel messages are passed to the output whether their channel
1291 // is matching the input channel and the filter setting
1292 template<class SerialPort, class Settings>
1294 {
1295  // If the feature is disabled, don't do anything.
1296  if (!mThruActivated || (mThruFilterMode == Thru::Off))
1297  return;
1298 
1299  // First, check if the received message is Channel
1300  if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
1301  {
1302  const bool filter_condition = ((mMessage.channel == inChannel) ||
1303  (inChannel == MIDI_CHANNEL_OMNI));
1304 
1305  // Now let's pass it to the output
1306  switch (mThruFilterMode)
1307  {
1308  case Thru::Full:
1309  send(mMessage.type,
1310  mMessage.data1,
1311  mMessage.data2,
1312  mMessage.channel);
1313  break;
1314 
1315  case Thru::SameChannel:
1316  if (filter_condition)
1317  {
1318  send(mMessage.type,
1319  mMessage.data1,
1320  mMessage.data2,
1321  mMessage.channel);
1322  }
1323  break;
1324 
1326  if (!filter_condition)
1327  {
1328  send(mMessage.type,
1329  mMessage.data1,
1330  mMessage.data2,
1331  mMessage.channel);
1332  }
1333  break;
1334 
1335  default:
1336  break;
1337  }
1338  }
1339  else
1340  {
1341  // Send the message to the output
1342  switch (mMessage.type)
1343  {
1344  // Real Time and 1 byte
1345  case Clock:
1346  case Start:
1347  case Stop:
1348  case Continue:
1349  case ActiveSensing:
1350  case SystemReset:
1351  case TuneRequest:
1352  sendRealTime(mMessage.type);
1353  break;
1354 
1355  case SystemExclusive:
1356  // Send SysEx (0xf0 and 0xf7 are included in the buffer)
1358  break;
1359 
1360  case SongSelect:
1361  sendSongSelect(mMessage.data1);
1362  break;
1363 
1364  case SongPosition:
1365  sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2 << 7));
1366  break;
1367 
1368  case TimeCodeQuarterFrame:
1369  sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
1370  break;
1371 
1372  default:
1373  break; // LCOV_EXCL_LINE - Unreacheable code, but prevents unhandled case warning.
1374  }
1375  }
1376 }
1377 
void setHandleProgramChange(void(*fptr)(byte channel, byte number))
Definition: MIDI.hpp:1144
System Common - Song Position Pointer.
Definition: midi_Defs.h:77
void disconnectCallbackFromType(MidiType inType)
Detach an external function from the given type.
Definition: MIDI.hpp:1166
Polyphonic AfterTouch.
Definition: midi_Defs.h:70
System Common - Song Select.
Definition: midi_Defs.h:78
void setHandleNoteOn(void(*fptr)(byte channel, byte note, byte velocity))
Definition: MIDI.hpp:1141
void sendSongSelect(DataByte inSongNumber)
Send a Song Select message.
Definition: MIDI.hpp:418
void setThruFilterMode(Thru::Mode inThruFilterMode)
Set the filter for thru mirroring.
Definition: MIDI.hpp:1252
void sendSongPosition(unsigned inBeats)
Send a Song Position Pointer message.
Definition: MIDI.hpp:404
Non-Registered Parameter Number (MSB)
Definition: midi_Defs.h:178
void turnThruOn(Thru::Mode inThruFilterMode=Thru::Full)
Definition: MIDI.hpp:1271
#define MIDI_PITCHBEND_MAX
Definition: midi_Defs.h:52
DataByte data1
Definition: midi_Message.h:71
System Exclusive.
Definition: midi_Defs.h:75
System Real Time - Stop.
Definition: midi_Defs.h:83
void beginRpn(unsigned inNumber, Channel inChannel)
Start a Registered Parameter Number frame.
Definition: MIDI.hpp:462
static const unsigned sSysExMaxSize
Definition: midi_Message.h:56
System Real Time - Timing Clock.
Definition: midi_Defs.h:80
void sendProgramChange(DataByte inProgramNumber, Channel inChannel)
Send a Program Change message.
Definition: MIDI.hpp:225
MidiType getType() const
Get the last received message&#39;s type.
Definition: MIDI.hpp:1010
DataByte data2
Definition: midi_Message.h:77
MidiType type
Definition: midi_Message.h:66
DataByte getData2() const
Get the second data byte of the last received message.
Definition: MIDI.hpp:1035
byte StatusByte
Definition: midi_Defs.h:57
#define BEGIN_MIDI_NAMESPACE
Mode
Definition: midi_Defs.h:93
System Common - MIDI Time Code Quarter Frame.
Definition: midi_Defs.h:76
Thru disabled (nothing passes through).
Definition: midi_Defs.h:95
#define MIDI_PITCHBEND_MIN
Definition: midi_Defs.h:51
DataByte sysexArray[sSysExMaxSize]
Definition: midi_Message.h:83
Channel getChannel() const
Get the channel of the message stored in the structure.
Definition: MIDI.hpp:1021
void setHandleSystemExclusive(void(*fptr)(byte *array, unsigned size))
Definition: MIDI.hpp:1147
#define END_MIDI_NAMESPACE
For notifying errors.
Definition: midi_Defs.h:67
void setHandleSongSelect(void(*fptr)(byte songnumber))
Definition: MIDI.hpp:1150
unsigned getSysExArrayLength() const
Get the lenght of the System Exclusive array.
Definition: MIDI.hpp:1056
Only the messages on the Input Channel will be sent back.
Definition: midi_Defs.h:97
bool read()
Read messages from the serial port using the main input channel.
Definition: MIDI.hpp:643
DataByte getData1() const
Get the first data byte of the last received message.
Definition: MIDI.hpp:1028
void setHandleClock(void(*fptr)(void))
Definition: MIDI.hpp:1152
Channel (monophonic) AfterTouch.
Definition: midi_Defs.h:73
Program Change.
Definition: midi_Defs.h:72
void beginNrpn(unsigned inNumber, Channel inChannel)
Start a Non-Registered Parameter Number frame.
Definition: MIDI.hpp:542
Non-Registered Parameter Number (LSB)
Definition: midi_Defs.h:177
void begin(Channel inChannel=1)
Call the begin method in the setup() function of the Arduino.
Definition: MIDI.hpp:84
void sendRealTime(MidiType inType)
Send a Real Time (one byte) message.
Definition: MIDI.hpp:436
static Channel getChannelFromStatusByte(byte inStatus)
Returns channel in the range 1-16.
Definition: MIDI.hpp:1117
void setHandleStart(void(*fptr)(void))
Definition: MIDI.hpp:1153
byte Channel
Definition: midi_Defs.h:59
System Real Time - Continue.
Definition: midi_Defs.h:82
void endRpn(Channel inChannel)
Terminate an RPN frame. This will send a Null Function to deselect the currently selected RPN...
Definition: MIDI.hpp:528
#define MIDI_CHANNEL_OMNI
Definition: midi_Defs.h:48
void setInputChannel(Channel inChannel)
Set the value for the input MIDI channel.
Definition: MIDI.hpp:1081
void setHandleSystemReset(void(*fptr)(void))
Definition: MIDI.hpp:1157
System Real Time - Start.
Definition: midi_Defs.h:81
bool getThruState() const
Definition: MIDI.hpp:1265
Channel getInputChannel() const
Definition: MIDI.hpp:1071
void endNrpn(Channel inChannel)
Terminate an NRPN frame. This will send a Null Function to deselect the currently selected NRPN...
Definition: MIDI.hpp:608
Registered Parameter Number (MSB)
Definition: midi_Defs.h:180
void turnThruOff()
Definition: MIDI.hpp:1278
void sendAfterTouch(DataByte inPressure, Channel inChannel)
Send a MonoPhonic AfterTouch message (applies to all notes)
Definition: MIDI.hpp:265
void sendPolyPressure(DataByte inNoteNumber, DataByte inPressure, Channel inChannel) __attribute__((deprecated))
Send a Polyphonic AfterTouch message (applies to a specified note)
Definition: MIDI.hpp:253
void sendRpnDecrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:517
void sendNrpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected NRPN number.
Definition: MIDI.hpp:560
void sendControlChange(DataByte inControlNumber, DataByte inControlValue, Channel inChannel)
Send a Control Change message.
Definition: MIDI.hpp:238
void sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble)
Send a MIDI Time Code Quarter Frame.
Definition: MIDI.hpp:375
void sendNrpnDecrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:597
void setHandleSongPosition(void(*fptr)(unsigned beats))
Definition: MIDI.hpp:1149
Note On.
Definition: midi_Defs.h:69
MidiType
Definition: midi_Defs.h:65
Control Change / Channel Mode.
Definition: midi_Defs.h:71
Thru::Mode getFilterMode() const
Definition: MIDI.hpp:1259
void sendPitchBend(int inPitchValue, Channel inChannel)
Send a Pitch Bend message using a signed integer value.
Definition: MIDI.hpp:292
System Common - Tune Request.
Definition: midi_Defs.h:79
static MidiType getTypeFromStatusByte(byte inStatus)
Extract an enumerated MIDI type from a status byte.
Definition: MIDI.hpp:1094
void setHandleActiveSensing(void(*fptr)(void))
Definition: MIDI.hpp:1156
Definition: midi_Defs.h:91
MidiInterface(SerialPort &inSerial)
Constructor for MidiInterface.
Definition: MIDI.hpp:34
static bool isChannelMessage(MidiType inType)
Definition: MIDI.hpp:1123
Pitch Bend.
Definition: midi_Defs.h:74
byte DataByte
Definition: midi_Defs.h:58
uint8_t byte
Definition: midi_Defs.h:36
void setHandleTimeCodeQuarterFrame(void(*fptr)(byte data))
Definition: MIDI.hpp:1148
void sendTuneRequest()
Send a Tune Request message.
Definition: MIDI.hpp:358
void setHandleContinue(void(*fptr)(void))
Definition: MIDI.hpp:1154
void sendNrpnIncrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:587
void sendRpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected RPN number.
Definition: MIDI.hpp:480
void send(MidiType inType, DataByte inData1, DataByte inData2, Channel inChannel)
Generate and send a MIDI message from the values given.
Definition: MIDI.hpp:133
bool valid
Definition: midi_Message.h:89
void sendSysEx(unsigned inLength, const byte *inArray, bool inArrayContainsBoundaries=false)
Generate and send a System Exclusive frame.
Definition: MIDI.hpp:325
void setHandleNoteOff(void(*fptr)(byte channel, byte note, byte velocity))
Definition: MIDI.hpp:1140
bool check() const
Check if a valid message is stored in the structure.
Definition: MIDI.hpp:1063
All the messages but the ones on the Input Channel will be sent back.
Definition: midi_Defs.h:98
void setHandlePitchBend(void(*fptr)(byte channel, int bend))
Definition: MIDI.hpp:1146
unsigned getSysExSize() const
Definition: midi_Message.h:91
void setHandleStop(void(*fptr)(void))
Definition: MIDI.hpp:1155
void sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note On message.
Definition: MIDI.hpp:194
const byte * getSysExArray() const
Get the System Exclusive byte array.
Definition: MIDI.hpp:1045
System Real Time - Active Sensing.
Definition: midi_Defs.h:84
Fully enabled Thru (every incoming message is sent back).
Definition: midi_Defs.h:96
void setHandleTuneRequest(void(*fptr)(void))
Definition: MIDI.hpp:1151
Note Off.
Definition: midi_Defs.h:68
System Real Time - System Reset.
Definition: midi_Defs.h:85
#define MIDI_CHANNEL_OFF
Definition: midi_Defs.h:49
~MidiInterface()
Destructor for MidiInterface.
Definition: MIDI.hpp:71
void setHandleControlChange(void(*fptr)(byte channel, byte number, byte value))
Definition: MIDI.hpp:1143
void sendNoteOff(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note Off message.
Definition: MIDI.hpp:213
The main class for MIDI handling. It is templated over the type of serial port to provide abstraction...
Definition: MIDI.h:45
Channel channel
Definition: midi_Message.h:61
void setHandleAfterTouchChannel(void(*fptr)(byte channel, byte pressure))
Definition: MIDI.hpp:1145
Registered Parameter Number (LSB)
Definition: midi_Defs.h:179
void sendRpnIncrement(byte inAmount, Channel inChannel)
Definition: MIDI.hpp:507
void setHandleAfterTouchPoly(void(*fptr)(byte channel, byte note, byte pressure))
Definition: MIDI.hpp:1142