added send(MidiMessage), added (untested) bridge example

added send(MidiMessage) for Bridge application (that convert MIDI transport x into MIDI transport y), avoiding parsing entry a stream, setting up all callback - whilst this allows for passing the content, without to much processing/parsing.

Had to move mPendingMessageExpectedLenght into MidiMessage to avoid parsing the data again, just to know the size

Added Bridge example (untested)
This commit is contained in:
lathoub 2020-03-09 23:05:44 +01:00
parent 8905d36c0e
commit 8893642b27
6 changed files with 92 additions and 22 deletions

View File

@ -0,0 +1,34 @@
#include <MIDI.h>
#include <USB-MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);
USBMIDI_CREATE_DEFAULT_INSTANCE();
void handleMessage1(TypedMidiInterface::MidiMessage message)
{
MIDI.sendMessage(message);
}
void handleMessage(TypedMidiInterface::MidiMessage message)
{
MIDI1.sendMessage(message);
}
// -----------------------------------------------------------------------------
void setup()
{
MIDI.setHandleMessage(handleMessage);
MIDI1.setHandleMessage(handleMessage1);
// Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI1.begin(MIDI_CHANNEL_OMNI);
}
void loop()
{
// Call MIDI.read the fastest you can for real-time performance.
MIDI.read();
MIDI1.read();
}

View File

@ -24,11 +24,6 @@ void handleNoteOff(byte channel, byte pitch, byte velocity)
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs. // Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
} }
void handleMessage(__mismt::MidiMessage message)
{
// Do something when the message.
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void setup() void setup()
@ -40,8 +35,6 @@ void setup()
// Do the same for NoteOffs // Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff); MIDI.setHandleNoteOff(handleNoteOff);
MIDI.setHandleMessage(handleMessage);
// Initiate MIDI communications, listen to all channels // Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI); MIDI.begin(MIDI_CHANNEL_OMNI);
} }

View File

