33template<
class Transport,
class Settings,
class Platform>
35 : mTransport(inTransport)
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)
51 mSenderActiveSensingPeriodicity = Settings::SenderActiveSensingPeriodicity;
58template<
class Transport,
class Settings,
class Platform>
71template<
class Transport,
class Settings,
class Platform>
77 mInputChannel = inChannel;
81 mPendingMessageIndex = 0;
82 mPendingMessageExpectedLength = 0;
84 mCurrentRpnNumber = 0xffff;
85 mCurrentNrpnNumber = 0xffff;
87 mLastMessageSentTime = Platform::now();
89 mMessage.valid =
false;
97 mThruActivated = mTransport.thruActivated;
117template<
class Transport,
class Settings,
class Platform>
120 if (!inMessage.
valid)
123 if (mTransport.beginTransmission(inMessage.
type))
125 const StatusByte status = getStatus(inMessage.
type, inMessage.
channel);
126 mTransport.write(status);
130 if (inMessage.
length > 1) mTransport.write(inMessage.
data1);
131 if (inMessage.
length > 2) mTransport.write(inMessage.
data2);
137 for (
size_t i = 0; i < inMessage.getSysExSize(); i++)
143 mTransport.endTransmission();
144 UpdateLastSentTime();
159template<
class Transport,
class Settings,
class Platform>
168 if (inChannel >= MIDI_CHANNEL_OFF ||
169 inChannel == MIDI_CHANNEL_OMNI ||
178 const StatusByte status = getStatus(inType, inChannel);
180 if (mTransport.beginTransmission(inType))
182 if (Settings::UseRunningStatus)
184 if (mRunningStatus_TX != status)
187 mRunningStatus_TX = status;
188 mTransport.write(mRunningStatus_TX);
194 mTransport.write(status);
198 mTransport.write(inData1);
201 mTransport.write(inData2);
204 mTransport.endTransmission();
205 UpdateLastSentTime();
210 sendRealTime(inType);
225template<
class Transport,
class Settings,
class Platform>
230 send(
NoteOn, inNoteNumber, inVelocity, inChannel);
244template<
class Transport,
class Settings,
class Platform>
249 send(
NoteOff, inNoteNumber, inVelocity, inChannel);
256template<
class Transport,
class Settings,
class Platform>
269template<
class Transport,
class Settings,
class Platform>
271 DataByte inControlValue,
274 send(
ControlChange, inControlNumber, inControlValue, inChannel);
284template<
class Transport,
class Settings,
class Platform>
296template<
class Transport,
class Settings,
class Platform>
309template<
class Transport,
class Settings,
class Platform>
323template<
class Transport,
class Settings,
class Platform>
327 const unsigned bend = unsigned(inPitchValue -
int(MIDI_PITCHBEND_MIN));
328 send(
PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
338template<
class Transport,
class Settings,
class Platform>
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);
356template<
class Transport,
class Settings,
class Platform>
359 bool inArrayContainsBoundaries)
361 const bool writeBeginEndBytes = !inArrayContainsBoundaries;
365 if (writeBeginEndBytes)
368 for (
unsigned i = 0; i < inLength; ++i)
369 mTransport.write(inArray[i]);
371 if (writeBeginEndBytes)
374 mTransport.endTransmission();
375 UpdateLastSentTime();
378 if (Settings::UseRunningStatus)
387template<
class Transport,
class Settings,
class Platform>
399template<
class Transport,
class Settings,
class Platform>
401 DataByte inValuesNibble)
403 const byte data = byte((((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f)));
404 sendTimeCodeQuarterFrame(data);
413template<
class Transport,
class Settings,
class Platform>
422template<
class Transport,
class Settings,
class Platform>
429template<
class Transport,
class Settings,
class Platform>
442template<
class Transport,
class Settings,
class Platform>
457 if (mTransport.beginTransmission(inType))
459 mTransport.write((
byte)inType);
463 mTransport.write(inData1);
466 mTransport.write(inData1 & 0x7f);
467 mTransport.write((inData1 >> 7) & 0x7f);
470 mTransport.write(inData1 & 0x7f);
477 mTransport.endTransmission();
478 UpdateLastSentTime();
481 if (Settings::UseRunningStatus)
491template<
class Transport,
class Settings,
class Platform>
505 if (mTransport.beginTransmission(inType))
507 mTransport.write((
byte)inType);
508 mTransport.endTransmission();
509 UpdateLastSentTime();
522template<
class Transport,
class Settings,
class Platform>
526 if (mCurrentRpnNumber != inNumber)
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;
540template<
class Transport,
class Settings,
class Platform>
544 const byte valMsb = 0x7f & (inValue >> 7);
545 const byte valLsb = 0x7f & inValue;
546 sendControlChange(DataEntryMSB, valMsb, inChannel);
547 sendControlChange(DataEntryLSB, valLsb, inChannel);
555template<
class Transport,
class Settings,
class Platform>
560 sendControlChange(DataEntryMSB, inMsb, inChannel);
561 sendControlChange(DataEntryLSB, inLsb, inChannel);
567template<
class Transport,
class Settings,
class Platform>
571 sendControlChange(DataIncrement, inAmount, inChannel);
577template<
class Transport,
class Settings,
class Platform>
581 sendControlChange(DataDecrement, inAmount, inChannel);
588template<
class Transport,
class Settings,
class Platform>
591 sendControlChange(
RPNLSB, 0x7f, inChannel);
592 sendControlChange(
RPNMSB, 0x7f, inChannel);
593 mCurrentRpnNumber = 0xffff;
602template<
class Transport,
class Settings,
class Platform>
606 if (mCurrentNrpnNumber != inNumber)
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;
620template<
class Transport,
class Settings,
class Platform>
624 const byte valMsb = 0x7f & (inValue >> 7);
625 const byte valLsb = 0x7f & inValue;
626 sendControlChange(DataEntryMSB, valMsb, inChannel);
627 sendControlChange(DataEntryLSB, valLsb, inChannel);
635template<
class Transport,
class Settings,
class Platform>
640 sendControlChange(DataEntryMSB, inMsb, inChannel);
641 sendControlChange(DataEntryLSB, inLsb, inChannel);
647template<
class Transport,
class Settings,
class Platform>
651 sendControlChange(DataIncrement, inAmount, inChannel);
657template<
class Transport,
class Settings,
class Platform>
661 sendControlChange(DataDecrement, inAmount, inChannel);
668template<
class Transport,
class Settings,
class Platform>
671 sendControlChange(
NRPNLSB, 0x7f, inChannel);
672 sendControlChange(
NRPNMSB, 0x7f, inChannel);
673 mCurrentNrpnNumber = 0xffff;
680template<
class Transport,
class Settings,
class Platform>
682 Channel inChannel)
const
684 return StatusByte(((
byte)inType | ((inChannel - 1) & 0x0f)));
703template<
class Transport,
class Settings,
class Platform>
706 return read(mInputChannel);
711template<
class Transport,
class Settings,
class Platform>
714 #ifndef RegionActiveSending
723 if (Settings::UseSenderActiveSensing && (mSenderActiveSensingPeriodicity > 0) && (Platform::now() - mLastMessageSentTime) > mSenderActiveSensingPeriodicity)
726 mLastMessageSentTime = Platform::now();
729 if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated && (mLastMessageReceivedTime + ActiveSensingTimeout < Platform::now()))
731 mReceiverActiveSensingActivated =
false;
733 mLastError |= 1UL << ErrorActiveSensingTimeout;
735 mErrorCallback(mLastError);
739 if (inChannel >= MIDI_CHANNEL_OFF)
745 #ifndef RegionActiveSending
747 if (Settings::UseReceiverActiveSensing && mMessage.type ==
ActiveSensing)
751 mReceiverActiveSensingActivated =
true;
754 if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
756 mLastError &= ~(1UL << ErrorActiveSensingTimeout);
758 mErrorCallback(mLastError);
763 if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
764 mLastMessageReceivedTime = Platform::now();
768 handleNullVelocityNoteOnAsNoteOff();
770 const bool channelMatch = inputFilter(inChannel);
774 thruFilter(inChannel);
782template<
class Transport,
class Settings,
class Platform>
785 if (mTransport.available() == 0)
789 mLastError &= ~(1UL << ErrorParse);
800 const byte extracted = mTransport.
read();
803 if (extracted == Undefined_FD)
804 return (Settings::Use1ByteParsing) ? false : parse();
806 if (mPendingMessageIndex == 0)
809 mPendingMessage[0] = extracted;
812 if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
818 if (extracted < 0x80)
820 mPendingMessage[0] = mRunningStatus_RX;
821 mPendingMessage[1] = extracted;
822 mPendingMessageIndex = 1;
829 const MidiType pendingType = getTypeFromStatusByte(mPendingMessage[0]);
843 mMessage.type = pendingType;
844 mMessage.channel = 0;
847 mMessage.valid =
true;
851 mPendingMessageIndex = 0;
852 mPendingMessageExpectedLength = 0;
862 mPendingMessageExpectedLength = 2;
872 mPendingMessageExpectedLength = 3;
879 mPendingMessageExpectedLength = MidiMessage::sSysExMaxSize;
881 mMessage.sysexArray[0] = pendingType;
887 mLastError |= 1UL << ErrorParse;
889 mErrorCallback(mLastError);
896 if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
899 mMessage.type = pendingType;
900 mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
901 mMessage.data1 = mPendingMessage[1];
905 mPendingMessageIndex = 0;
906 mPendingMessageExpectedLength = 0;
907 mMessage.valid =
true;
914 mPendingMessageIndex++;
917 return (Settings::Use1ByteParsing) ? false : parse();
922 if (extracted >= 0x80)
943 mMessage.type = (
MidiType)extracted;
946 mMessage.channel = 0;
948 mMessage.valid =
true;
959 mMessage.sysexArray[mPendingMessageIndex++] = extracted;
963 mMessage.data1 = mPendingMessageIndex & 0xff;
964 mMessage.data2 = byte(mPendingMessageIndex >> 8);
965 mMessage.channel = 0;
966 mMessage.length = mPendingMessageIndex;
967 mMessage.valid =
true;
976 mLastError |= 1UL << ErrorParse;
978 mErrorCallback(mLastError);
992 mMessage.sysexArray[mPendingMessageIndex] = extracted;
994 mPendingMessage[mPendingMessageIndex] = extracted;
997 if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
1007 auto lastByte = mMessage.sysexArray[Settings::SysExMaxSize - 1];
1012 mMessage.data1 = Settings::SysExMaxSize & 0xff;
1013 mMessage.data2 = byte(Settings::SysExMaxSize >> 8);
1014 mMessage.channel = 0;
1015 mMessage.length = Settings::SysExMaxSize;
1016 mMessage.valid =
true;
1023 mMessage.sysexArray[1] = lastByte;
1025 mPendingMessageIndex = 2;
1030 mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
1032 if (isChannelMessage(mMessage.type))
1033 mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
1035 mMessage.channel = 0;
1037 mMessage.data1 = mPendingMessage[1];
1039 mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0;
1042 mPendingMessageIndex = 0;
1043 mPendingMessageExpectedLength = 0;
1045 mMessage.valid =
true;
1048 switch (mMessage.type)
1058 mRunningStatus_RX = mPendingMessage[0];
1071 mPendingMessageIndex++;
1073 return (Settings::Use1ByteParsing) ? false : parse();
1079template<
class Transport,
class Settings,
class Platform>
1082 if (Settings::HandleNullVelocityNoteOnAsNoteOff &&
1083 getType() ==
NoteOn && getData2() == 0)
1090template<
class Transport,
class Settings,
class Platform>
1100 if ((mMessage.channel == inChannel) ||
1101 (inChannel == MIDI_CHANNEL_OMNI))
1119template<
class Transport,
class Settings,
class Platform>
1122 mPendingMessageIndex = 0;
1123 mPendingMessageExpectedLength = 0;
1133template<
class Transport,
class Settings,
class Platform>
1136 return mMessage.type;
1144template<
class Transport,
class Settings,
class Platform>
1147 return mMessage.channel;
1151template<
class Transport,
class Settings,
class Platform>
1154 return mMessage.data1;
1158template<
class Transport,
class Settings,
class Platform>
1161 return mMessage.data2;
1168template<
class Transport,
class Settings,
class Platform>
1171 return mMessage.sysexArray;
1179template<
class Transport,
class Settings,
class Platform>
1182 return mMessage.getSysExSize();
1186template<
class Transport,
class Settings,
class Platform>
1189 return mMessage.valid;
1194template<
class Transport,
class Settings,
class Platform>
1197 return mInputChannel;
1204template<
class Transport,
class Settings,
class Platform>
1207 mInputChannel = inChannel;
1217template<
class Transport,
class Settings,
class Platform>
1220 if ((inStatus < 0x80) ||
1221 (inStatus == Undefined_F4) ||
1222 (inStatus == Undefined_F5) ||
1223 (inStatus == Undefined_FD))
1226 if (inStatus < 0xf0)
1235template<
class Transport,
class Settings,
class Platform>
1238 return Channel((inStatus & 0x0f) + 1);
1241template<
class Transport,
class Settings,
class Platform>
1261template<
class Transport,
class Settings,
class Platform>
1266 case NoteOff: mNoteOffCallback =
nullptr;
break;
1267 case NoteOn: mNoteOnCallback =
nullptr;
break;
1269 case ControlChange: mControlChangeCallback =
nullptr;
break;
1270 case ProgramChange: mProgramChangeCallback =
nullptr;
break;
1272 case PitchBend: mPitchBendCallback =
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;
1293template<
class Transport,
class Settings,
class Platform>
1296 if (mMessageCallback != 0) mMessageCallback(mMessage);
1299 switch (mMessage.type)
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;
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;
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;
1319 case ProgramChange:
if (mProgramChangeCallback !=
nullptr) mProgramChangeCallback(mMessage.channel, mMessage.data1);
break;
1320 case SystemExclusive:
if (mSystemExclusiveCallback !=
nullptr) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.getSysExSize());
break;
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;
1328 case SystemReset:
if (mSystemResetCallback !=
nullptr) mSystemResetCallback();
break;
1351template<
class Transport,
class Settings,
class Platform>
1354 mThruFilterMode = inThruFilterMode;
1355 mThruActivated = mThruFilterMode !=
Thru::Off;
1358template<
class Transport,
class Settings,
class Platform>
1361 return mThruFilterMode;
1364template<
class Transport,
class Settings,
class Platform>
1367 return mThruActivated;
1370template<
class Transport,
class Settings,
class Platform>
1373 mThruActivated =
true;
1374 mThruFilterMode = inThruFilterMode;
1377template<
class Transport,
class Settings,
class Platform>
1380 mThruActivated =
false;
1384template<
class Transport,
class Settings,
class Platform>
1387 if (Settings::UseSenderActiveSensing && mSenderActiveSensingPeriodicity)
1388 mLastMessageSentTime = Platform::now();
1399template<
class Transport,
class Settings,
class Platform>
1403 if (!mThruActivated || (mThruFilterMode ==
Thru::Off))
1409 const bool filter_condition = ((mMessage.channel == inChannel) ||
1410 (inChannel == MIDI_CHANNEL_OMNI));
1413 switch (mThruFilterMode)
1423 if (filter_condition)
1433 if (!filter_condition)
1449 switch (mMessage.type)
1459 sendRealTime(mMessage.type);
1464 sendSysEx(getSysExArrayLength(), getSysExArray(),
true);
1468 sendSongSelect(mMessage.data1);
1472 sendSongPosition(mMessage.data1 | ((
unsigned)mMessage.data2 << 7));
1476 sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
The main class for MIDI handling. It is templated over the type of serial port to provide abstraction...
~MidiInterface()
Destructor for MidiInterface.
MidiInterface(Transport &)
Constructor for MidiInterface.
void begin(Channel inChannel=1)
Call the begin method in the setup() function of the Arduino.
void sendAfterTouch(DataByte inPressure, Channel inChannel)
Send a MonoPhonic AfterTouch message (applies to all notes)
void endRpn(Channel inChannel)
Terminate an RPN frame. This will send a Null Function to deselect the currently selected RPN.
void sendPitchBend(int inPitchValue, Channel inChannel)
Send a Pitch Bend message using a signed integer value.
void sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble)
Send a MIDI Time Code Quarter Frame.
void endNrpn(Channel inChannel)
Terminate an NRPN frame. This will send a Null Function to deselect the currently selected NRPN.
void sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note On message.
void sendTuneRequest()
Send a Tune Request message.
void sendControlChange(DataByte inControlNumber, DataByte inControlValue, Channel inChannel)
Send a Control Change message.
void send(const MidiMessage &)
Send a MIDI message.
void sendProgramChange(DataByte inProgramNumber, Channel inChannel)
Send a Program Change message.
void beginNrpn(unsigned inNumber, Channel inChannel)
Start a Non-Registered Parameter Number frame.
void beginRpn(unsigned inNumber, Channel inChannel)
Start a Registered Parameter Number frame.
void sendSongPosition(unsigned inBeats)
Send a Song Position Pointer message.
void sendPolyPressure(DataByte inNoteNumber, DataByte inPressure, Channel inChannel)
Send a Polyphonic AfterTouch message (applies to a specified note)
void sendSongSelect(DataByte inSongNumber)
Send a Song Select message.
void sendCommon(MidiType inType, unsigned=0)
Send a Common message. Common messages reset the running status.
void sendRpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected RPN number.
void sendRealTime(MidiType inType)
Send a Real Time (one byte) message.
void sendSysEx(unsigned inLength, const byte *inArray, bool inArrayContainsBoundaries=false)
Generate and send a System Exclusive frame.
void sendNoteOff(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note Off message.
void sendNrpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected NRPN number.
void setThruFilterMode(Thru::Mode inThruFilterMode)
Set the filter for thru mirroring.
@ RPNLSB
Registered Parameter Number (LSB)
@ RPNMSB
Registered Parameter Number (MSB)
@ NRPNLSB
Non-Registered Parameter Number (LSB)
@ NRPNMSB
Non-Registered Parameter Number (MSB)
@ AfterTouchChannel
Channel Message - Channel (monophonic) AfterTouch.
@ ProgramChange
Channel Message - Program Change.
@ SystemExclusiveStart
System Exclusive Start.
@ Continue
System Real Time - Continue.
@ Clock
System Real Time - Timing Clock.
@ ActiveSensing
System Real Time - Active Sensing.
@ SystemReset
System Real Time - System Reset.
@ SystemExclusive
System Exclusive.
@ InvalidType
For notifying errors.
@ SongPosition
System Common - Song Position Pointer.
@ Tick
System Real Time - Timing Tick (1 tick = 10 milliseconds)
@ TuneRequest
System Common - Tune Request.
@ Start
System Real Time - Start.
@ ControlChange
Channel Message - Control Change / Channel Mode.
@ PitchBend
Channel Message - Pitch Bend.
@ NoteOff
Channel Message - Note Off.
@ SongSelect
System Common - Song Select.
@ SystemExclusiveEnd
System Exclusive End.
@ AfterTouchPoly
Channel Message - Polyphonic AfterTouch.
@ Stop
System Real Time - Stop.
@ TimeCodeQuarterFrame
System Common - MIDI Time Code Quarter Frame.
@ NoteOn
Channel Message - Note On.
DataByte sysexArray[sSysExMaxSize]
@ DifferentChannel
All the messages but the ones on the Input Channel will be sent back.
@ Full
Fully enabled Thru (every incoming message is sent back).
@ Off
Thru disabled (nothing passes through).
@ SameChannel
Only the messages on the Input Channel will be sent back.