34 template<
class SerialPort,
class Settings>
40 , mPendingMessageExpectedLenght(0)
41 , mPendingMessageIndex(0)
42 , mCurrentRpnNumber(0xffff)
43 , mCurrentNrpnNumber(0xffff)
44 , mThruActivated(true)
45 , mThruFilterMode(
Thru::Full)
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;
61 mContinueCallback = 0;
63 mActiveSensingCallback = 0;
64 mSystemResetCallback = 0;
71 template<
class SerialPort,
class Settings>
84 template<
class SerialPort,
class Settings>
89 mSerial.
template open<Settings::BaudRate>();
91 mSerial.begin(Settings::BaudRate);
94 mInputChannel = inChannel;
98 mPendingMessageIndex = 0;
99 mPendingMessageExpectedLenght = 0;
101 mCurrentRpnNumber = 0xffff;
102 mCurrentNrpnNumber = 0xffff;
104 mMessage.
valid =
false;
111 mThruActivated =
true;
133 template<
class SerialPort,
class Settings>
153 const StatusByte status = getStatus(inType, inChannel);
155 if (Settings::UseRunningStatus)
157 if (mRunningStatus_TX != status)
160 mRunningStatus_TX = status;
161 mSerial.write(mRunningStatus_TX);
167 mSerial.write(status);
171 mSerial.write(inData1);
174 mSerial.write(inData2);
194 template<
class SerialPort,
class Settings>
199 send(
NoteOn, inNoteNumber, inVelocity, inChannel);
213 template<
class SerialPort,
class Settings>
218 send(
NoteOff, inNoteNumber, inVelocity, inChannel);
225 template<
class SerialPort,
class Settings>
238 template<
class SerialPort,
class Settings>
253 template<
class SerialPort,
class Settings>
265 template<
class SerialPort,
class Settings>
278 template<
class SerialPort,
class Settings>
292 template<
class SerialPort,
class Settings>
297 send(
PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, inChannel);
307 template<
class SerialPort,
class Settings>
312 const int value = int(inPitchValue *
double(scale));
325 template<
class SerialPort,
class Settings>
328 bool inArrayContainsBoundaries)
330 const bool writeBeginEndBytes = !inArrayContainsBoundaries;
332 if (writeBeginEndBytes)
337 for (
unsigned i = 0; i < inLength; ++i)
339 mSerial.write(inArray[i]);
342 if (writeBeginEndBytes)
347 if (Settings::UseRunningStatus)
358 template<
class SerialPort,
class Settings>
363 if (Settings::UseRunningStatus)
375 template<
class SerialPort,
class Settings>
379 const byte data = (((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0f));
389 template<
class SerialPort,
class Settings>
393 mSerial.write(inData);
395 if (Settings::UseRunningStatus)
404 template<
class SerialPort,
class Settings>
408 mSerial.write(inBeats & 0x7f);
409 mSerial.write((inBeats >> 7) & 0x7f);
411 if (Settings::UseRunningStatus)
418 template<
class SerialPort,
class Settings>
422 mSerial.write(inSongNumber & 0x7f);
424 if (Settings::UseRunningStatus)
436 template<
class SerialPort,
class Settings>
450 mSerial.write((
byte)inType);
462 template<
class SerialPort,
class Settings>
466 if (mCurrentRpnNumber != inNumber)
468 const byte numMsb = 0x7f & (inNumber >> 7);
469 const byte numLsb = 0x7f & inNumber;
472 mCurrentRpnNumber = inNumber;
480 template<
class SerialPort,
class Settings>
484 const byte valMsb = 0x7f & (inValue >> 7);
485 const byte valLsb = 0x7f & inValue;
495 template<
class SerialPort,
class Settings>
507 template<
class SerialPort,
class Settings>
517 template<
class SerialPort,
class Settings>
528 template<
class SerialPort,
class Settings>
533 mCurrentRpnNumber = 0xffff;
542 template<
class SerialPort,
class Settings>
546 if (mCurrentNrpnNumber != inNumber)
548 const byte numMsb = 0x7f & (inNumber >> 7);
549 const byte numLsb = 0x7f & inNumber;
552 mCurrentNrpnNumber = inNumber;
560 template<
class SerialPort,
class Settings>
564 const byte valMsb = 0x7f & (inValue >> 7);
565 const byte valLsb = 0x7f & inValue;
575 template<
class SerialPort,
class Settings>
587 template<
class SerialPort,
class Settings>
597 template<
class SerialPort,
class Settings>
608 template<
class SerialPort,
class Settings>
613 mCurrentNrpnNumber = 0xffff;
620 template<
class SerialPort,
class Settings>
624 return ((
byte)inType | ((inChannel - 1) & 0x0f));
643 template<
class SerialPort,
class Settings>
646 return read(mInputChannel);
651 template<
class SerialPort,
class Settings>
660 handleNullVelocityNoteOnAsNoteOff();
661 const bool channelMatch = inputFilter(inChannel);
668 thruFilter(inChannel);
676 template<
class SerialPort,
class Settings>
679 if (mSerial.available() == 0)
692 const byte extracted = mSerial.read();
695 if (extracted == 0xf9 || extracted == 0xfd)
697 if (Settings::Use1ByteParsing)
707 if (mPendingMessageIndex == 0)
710 mPendingMessage[0] = extracted;
719 if (extracted < 0x80)
721 mPendingMessage[0] = mRunningStatus_RX;
722 mPendingMessage[1] = extracted;
723 mPendingMessageIndex = 1;
745 mMessage.
valid =
true;
749 mPendingMessageIndex = 0;
750 mPendingMessageExpectedLenght = 0;
760 mPendingMessageExpectedLenght = 2;
770 mPendingMessageExpectedLenght = 3;
789 if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
794 mMessage.
data1 = mPendingMessage[1];
797 mPendingMessageIndex = 0;
798 mPendingMessageExpectedLenght = 0;
799 mMessage.
valid =
true;
805 mPendingMessageIndex++;
808 if (Settings::Use1ByteParsing)
823 if (extracted >= 0x80)
847 mMessage.
valid =
true;
855 mMessage.
sysexArray[mPendingMessageIndex++] = 0xf7;
859 mMessage.
data1 = mPendingMessageIndex & 0xff;
860 mMessage.
data2 = mPendingMessageIndex >> 8;
862 mMessage.
valid =
true;
881 mMessage.
sysexArray[mPendingMessageIndex] = extracted;
883 mPendingMessage[mPendingMessageIndex] = extracted;
886 if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
904 mMessage.
data1 = mPendingMessage[1];
907 mMessage.
data2 = mPendingMessageExpectedLenght == 3 ? mPendingMessage[2] : 0;
910 mPendingMessageIndex = 0;
911 mPendingMessageExpectedLenght = 0;
913 mMessage.
valid =
true;
916 switch (mMessage.
type)
926 mRunningStatus_RX = mPendingMessage[0];
939 mPendingMessageIndex++;
941 if (Settings::Use1ByteParsing)
956 template<
class SerialPort,
class Settings>
959 if (Settings::HandleNullVelocityNoteOnAsNoteOff &&
967 template<
class SerialPort,
class Settings>
977 if ((mMessage.
channel == inChannel) ||
996 template<
class SerialPort,
class Settings>
999 mPendingMessageIndex = 0;
1000 mPendingMessageExpectedLenght = 0;
1010 template<
class SerialPort,
class Settings>
1013 return mMessage.
type;
1021 template<
class SerialPort,
class Settings>
1028 template<
class SerialPort,
class Settings>
1031 return mMessage.
data1;
1035 template<
class SerialPort,
class Settings>
1038 return mMessage.
data2;
1045 template<
class SerialPort,
class Settings>
1056 template<
class SerialPort,
class Settings>
1063 template<
class SerialPort,
class Settings>
1066 return mMessage.
valid;
1071 template<
class SerialPort,
class Settings>
1074 return mInputChannel;
1081 template<
class SerialPort,
class Settings>
1084 mInputChannel = inChannel;
1094 template<
class SerialPort,
class Settings>
1097 if ((inStatus < 0x80) ||
1098 (inStatus == 0xf4) ||
1099 (inStatus == 0xf5) ||
1100 (inStatus == 0xf9) ||
1106 if (inStatus < 0xf0)
1117 template<
class SerialPort,
class Settings>
1120 return (inStatus & 0x0f) + 1;
1123 template<
class SerialPort,
class Settings>
1166 template<
class SerialPort,
class Settings>
1171 case NoteOff: mNoteOffCallback = 0;
break;
1172 case NoteOn: mNoteOnCallback = 0;
break;
1177 case PitchBend: mPitchBendCallback = 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;
1188 case SystemReset: mSystemResetCallback = 0;
break;
1197 template<
class SerialPort,
class Settings>
1201 switch (mMessage.
type)
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;
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;
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;
1229 case SystemReset:
if (mSystemResetCallback != 0) mSystemResetCallback();
break;
1252 template<
class SerialPort,
class Settings>
1255 mThruFilterMode = inThruFilterMode;
1256 mThruActivated = mThruFilterMode !=
Thru::Off;
1259 template<
class SerialPort,
class Settings>
1262 return mThruFilterMode;
1265 template<
class SerialPort,
class Settings>
1268 return mThruActivated;
1271 template<
class SerialPort,
class Settings>
1274 mThruActivated =
true;
1275 mThruFilterMode = inThruFilterMode;
1278 template<
class SerialPort,
class Settings>
1281 mThruActivated =
false;
1293 template<
class SerialPort,
class Settings>
1297 if (!mThruActivated || (mThruFilterMode ==
Thru::Off))
1303 const bool filter_condition = ((mMessage.
channel == inChannel) ||
1307 switch (mThruFilterMode)
1317 if (filter_condition)
1327 if (!filter_condition)
1343 switch (mMessage.
type)
void setHandleProgramChange(void(*fptr)(byte channel, byte number))
System Common - Song Position Pointer.
void disconnectCallbackFromType(MidiType inType)
Detach an external function from the given type.
System Common - Song Select.
void setHandleNoteOn(void(*fptr)(byte channel, byte note, byte velocity))
void sendSongSelect(DataByte inSongNumber)
Send a Song Select message.
void setThruFilterMode(Thru::Mode inThruFilterMode)
Set the filter for thru mirroring.
void sendSongPosition(unsigned inBeats)
Send a Song Position Pointer message.
Non-Registered Parameter Number (MSB)
void turnThruOn(Thru::Mode inThruFilterMode=Thru::Full)
#define MIDI_PITCHBEND_MAX
void beginRpn(unsigned inNumber, Channel inChannel)
Start a Registered Parameter Number frame.
static const unsigned sSysExMaxSize
System Real Time - Timing Clock.
void sendProgramChange(DataByte inProgramNumber, Channel inChannel)
Send a Program Change message.
MidiType getType() const
Get the last received message's type.
DataByte getData2() const
Get the second data byte of the last received message.
#define BEGIN_MIDI_NAMESPACE
System Common - MIDI Time Code Quarter Frame.
Thru disabled (nothing passes through).
#define MIDI_PITCHBEND_MIN
DataByte sysexArray[sSysExMaxSize]
Channel getChannel() const
Get the channel of the message stored in the structure.
void setHandleSystemExclusive(void(*fptr)(byte *array, unsigned size))
#define END_MIDI_NAMESPACE
void setHandleSongSelect(void(*fptr)(byte songnumber))
unsigned getSysExArrayLength() const
Get the lenght of the System Exclusive array.
Only the messages on the Input Channel will be sent back.
bool read()
Read messages from the serial port using the main input channel.
DataByte getData1() const
Get the first data byte of the last received message.
void setHandleClock(void(*fptr)(void))
Channel (monophonic) AfterTouch.
void beginNrpn(unsigned inNumber, Channel inChannel)
Start a Non-Registered Parameter Number frame.
Non-Registered Parameter Number (LSB)
void begin(Channel inChannel=1)
Call the begin method in the setup() function of the Arduino.
void sendRealTime(MidiType inType)
Send a Real Time (one byte) message.
static Channel getChannelFromStatusByte(byte inStatus)
Returns channel in the range 1-16.
void setHandleStart(void(*fptr)(void))
System Real Time - Continue.
void endRpn(Channel inChannel)
Terminate an RPN frame. This will send a Null Function to deselect the currently selected RPN...
#define MIDI_CHANNEL_OMNI
void setInputChannel(Channel inChannel)
Set the value for the input MIDI channel.
void setHandleSystemReset(void(*fptr)(void))
System Real Time - Start.
bool getThruState() const
Channel getInputChannel() const
void endNrpn(Channel inChannel)
Terminate an NRPN frame. This will send a Null Function to deselect the currently selected NRPN...
Registered Parameter Number (MSB)
void sendAfterTouch(DataByte inPressure, Channel inChannel)
Send a MonoPhonic AfterTouch message (applies to all notes)
void sendPolyPressure(DataByte inNoteNumber, DataByte inPressure, Channel inChannel) __attribute__((deprecated))
Send a Polyphonic AfterTouch message (applies to a specified note)
void sendRpnDecrement(byte inAmount, Channel inChannel)
void sendNrpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected NRPN number.
void sendControlChange(DataByte inControlNumber, DataByte inControlValue, Channel inChannel)
Send a Control Change message.
void sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble)
Send a MIDI Time Code Quarter Frame.
void sendNrpnDecrement(byte inAmount, Channel inChannel)
void setHandleSongPosition(void(*fptr)(unsigned beats))
Control Change / Channel Mode.
Thru::Mode getFilterMode() const
void sendPitchBend(int inPitchValue, Channel inChannel)
Send a Pitch Bend message using a signed integer value.
System Common - Tune Request.
static MidiType getTypeFromStatusByte(byte inStatus)
Extract an enumerated MIDI type from a status byte.
void setHandleActiveSensing(void(*fptr)(void))
MidiInterface(SerialPort &inSerial)
Constructor for MidiInterface.
static bool isChannelMessage(MidiType inType)
void setHandleTimeCodeQuarterFrame(void(*fptr)(byte data))
void sendTuneRequest()
Send a Tune Request message.
void setHandleContinue(void(*fptr)(void))
void sendNrpnIncrement(byte inAmount, Channel inChannel)
void sendRpnValue(unsigned inValue, Channel inChannel)
Send a 14-bit value for the currently selected RPN number.
void send(MidiType inType, DataByte inData1, DataByte inData2, Channel inChannel)
Generate and send a MIDI message from the values given.
void sendSysEx(unsigned inLength, const byte *inArray, bool inArrayContainsBoundaries=false)
Generate and send a System Exclusive frame.
void setHandleNoteOff(void(*fptr)(byte channel, byte note, byte velocity))
bool check() const
Check if a valid message is stored in the structure.
All the messages but the ones on the Input Channel will be sent back.
void setHandlePitchBend(void(*fptr)(byte channel, int bend))
unsigned getSysExSize() const
void setHandleStop(void(*fptr)(void))
void sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note On message.
const byte * getSysExArray() const
Get the System Exclusive byte array.
System Real Time - Active Sensing.
Fully enabled Thru (every incoming message is sent back).
void setHandleTuneRequest(void(*fptr)(void))
System Real Time - System Reset.
~MidiInterface()
Destructor for MidiInterface.
void setHandleControlChange(void(*fptr)(byte channel, byte number, byte value))
void sendNoteOff(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel)
Send a Note Off message.
The main class for MIDI handling. It is templated over the type of serial port to provide abstraction...
void setHandleAfterTouchChannel(void(*fptr)(byte channel, byte pressure))
Registered Parameter Number (LSB)
void sendRpnIncrement(byte inAmount, Channel inChannel)
void setHandleAfterTouchPoly(void(*fptr)(byte channel, byte note, byte pressure))