Reverted Thru changes on branch release/4.0 (breaking thread/merge-safety).

This commit is contained in:
Francois Best 2013-07-07 15:10:13 +02:00
parent cc9927fd50
commit 601ddb3773
3 changed files with 464 additions and 411 deletions

View File

@ -68,7 +68,7 @@ public:
inline void sendAfterTouch(DataByte inPressure, inline void sendAfterTouch(DataByte inPressure,
Channel inChannel); Channel inChannel);
inline void sendSysEx(unsigned inLength, inline void sendSysEx(unsigned int inLength,
const byte* inArray, const byte* inArray,
bool inArrayContainsBoundaries = false); bool inArrayContainsBoundaries = false);
@ -76,7 +76,7 @@ public:
DataByte inValuesNibble); DataByte inValuesNibble);
inline void sendTimeCodeQuarterFrame(DataByte inData); inline void sendTimeCodeQuarterFrame(DataByte inData);
inline void sendSongPosition(unsigned inBeats); inline void sendSongPosition(unsigned int inBeats);
inline void sendSongSelect(DataByte inSongNumber); inline void sendSongSelect(DataByte inSongNumber);
inline void sendTuneRequest(); inline void sendTuneRequest();
inline void sendRealTime(MidiType inType); inline void sendRealTime(MidiType inType);
@ -109,7 +109,7 @@ public:
inline DataByte getData1() const; inline DataByte getData1() const;
inline DataByte getData2() const; inline DataByte getData2() const;
inline const byte* getSysExArray() const; inline const byte* getSysExArray() const;
inline unsigned getSysExArrayLength() const; inline unsigned int getSysExArrayLength() const;
inline bool check() const; inline bool check() const;
public: public:
@ -117,11 +117,8 @@ public:
inline void setInputChannel(Channel inChannel); inline void setInputChannel(Channel inChannel);
public: public:
static inline MidiType getTypeFromStatusByte(StatusByte inStatus); static inline MidiType getTypeFromStatusByte(byte inStatus);
static inline byte getMessageLength(StatusByte inStatus);
static inline bool isChannelMessage(MidiType inType); static inline bool isChannelMessage(MidiType inType);
static inline bool isSystemRealtimeMessage(MidiType inType);
static inline bool isSystemCommonMessage(MidiType inType);
private: private:
bool inputFilter(Channel inChannel); bool inputFilter(Channel inChannel);
@ -133,8 +130,8 @@ private:
Channel mInputChannel; Channel mInputChannel;
byte mPendingMessage[3]; // SysEx are dumped into mMessage directly. byte mPendingMessage[3]; // SysEx are dumped into mMessage directly.
unsigned mPendingMessageExpectedLenght; unsigned int mPendingMessageExpectedLenght;
unsigned mPendingMessageIndex; // Extended to unsigned for larger SysEx payloads. unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads.
Message mMessage; Message mMessage;
@ -153,7 +150,7 @@ public:
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend)); inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size)); inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size));
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
inline void setHandleSongPosition(void (*fptr)(unsigned beats)); inline void setHandleSongPosition(void (*fptr)(unsigned int beats));
inline void setHandleSongSelect(void (*fptr)(byte songnumber)); inline void setHandleSongSelect(void (*fptr)(byte songnumber));
inline void setHandleTuneRequest(void (*fptr)(void)); inline void setHandleTuneRequest(void (*fptr)(void));
inline void setHandleClock(void (*fptr)(void)); inline void setHandleClock(void (*fptr)(void));
@ -178,7 +175,7 @@ private:
void (*mPitchBendCallback)(byte channel, int); void (*mPitchBendCallback)(byte channel, int);
void (*mSystemExclusiveCallback)(byte * array, byte size); void (*mSystemExclusiveCallback)(byte * array, byte size);
void (*mTimeCodeQuarterFrameCallback)(byte data); void (*mTimeCodeQuarterFrameCallback)(byte data);
void (*mSongPositionCallback)(unsigned beats); void (*mSongPositionCallback)(unsigned int beats);
void (*mSongSelectCallback)(byte songnumber); void (*mSongSelectCallback)(byte songnumber);
void (*mTuneRequestCallback)(void); void (*mTuneRequestCallback)(void);
void (*mClockCallback)(void); void (*mClockCallback)(void);
@ -199,19 +196,20 @@ private:
#if MIDI_BUILD_THRU #if MIDI_BUILD_THRU
public: public:
inline void turnThruOn(ThruFlags inThruFlags = ThruFilterFlags::all); inline MidiFilterMode getFilterMode() const;
inline bool getThruState() const;
inline void turnThruOn(MidiFilterMode inThruFilterMode = Full);
inline void turnThruOff(); inline void turnThruOff();
inline void setThruFilterMode(MidiFilterMode inThruFilterMode);
inline void setThruFilterMode(ThruFlags inFlags);
inline ThruFlags getThruFilterMode() const;
inline bool isThruOn() const;
inline bool hasThruFlag(byte inFlag) const;
private: private:
inline bool shouldMessageBeForwarded(StatusByte inStatus) const; void thruFilter(byte inChannel);
private: private:
ThruFlags mThruFlags; bool mThruActivated : 1;
MidiFilterMode mThruFilterMode : 7;
#endif // MIDI_BUILD_THRU #endif // MIDI_BUILD_THRU

