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