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.
}
void handleMessage(__mismt::MidiMessage message)
{
// Do something when the message.
}
// -----------------------------------------------------------------------------
void setup()
@ -40,8 +35,6 @@ void setup()
// Do the same for NoteOffs
MIDI.setHandleNoteOff(handleNoteOff);
MIDI.setHandleMessage(handleMessage);
// Initiate MIDI communications, listen to all channels
MIDI.begin(MIDI_CHANNEL_OMNI);
}

View File

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

View File

@ -36,7 +36,6 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
, mInputChannel(0)
, mRunningStatus_RX(InvalidType)
, mRunningStatus_TX(InvalidType)
, mPendingMessageExpectedLenght(0)
, mPendingMessageIndex(0)
, mCurrentRpnNumber(0xffff)
, mCurrentNrpnNumber(0xffff)
@ -94,7 +93,6 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mRunningStatus_RX = InvalidType;
mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
mCurrentRpnNumber = 0xffff;
mCurrentNrpnNumber = 0xffff;
@ -107,6 +105,7 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
mMessage.channel = 0;
mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.length = 0;
mThruFilterMode = Thru::Full;
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.
\param inType The message type (see type defines for reference)
\param inData1 The first data byte.
@ -764,12 +802,12 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.channel = 0;
mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.length = 0;
mMessage.valid = true;
// Do not reset all input attributes, Running Status must remain unchanged.
// We still need to reset these
mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
return true;
break;
@ -779,7 +817,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
case AfterTouchChannel:
case TimeCodeQuarterFrame:
case SongSelect:
mPendingMessageExpectedLenght = 2;
mMessage.length = 2;
break;
// 3 bytes messages
@ -789,14 +827,14 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
case PitchBend:
case AfterTouchPoly:
case SongPosition:
mPendingMessageExpectedLenght = 3;
mMessage.length = 3;
break;
case SystemExclusiveStart:
case SystemExclusiveEnd:
// The message can be any lenght
// between 3 and MidiMessage::sSysExMaxSize bytes
mPendingMessageExpectedLenght = MidiMessage::sSysExMaxSize;
mMessage.length = MidiMessage::sSysExMaxSize;
mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = pendingType;
break;
@ -809,7 +847,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
break;
}
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
if (mPendingMessageIndex >= (mMessage.length - 1))
{
// Reception complete
mMessage.type = pendingType;
@ -818,7 +856,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mMessage.data2 = 0; // Completed new message has 1 data byte
mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
mMessage.length = 0;
mMessage.valid = true;
return true;
}
@ -909,7 +947,7 @@ bool MidiInterface<Transport, Settings, Platform>::parse()
mPendingMessage[mPendingMessageIndex] = extracted;
// 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..
// 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];
// Save data2 only if applicable
mMessage.data2 = mPendingMessageExpectedLenght == 3 ? mPendingMessage[2] : 0;
mMessage.data2 = mMessage.length == 3 ? mPendingMessage[2] : 0;
// Reset local variables
mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
mMessage.length = 0;
mMessage.valid = true;
@ -1014,7 +1052,7 @@ template<class Transport, class Settings, class Platform>
inline void MidiInterface<Transport, Settings, Platform>::resetInput()
{
mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
mMessage.length = 0;
mRunningStatus_RX = InvalidType;
}

View File

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

View File

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