@ -130,6 +130,8 @@ public:
Channel inChannel); Channel inChannel);
inline void endNrpn(Channel inChannel); inline void endNrpn(Channel inChannel);
inline void send(MidiMessage);
public: public:
void send(MidiType inType, void send(MidiType inType,
DataByte inData1, DataByte inData1,
@ -239,7 +241,6 @@ private:
StatusByte mRunningStatus_RX; StatusByte mRunningStatus_RX;
StatusByte mRunningStatus_TX; StatusByte mRunningStatus_TX;
byte mPendingMessage[3]; byte mPendingMessage[3];
unsigned mPendingMessageExpectedLenght;
unsigned mPendingMessageIndex; unsigned mPendingMessageIndex;
unsigned mCurrentRpnNumber; unsigned mCurrentRpnNumber;
unsigned mCurrentNrpnNumber; unsigned mCurrentNrpnNumber;

View File

@ -36,7 +36,6 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
, mInputChannel(0) , mInputChannel(0)
, mRunningStatus_RX(InvalidType) , mRunningStatus_RX(InvalidType)
, mRunningStatus_TX(InvalidType) , mRunningStatus_TX(InvalidType)
, mPendingMessageExpectedLenght(0)
, mPendingMessageIndex(0) , mPendingMessageIndex(0)
, mCurrentRpnNumber(0xffff) , mCurrentRpnNumber(0xffff)
, mCurrentNrpnNumber(0xffff) , mCurrentNrpnNumber(0xffff)
@ -94,7 +93,6 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
mCurrentRpnNumber = 0xffff; mCurrentRpnNumber = 0xffff;
mCurrentNrpnNumber = 0xffff; mCurrentNrpnNumber = 0xffff;
@ -107,6 +105,7 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mMessage.channel = 0; mMessage.channel = 0;
mMessage.data1 = 0; mMessage.data1 = 0;
mMessage.data2 = 0; mMessage.data2 = 0;
mMessage.length = 0;
mThruFilterMode = Thru::Full; mThruFilterMode = Thru::Full;
mThruActivated = false; mThruActivated = false;
@ -120,6 +119,45 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
@{ @{
*/ */
/*! \brief Send a MIDI message.
\param inMessage The message
This method is used when you want to send a Message that has not been constructed
by the library, but by an external source.
This method does *not* check against any of the constraints.
Typically this function is use by MIDI Bridges taking MIDI messages and passing
them thru.
*/
template<class Transport, class Settings, class Platform>
void MidiInterface<Transport, Settings, Platform>::send(MidiMessage inMessage)
{
if (!inMessage.valid)
return;
if (mTransport.beginTransmission(inMessage.type))
{
const StatusByte status = getStatus(inMessage.type, inMessage.channel);
mTransport.write(status);
if (inMessage.type != MidiType::SystemExclusive)
{
if (inMessage.length > 1) mTransport.write(inMessage.data1);
if (inMessage.length > 2) mTransport.write(inMessage.data2);
} else
{
// sysexArray does not contain the start and end tags
mTransport.write(MidiType::SystemExclusiveStart);
for (size_t i = 0; i < inMessage.getSysExSize(); i++)
mTransport.write(inMessage.sysexArray[i]);
mTransport.write(MidiType::SystemExclusiveEnd);
}
}
mTransport.endTransmission();
}
/*! \brief Generate and send a MIDI message from the values given. /*! \brief Generate and send a MIDI message from the values given.
\param inType The message type (see type defines for reference) \param inType The message type (see type defines for reference)
\param inData1 The first data byte. \param inData1 The first data byte.
@ -764,12 +802,12 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.channel = 0; mMessage.channel = 0;
mMessage.data1 = 0; mMessage.data1 = 0;
mMessage.data2 = 0; mMessage.data2 = 0;
mMessage.length = 0;
mMessage.valid = true; mMessage.valid = true;
// Do not reset all input attributes, Running Status must remain unchanged. // Do not reset all input attributes, Running Status must remain unchanged.
// We still need to reset these // We still need to reset these
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
return true; return true;
break; break;
@ -779,7 +817,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
case AfterTouchChannel: case AfterTouchChannel:
case TimeCodeQuarterFrame: case TimeCodeQuarterFrame:
case SongSelect: case SongSelect:
mPendingMessageExpectedLenght = 2; mMessage.length = 2;
break; break;
// 3 bytes messages // 3 bytes messages
@ -789,14 +827,14 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
case PitchBend: case PitchBend:
case AfterTouchPoly: case AfterTouchPoly:
case SongPosition: case SongPosition:
mPendingMessageExpectedLenght = 3; mMessage.length = 3;
break; break;
case SystemExclusiveStart: case SystemExclusiveStart:
case SystemExclusiveEnd: case SystemExclusiveEnd:
// The message can be any lenght // The message can be any lenght
// between 3 and MidiMessage::sSysExMaxSize bytes // between 3 and MidiMessage::sSysExMaxSize bytes
mPendingMessageExpectedLenght = MidiMessage::sSysExMaxSize; mMessage.length = MidiMessage::sSysExMaxSize;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = pendingType; mMessage.sysexArray[0] = pendingType;
break; break;
@ -809,7 +847,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
break; break;
} }
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1)) if (mPendingMessageIndex >= (mMessage.length - 1))
{ {
// Reception complete // Reception complete
mMessage.type = pendingType; mMessage.type = pendingType;
@ -818,7 +856,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.data2 = 0; // Completed new message has 1 data byte mMessage.data2 = 0; // Completed new message has 1 data byte
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mMessage.length = 0;
mMessage.valid = true; mMessage.valid = true;
return true; return true;
} }
@ -909,7 +947,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mPendingMessage[mPendingMessageIndex] = extracted; mPendingMessage[mPendingMessageIndex] = extracted;
// Now we are going to check if we have reached the end of the message // Now we are going to check if we have reached the end of the message
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1)) if (mPendingMessageIndex >= (mMessage.length - 1))
{ {
// "FML" case: fall down here with an overflown SysEx.. // "FML" case: fall down here with an overflown SysEx..
// This means we received the last possible data byte that can fit // This means we received the last possible data byte that can fit
@ -930,11 +968,11 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.data1 = mPendingMessage[1]; mMessage.data1 = mPendingMessage[1];
// Save data2 only if applicable // Save data2 only if applicable
mMessage.data2 = mPendingMessageExpectedLenght == 3 ? mPendingMessage[2] : 0; mMessage.data2 = mMessage.length == 3 ? mPendingMessage[2] : 0;
// Reset local variables // Reset local variables
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mMessage.length = 0;
mMessage.valid = true; mMessage.valid = true;
@ -1014,7 +1052,7 @@ template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::resetInput() inline void MidiInterface<Transport, Settings, Platform>::resetInput()
{ {
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mMessage.length = 0;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
} }

View File

@ -91,6 +91,10 @@ struct Message
*/ */
bool valid; bool valid;
/*! Total Length of the message.
*/
unsigned length;
inline unsigned getSysExSize() const inline unsigned getSysExSize() const
{ {
const unsigned size = unsigned(data2) << 8 | data1; const unsigned size = unsigned(data2) << 8 | data1;

View File

@ -53,9 +53,9 @@ private:
*/ */
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \ #define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
typedef MIDI_NAMESPACE::serialMIDI<Type> __smt;\ typedef MIDI_NAMESPACE::serialMIDI<Type> __smt;\
typedef MIDI_NAMESPACE::MidiInterface<__smt> __mismt;\ typedef MIDI_NAMESPACE::MidiInterface<__smt> TypedMidiInterface;\
__smt serialMidi(SerialPort);\ __smt serialMidi(SerialPort);\
__mismt Name((__smt&)serialMidi); TypedMidiInterface Name((__smt&)serialMidi);
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) #if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
// Leonardo, Due and other USB boards use Serial1 by default. // Leonardo, Due and other USB boards use Serial1 by default.