View File

@ -70,7 +70,7 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif #endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS
#if MIDI_BUILD_INPUT #if MIDI_BUILD_INPUT
@ -86,14 +86,16 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
mMessage.data1 = 0; mMessage.data1 = 0;
mMessage.data2 = 0; mMessage.data2 = 0;
#endif #endif // MIDI_BUILD_INPUT
#if MIDI_BUILD_THRU #if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru
mThruFlags = ThruFilterFlags::all; mThruFilterMode = Full;
mThruActivated = true;
#endif // Thru
#endif
} }
@ -266,10 +268,11 @@ template<class SerialPort>
void MidiInterface<SerialPort>::sendPitchBend(int inPitchValue, void MidiInterface<SerialPort>::sendPitchBend(int inPitchValue,
Channel inChannel) Channel inChannel)
{ {
const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN; const unsigned int bend = inPitchValue - MIDI_PITCHBEND_MIN;
send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel); send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel);
} }
/*! \brief Send a Pitch Bend message using a floating point value. /*! \brief Send a Pitch Bend message using a floating point value.
\param inPitchValue The amount of bend to send (in a floating point format), \param inPitchValue The amount of bend to send (in a floating point format),
between -1.0f (maximum downwards bend) between -1.0f (maximum downwards bend)
@ -290,11 +293,11 @@ void MidiInterface<SerialPort>::sendPitchBend(double inPitchValue,
\param inArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes \param inArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes
(start & stop SysEx) will NOT be sent (start & stop SysEx) will NOT be sent
(and therefore must be included in the array). (and therefore must be included in the array).
default value for inArrayContainsBoundaries is set to 'false' for compatibility default value for ArrayContainsBoundaries is set to 'false' for compatibility
with previous versions of the library. with previous versions of the library.
*/ */
template<class SerialPort> template<class SerialPort>
void MidiInterface<SerialPort>::sendSysEx(unsigned inLength, void MidiInterface<SerialPort>::sendSysEx(unsigned int inLength,
const byte* inArray, const byte* inArray,
bool inArrayContainsBoundaries) bool inArrayContainsBoundaries)
{ {
@ -302,14 +305,14 @@ void MidiInterface<SerialPort>::sendSysEx(unsigned inLength,
{ {
mSerial.write(0xF0); mSerial.write(0xF0);
for (unsigned i = 0; i < inLength; ++i) for (unsigned int i = 0; i < inLength; ++i)
mSerial.write(inArray[i]); mSerial.write(inArray[i]);
mSerial.write(0xF7); mSerial.write(0xF7);
} }
else else
{ {
for (unsigned i = 0; i < inLength; ++i) for (unsigned int i = 0; i < inLength; ++i)
mSerial.write(inArray[i]); mSerial.write(inArray[i]);
} }
@ -364,7 +367,7 @@ void MidiInterface<SerialPort>::sendTimeCodeQuarterFrame(DataByte inData)
\param inBeats The number of beats since the start of the song. \param inBeats The number of beats since the start of the song.
*/ */
template<class SerialPort> template<class SerialPort>
void MidiInterface<SerialPort>::sendSongPosition(unsigned inBeats) void MidiInterface<SerialPort>::sendSongPosition(unsigned int inBeats)
{ {
mSerial.write((byte)SongPosition); mSerial.write((byte)SongPosition);
mSerial.write(inBeats & 0x7F); mSerial.write(inBeats & 0x7F);
@ -397,9 +400,20 @@ void MidiInterface<SerialPort>::sendSongSelect(DataByte inSongNumber)
template<class SerialPort> template<class SerialPort>
void MidiInterface<SerialPort>::sendRealTime(MidiType inType) void MidiInterface<SerialPort>::sendRealTime(MidiType inType)
{ {
if (isSystemRealtimeMessage(inType) || inType == TuneRequest) switch (inType)
{ {
case TuneRequest: // Not really real-time, but one byte anyway.
case Clock:
case Start:
case Stop:
case Continue:
case ActiveSensing:
case SystemReset:
mSerial.write((byte)inType); mSerial.write((byte)inType);
break;
default:
// Invalid Real Time marker
break;
} }
// Do not cancel Running Status for real-time messages as they can be // Do not cancel Running Status for real-time messages as they can be
@ -456,13 +470,22 @@ bool MidiInterface<SerialPort>::read(Channel inChannel)
if (inChannel >= MIDI_CHANNEL_OFF) if (inChannel >= MIDI_CHANNEL_OFF)
return false; // MIDI Input disabled. return false; // MIDI Input disabled.
if (parse() && inputFilter(inChannel)) if (parse())
{ {
if (inputFilter(inChannel))
{
#if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
thruFilter(inChannel);
#endif
#if MIDI_USE_CALLBACKS #if MIDI_USE_CALLBACKS
launchCallback(); launchCallback();
#endif #endif
return true; return true;
} }
}
return false; return false;
} }
@ -485,7 +508,7 @@ bool MidiInterface<SerialPort>::parse()
// - Look for other bytes in buffer, call parser recursively, // - Look for other bytes in buffer, call parser recursively,
// until the message is assembled or the buffer is empty. // until the message is assembled or the buffer is empty.
// Else, add the extracted byte to the pending message, and check validity. // Else, add the extracted byte to the pending message, and check validity.
// When the message is recomposed, notify the caller. // When the message is done, store it.
const byte extracted = mSerial.read(); const byte extracted = mSerial.read();
@ -530,16 +553,17 @@ bool MidiInterface<SerialPort>::parse()
} }
} }
mPendingMessageExpectedLenght = getMessageLength(mPendingMessage[0]); switch (getTypeFromStatusByte(mPendingMessage[0]))
if (mPendingMessageExpectedLenght == 1)
{ {
// RealTime and 1 byte messages -> send & handle rightaway // 1 byte messages
if (shouldMessageBeForwarded(mPendingMessage[0])) case Start:
{ case Continue:
mSerial.write(mPendingMessage[0]); case Stop:
} case Clock:
case ActiveSensing:
case SystemReset:
case TuneRequest:
// Handle the message type directly here.
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = 0; mMessage.channel = 0;
mMessage.data1 = 0; mMessage.data1 = 0;
@ -555,32 +579,45 @@ bool MidiInterface<SerialPort>::parse()
mPendingMessageExpectedLenght = 0; mPendingMessageExpectedLenght = 0;
return true; return true;
} break;
else if (mPendingMessageExpectedLenght == 0xff)
{ // 2 bytes messages
// SysEx: The message can be any length case ProgramChange:
case AfterTouchChannel:
case TimeCodeQuarterFrame:
case SongSelect:
mPendingMessageExpectedLenght = 2;
break;
// 3 bytes messages
case NoteOn:
case NoteOff:
case ControlChange:
case PitchBend:
case AfterTouchPoly:
case SongPosition:
mPendingMessageExpectedLenght = 3;
break;
case SystemExclusive:
// The message can be any lenght
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = SystemExclusive; mMessage.sysexArray[0] = SystemExclusive;
break;
case InvalidType:
} default:
else if (mPendingMessageExpectedLenght == 0) // This is obviously wrong. Let's get the hell out'a here.
{
// Parse error
resetInput(); resetInput();
return false; return false;
break;
} }
// Then update the index of the pending message. // Then update the index of the pending message.
mPendingMessageIndex++; mPendingMessageIndex++;
if (shouldMessageBeForwarded(mPendingMessage[0]))
{
mSerial.write(mPendingMessage[0]);
}
#if USE_1BYTE_PARSING #if USE_1BYTE_PARSING
// Message is not complete. // Message is not complete.
return false; return false;
@ -589,6 +626,7 @@ bool MidiInterface<SerialPort>::parse()
// to parse the rest of the message. // to parse the rest of the message.
return parse(); return parse();
#endif #endif
} }
else else
{ {
@ -597,12 +635,14 @@ bool MidiInterface<SerialPort>::parse()
{ {
// Reception of status bytes in the middle of an uncompleted message // Reception of status bytes in the middle of an uncompleted message
// are allowed only for interleaved Real Time message or EOX // are allowed only for interleaved Real Time message or EOX
if (isSystemRealtimeMessage(getTypeFromStatusByte(extracted))) switch (extracted)
{ {
if (shouldMessageBeForwarded(extracted)) case Clock:
{ case Start:
mSerial.write(extracted); case Continue:
} case Stop:
case ActiveSensing:
case SystemReset:
// Here we will have to extract the one-byte message, // Here we will have to extract the one-byte message,
// pass it to the structure for being read outside // pass it to the structure for being read outside
@ -617,16 +657,13 @@ bool MidiInterface<SerialPort>::parse()
mMessage.channel = 0; mMessage.channel = 0;
mMessage.valid = true; mMessage.valid = true;
return true; return true;
}
else if (extracted == 0xF7) break;
{
// End of Exclusive
case 0xF7:
if (mMessage.sysexArray[0] == SystemExclusive) if (mMessage.sysexArray[0] == SystemExclusive)
{ {
if (shouldMessageBeForwarded(SystemExclusive))
{
mSerial.write(extracted);
}
// Store the last byte (EOX) // Store the last byte (EOX)
mMessage.sysexArray[mPendingMessageIndex++] = 0xF7; mMessage.sysexArray[mPendingMessageIndex++] = 0xF7;
@ -641,27 +678,24 @@ bool MidiInterface<SerialPort>::parse()
resetInput(); resetInput();
return true; return true;
} }
}
else else
{ {
// Parse error: unexpected status byte // Well well well.. error.
resetInput();
return false;
}
break;
default:
break;
} }
} }
// Add extracted data byte to pending message // Add extracted data byte to pending message
if (mPendingMessage[0] == SystemExclusive) if (mPendingMessage[0] == SystemExclusive)
{
mMessage.sysexArray[mPendingMessageIndex] = extracted; mMessage.sysexArray[mPendingMessageIndex] = extracted;
}
else else
{
mPendingMessage[mPendingMessageIndex] = extracted; mPendingMessage[mPendingMessageIndex] = extracted;
}
if (shouldMessageBeForwarded(mPendingMessage[0]))
{
mSerial.write(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 >= (mPendingMessageExpectedLenght-1))
@ -743,18 +777,31 @@ bool MidiInterface<SerialPort>::inputFilter(Channel inChannel)
{ {
// This method handles recognition of channel // This method handles recognition of channel
// (to know if the message is destinated to the Arduino) // (to know if the message is destinated to the Arduino)
if (mMessage.type == InvalidType) if (mMessage.type == InvalidType)
return false; return false;
if (isChannelMessage(mMessage.type)) // First, check if the received message is Channel
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
{
// Then we need to know if we listen to it
if ((mMessage.channel == mInputChannel) ||
(mInputChannel == MIDI_CHANNEL_OMNI))
{ {
return (mInputChannel == MIDI_CHANNEL_OMNI ||
mInputChannel == mMessage.channel);
}
// Other message types are always received.
return true; return true;
} }
else
{
// We don't listen to this channel
return false;
}
}
else
{
// System messages are always received
return true;
}
}
// Private method: reset input attributes // Private method: reset input attributes
template<class SerialPort> template<class SerialPort>
@ -812,15 +859,15 @@ const byte* MidiInterface<SerialPort>::getSysExArray() const
return mMessage.sysexArray; return mMessage.sysexArray;
} }
/*! \brief Get the length of the System Exclusive array. /*! \brief Get the lenght of the System Exclusive array.
It is coded using data1 as LSB and data2 as MSB. It is coded using data1 as LSB and data2 as MSB.
\return The array's length, in bytes. \return The array's length, in bytes.
*/ */
template<class SerialPort> template<class SerialPort>
unsigned MidiInterface<SerialPort>::getSysExArrayLength() const unsigned int MidiInterface<SerialPort>::getSysExArrayLength() const
{ {
const unsigned size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1; const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size; return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
} }
@ -857,7 +904,7 @@ void MidiInterface<SerialPort>::setInputChannel(Channel inChannel)
made public so you can handle MidiTypes more easily. made public so you can handle MidiTypes more easily.
*/ */
template<class SerialPort> template<class SerialPort>
MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(StatusByte inStatus) MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(byte inStatus)
{ {
if ((inStatus < 0x80) || if ((inStatus < 0x80) ||
(inStatus == 0xF4) || (inStatus == 0xF4) ||
@ -868,38 +915,6 @@ MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(StatusByte inStatus)
else return (MidiType)inStatus; else return (MidiType)inStatus;
} }
template<class SerialPort>
byte MidiInterface<SerialPort>::getMessageLength(StatusByte inStatus)
{
const MidiType type = getTypeFromStatusByte(inStatus);
if (isSystemRealtimeMessage(type) || type == TuneRequest)
{
return 1;
}
else if (type == NoteOn ||
type == NoteOff ||
type == ControlChange ||
type == PitchBend ||
type == AfterTouchPoly ||
type == SongPosition)
{
return 3;
}
else if (type == ProgramChange ||
type == AfterTouchChannel ||
type == TimeCodeQuarterFrame ||
type == SongSelect)
{
return 2;
}
else if (type == SystemExclusive)
{
return 0xff; // SysEx messages can have variable length
}
return 0; // Unknown message type
}
template<class SerialPort> template<class SerialPort>
bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType) bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType)
{ {
@ -912,26 +927,6 @@ bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType)
inType == ProgramChange); inType == ProgramChange);
} }
template<class SerialPort>
bool MidiInterface<SerialPort>::isSystemRealtimeMessage(MidiType inType)
{
return inType == Clock ||
inType == Start ||
inType == Stop ||
inType == Continue ||
inType == ActiveSensing ||
inType == SystemReset;
}
template<class SerialPort>
bool MidiInterface<SerialPort>::isSystemCommonMessage(MidiType inType)
{
return inType == TimeCodeQuarterFrame ||
inType == SongPosition ||
inType == SongSelect ||
inType == TuneRequest;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_USE_CALLBACKS #if MIDI_USE_CALLBACKS
@ -949,7 +944,7 @@ template<class SerialPort> void MidiInterface<SerialPort>::setHandleAfterTouchCh
template<class SerialPort> void MidiInterface<SerialPort>::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSystemExclusive(void (*fptr)(byte* array, byte size)) { mSystemExclusiveCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleSystemExclusive(void (*fptr)(byte* array, byte size)) { mSystemExclusiveCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } template<class SerialPort> void MidiInterface<SerialPort>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; }
@ -1046,84 +1041,160 @@ void MidiInterface<SerialPort>::launchCallback()
// Thru // Thru
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if MIDI_BUILD_THRU #if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
/*! \addtogroup thru /*! \addtogroup thru
@{ @{
*/ */
/*! \brief Set the filter for thru mirroring
\param inThruFilterMode a filter mode
@see MidiFilterMode
*/
template<class SerialPort> template<class SerialPort>
void MidiInterface<SerialPort>::turnThruOn(ThruFlags inFlags) void MidiInterface<SerialPort>::setThruFilterMode(MidiFilterMode inThruFilterMode)
{ {
mThruFlags = inFlags; mThruFilterMode = inThruFilterMode;
if (mThruFilterMode != Off)
mThruActivated = true;
else
mThruActivated = false;
}
template<class SerialPort>
MidiFilterMode MidiInterface<SerialPort>::getFilterMode() const
{
return mThruFilterMode;
}
template<class SerialPort>
bool MidiInterface<SerialPort>::getThruState() const
{
return mThruActivated;
}
template<class SerialPort>
void MidiInterface<SerialPort>::turnThruOn(MidiFilterMode inThruFilterMode)
{
mThruActivated = true;
mThruFilterMode = inThruFilterMode;
} }
template<class SerialPort> template<class SerialPort>
void MidiInterface<SerialPort>::turnThruOff() void MidiInterface<SerialPort>::turnThruOff()
{ {
mThruFlags = ThruFilterFlags::off; mThruActivated = false;
} mThruFilterMode = Off;
/*! \brief Set the filter for thru mirroring
\param inThruFilterMode a filter mode
@see ThruFilterFlags
*/
template<class SerialPort>
void MidiInterface<SerialPort>::setThruFilterMode(ThruFlags inFlags)
{
mThruFlags = inFlags;
}
template<class SerialPort>
ThruFlags MidiInterface<SerialPort>::getThruFilterMode() const
{
return mThruFlags;
}
template<class SerialPort>
bool MidiInterface<SerialPort>::isThruOn() const
{
return mThruFlags != ThruFilterFlags::off;
}
template<class SerialPort>
bool MidiInterface<SerialPort>::hasThruFlag(byte inFlag) const
{
return mThruFlags & inFlag;
} }
/*! @} */ // End of doc group MIDI Thru /*! @} */ // End of doc group MIDI Thru
// ----------------------------------------------------------------------------- // This method is called upon reception of a message
// and takes care of Thru filtering and sending.
template<class SerialPort> template<class SerialPort>
bool MidiInterface<SerialPort>::shouldMessageBeForwarded(StatusByte inStatus) const void MidiInterface<SerialPort>::thruFilter(Channel inChannel)
{ {
const MidiType type = getTypeFromStatusByte(inStatus);
if (isChannelMessage(type)) /*
This method handles Soft-Thru filtering.
Soft-Thru filtering:
- All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off
- Channel messages are passed to the output whether their channel is matching the input channel and the filter setting
*/
// If the feature is disabled, don't do anything.
if (!mThruActivated || (mThruFilterMode == Off))
return;
// First, check if the received message is Channel
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
{ {
const Channel channel = (inStatus & 0x0F) + 1; const bool filter_condition = ((mMessage.channel == mInputChannel) ||
const bool forwardSame = hasThruFlag(ThruFilterFlags::channelSame); (mInputChannel == MIDI_CHANNEL_OMNI));
const bool forwardDiff = hasThruFlag(ThruFilterFlags::channelDifferent);
return (forwardSame && mInputChannel == channel) || // Now let's pass it to the output
(forwardDiff && mInputChannel != channel); switch (mThruFilterMode)
{
case Full:
send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return;
break;
case SameChannel:
if (filter_condition)
{
send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return;
} }
else if (isSystemRealtimeMessage(type)) break;
case DifferentChannel:
if (!filter_condition)
{ {
return hasThruFlag(ThruFilterFlags::systemRealtime); send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return;
} }
else if (isSystemCommonMessage(type)) break;
{ case Off:
return hasThruFlag(ThruFilterFlags::systemCommon); // Do nothing.
// Technically it's impossible to get there because
// the case was already tested earlier.
break;
default:
break;
} }
else if (type == SystemExclusive)
{
return hasThruFlag(ThruFilterFlags::systemExclusive);
} }
else else
{ {
// Unknown message or junk // Send the message to the output
return hasThruFlag(ThruFilterFlags::junk); switch (mMessage.type)
{
// Real Time and 1 byte
case Clock:
case Start:
case Stop:
case Continue:
case ActiveSensing:
case SystemReset:
case TuneRequest:
sendRealTime(mMessage.type);
return;
break;
case SystemExclusive:
// Send SysEx (0xF0 and 0xF7 are included in the buffer)
sendSysEx(mMessage.data1,mMessage.sysexArray,true);
return;
break;
case SongSelect:
sendSongSelect(mMessage.data1);
return;
break;
case SongPosition:
sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7));
return;
break;
case TimeCodeQuarterFrame:
sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
return;
break;
default:
break;
}
} }
} }

View File

@ -61,30 +61,14 @@ enum MidiType
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! Enumeration of Thru filter modes /*! Enumeration of Thru filter modes */
@see setThruFilterMode enum MidiFilterMode
*/
struct ThruFilterFlags
{ {
enum Off = 0, ///< Thru disabled (nothing passes through).
{ Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
off = 0 ///< Thru disabled (nothing passes through). SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
, channelSame = (1 << 0) ///< Only the messages on the Input Channel will be sent back.
, channelDifferent = (1 << 1) ///< All the messages but the ones on the Input Channel will be sent back.
, channel = channelSame | channelDifferent
, systemExclusive = (1 << 2)
, systemCommon = (1 << 3)
, systemRealtime = (1 << 4)
, system = systemExclusive | systemCommon | systemRealtime
, junk = (1 << 7) ///< Send mis-formated data back (unadvisable)
/// Fully enabled Thru (every incoming message is sent back).
, all = channel | systemExclusive | systemCommon | systemRealtime
}; };
};
typedef byte ThruFlags;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------