diff --git a/examples/Bridge/Bridge.ino b/examples/Bridge/Bridge.ino new file mode 100644 index 0000000..c5e1443 --- /dev/null +++ b/examples/Bridge/Bridge.ino @@ -0,0 +1,34 @@ +#include +#include + +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(); +} diff --git a/examples/Callbacks/Callbacks.ino b/examples/Callbacks/Callbacks.ino index 90e1901..642223b 100644 --- a/examples/Callbacks/Callbacks.ino +++ b/examples/Callbacks/Callbacks.ino @@ -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); } diff --git a/src/MIDI.h b/src/MIDI.h index 56d44bf..b9183d1 100644 --- a/src/MIDI.h +++ b/src/MIDI.h @@ -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; diff --git a/src/MIDI.hpp b/src/MIDI.hpp index 16bdbb0..1bb37eb 100644 --- a/src/MIDI.hpp +++ b/src/MIDI.hpp @@ -36,7 +36,6 @@ inline MidiInterface::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::begin(Channel inChannel) mRunningStatus_RX = InvalidType; mPendingMessageIndex = 0; - mPendingMessageExpectedLenght = 0; mCurrentRpnNumber = 0xffff; mCurrentNrpnNumber = 0xffff; @@ -107,6 +105,7 @@ void MidiInterface::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::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 +void MidiInterface::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::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::parse() case AfterTouchChannel: case TimeCodeQuarterFrame: case SongSelect: - mPendingMessageExpectedLenght = 2; + mMessage.length = 2; break; // 3 bytes messages @@ -789,14 +827,14 @@ bool MidiInterface::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::parse() break; } - if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1)) + if (mPendingMessageIndex >= (mMessage.length - 1)) { // Reception complete mMessage.type = pendingType; @@ -818,7 +856,7 @@ bool MidiInterface::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::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::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 inline void MidiInterface::resetInput() { mPendingMessageIndex = 0; - mPendingMessageExpectedLenght = 0; + mMessage.length = 0; mRunningStatus_RX = InvalidType; } diff --git a/src/midi_Message.h b/src/midi_Message.h index 04b56c9..dadb896 100644 --- a/src/midi_Message.h +++ b/src/midi_Message.h @@ -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; diff --git a/src/serialMIDI.h b/src/serialMIDI.h index 801978f..1aa2ac3 100644 --- a/src/serialMIDI.h +++ b/src/serialMIDI.h @@ -53,9 +53,9 @@ private: */ #define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \ typedef MIDI_NAMESPACE::serialMIDI __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.