Working on thru.

This commit is contained in:
Francois Best 2013-07-07 12:52:27 +02:00
parent d8e0631399
commit 8c9d289216
3 changed files with 306 additions and 286 deletions

View File

@ -68,7 +68,7 @@ public:
inline void sendAfterTouch(DataByte inPressure,
Channel inChannel);
inline void sendSysEx(unsigned int inLength,
inline void sendSysEx(unsigned inLength,
const byte* inArray,
bool inArrayContainsBoundaries = false);
@ -76,7 +76,7 @@ public:
DataByte inValuesNibble);
inline void sendTimeCodeQuarterFrame(DataByte inData);
inline void sendSongPosition(unsigned int inBeats);
inline void sendSongPosition(unsigned inBeats);
inline void sendSongSelect(DataByte inSongNumber);
inline void sendTuneRequest();
inline void sendRealTime(MidiType inType);
@ -109,7 +109,7 @@ public:
inline DataByte getData1() const;
inline DataByte getData2() const;
inline const byte* getSysExArray() const;
inline unsigned int getSysExArrayLength() const;
inline unsigned getSysExArrayLength() const;
inline bool check() const;
public:
@ -119,6 +119,7 @@ public:
public:
static inline MidiType getTypeFromStatusByte(byte inStatus);
static inline bool isChannelMessage(MidiType inType);
static inline bool isRealtimeMessage(MidiType inType);
private:
bool inputFilter(Channel inChannel);
@ -130,8 +131,8 @@ private:
Channel mInputChannel;
byte mPendingMessage[3]; // SysEx are dumped into mMessage directly.
unsigned int mPendingMessageExpectedLenght;
unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads.
unsigned mPendingMessageExpectedLenght;
unsigned mPendingMessageIndex; // Extended to unsigned for larger SysEx payloads.
Message mMessage;
@ -150,7 +151,7 @@ public:
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size));
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
inline void setHandleSongPosition(void (*fptr)(unsigned int beats));
inline void setHandleSongPosition(void (*fptr)(unsigned beats));
inline void setHandleSongSelect(void (*fptr)(byte songnumber));
inline void setHandleTuneRequest(void (*fptr)(void));
inline void setHandleClock(void (*fptr)(void));
@ -175,7 +176,7 @@ private:
void (*mPitchBendCallback)(byte channel, int);
void (*mSystemExclusiveCallback)(byte * array, byte size);
void (*mTimeCodeQuarterFrameCallback)(byte data);
void (*mSongPositionCallback)(unsigned int beats);
void (*mSongPositionCallback)(unsigned beats);
void (*mSongSelectCallback)(byte songnumber);
void (*mTuneRequestCallback)(void);
void (*mClockCallback)(void);
@ -196,20 +197,20 @@ private:
#if MIDI_BUILD_THRU
public:
inline MidiFilterMode getFilterMode() const;
inline bool getThruState() const;
inline void turnThruOn(MidiFilterMode inThruFilterMode = Full);
inline void turnThruOn(ThruFlags inThruFlags = ThruFilterFlags::all);
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:
void thruFilter(byte inChannel);
inline bool shouldMessageBeForwarded(StatusByte inStatus) const;
private:
bool mThruActivated : 1;
MidiFilterMode mThruFilterMode : 7;
ThruFlags mThruFlags;
bool mForwardCurrentMessage;
#endif // MIDI_BUILD_THRU

View File

@ -70,7 +70,7 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
mRunningStatus_TX = InvalidType;
#endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS
#endif
#if MIDI_BUILD_INPUT
@ -86,16 +86,14 @@ void MidiInterface<SerialPort>::begin(Channel inChannel)
mMessage.data1 = 0;
mMessage.data2 = 0;
#endif // MIDI_BUILD_INPUT
#endif
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru
#if MIDI_BUILD_THRU
mThruFilterMode = Full;
mThruActivated = true;
#endif // Thru
mThruFlags = ThruFilterFlags::all;
#endif
}
@ -268,11 +266,10 @@ template<class SerialPort>
void MidiInterface<SerialPort>::sendPitchBend(int inPitchValue,
Channel inChannel)
{
const unsigned int bend = inPitchValue - MIDI_PITCHBEND_MIN;
const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN;
send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel);
}
/*! \brief Send a Pitch Bend message using a floating point value.
\param inPitchValue The amount of bend to send (in a floating point format),
between -1.0f (maximum downwards bend)
@ -293,11 +290,11 @@ void MidiInterface<SerialPort>::sendPitchBend(double inPitchValue,
\param inArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes
(start & stop SysEx) will NOT be sent
(and therefore must be included in the array).
default value for ArrayContainsBoundaries is set to 'false' for compatibility
default value for inArrayContainsBoundaries is set to 'false' for compatibility
with previous versions of the library.
*/
template<class SerialPort>
void MidiInterface<SerialPort>::sendSysEx(unsigned int inLength,
void MidiInterface<SerialPort>::sendSysEx(unsigned inLength,
const byte* inArray,
bool inArrayContainsBoundaries)
{
@ -305,14 +302,14 @@ void MidiInterface<SerialPort>::sendSysEx(unsigned int inLength,
{
mSerial.write(0xF0);
for (unsigned int i = 0; i < inLength; ++i)
for (unsigned i = 0; i < inLength; ++i)
mSerial.write(inArray[i]);
mSerial.write(0xF7);
}
else
{
for (unsigned int i = 0; i < inLength; ++i)
for (unsigned i = 0; i < inLength; ++i)
mSerial.write(inArray[i]);
}
@ -367,7 +364,7 @@ void MidiInterface<SerialPort>::sendTimeCodeQuarterFrame(DataByte inData)
\param inBeats The number of beats since the start of the song.
*/
template<class SerialPort>
void MidiInterface<SerialPort>::sendSongPosition(unsigned int inBeats)
void MidiInterface<SerialPort>::sendSongPosition(unsigned inBeats)
{
mSerial.write((byte)SongPosition);
mSerial.write(inBeats & 0x7F);
@ -400,20 +397,9 @@ void MidiInterface<SerialPort>::sendSongSelect(DataByte inSongNumber)
template<class SerialPort>
void MidiInterface<SerialPort>::sendRealTime(MidiType inType)
{
switch (inType)
if (isRealtimeMessage(inType) || inType == TuneRequest)
{
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);
break;
default:
// Invalid Real Time marker
break;
}
// Do not cancel Running Status for real-time messages as they can be
@ -470,22 +456,13 @@ bool MidiInterface<SerialPort>::read(Channel inChannel)
if (inChannel >= MIDI_CHANNEL_OFF)
return false; // MIDI Input disabled.
if (parse())
if (parse() && inputFilter(inChannel))
{
if (inputFilter(inChannel))
{
#if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
thruFilter(inChannel);
#endif
#if MIDI_USE_CALLBACKS
launchCallback();
#endif
return true;
}
}
return false;
}
@ -777,30 +754,17 @@ bool MidiInterface<SerialPort>::inputFilter(Channel inChannel)
{
// This method handles recognition of channel
// (to know if the message is destinated to the Arduino)
if (mMessage.type == InvalidType)
return false;
// 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))
if (isChannelMessage(mMessage.type))
{
return (mInputChannel == MIDI_CHANNEL_OMNI ||
mInputChannel == mMessage.channel);
}
// Other message types are always received.
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
@ -865,9 +829,9 @@ const byte* MidiInterface<SerialPort>::getSysExArray() const
\return The array's length, in bytes.
*/
template<class SerialPort>
unsigned int MidiInterface<SerialPort>::getSysExArrayLength() const
unsigned MidiInterface<SerialPort>::getSysExArrayLength() const
{
const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
const unsigned size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
}
@ -927,6 +891,17 @@ bool MidiInterface<SerialPort>::isChannelMessage(MidiType inType)
inType == ProgramChange);
}
template<class SerialPort>
bool MidiInterface<SerialPort>::isRealtimeMessage(MidiType inType)
{
return inType == Clock ||
inType == Start ||
inType == Stop ||
inType == Continue ||
inType == ActiveSensing ||
inType == SystemReset;
}
// -----------------------------------------------------------------------------
#if MIDI_USE_CALLBACKS
@ -944,7 +919,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>::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>::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; }
template<class SerialPort> void MidiInterface<SerialPort>::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = 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>::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; }
@ -1041,55 +1016,82 @@ void MidiInterface<SerialPort>::launchCallback()
// Thru
// -----------------------------------------------------------------------------
#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU)
#if MIDI_BUILD_THRU
/*! \addtogroup thru
@{
*/
/*! \brief Set the filter for thru mirroring
\param inThruFilterMode a filter mode
@see MidiFilterMode
*/
template<class SerialPort>
void MidiInterface<SerialPort>::setThruFilterMode(MidiFilterMode inThruFilterMode)
void MidiInterface<SerialPort>::turnThruOn(ThruFlags 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;
mThruFlags = inFlags;
}
template<class SerialPort>
void MidiInterface<SerialPort>::turnThruOff()
{
mThruActivated = false;
mThruFilterMode = Off;
mThruFlags = ThruFilterFlags::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
// -----------------------------------------------------------------------------
template<class SerialPort>
bool MidiInterface<SerialPort>::shouldMessageBeForwarded(StatusByte inStatus) const
{
if (isChannelMessage(inStatus))
{
const Channel channel = getChannel(inStatus);
const bool forwardSame = hasThruFlag(ThruFilterFlags::channelSame);
const bool forwardDiff = hasThruFlag(ThruFilterFlags::channelDifferent);
return (forwardSame && mInputChannel == channel) ||
(forwardDiff && mInputChannel != channel);
}
else if (isRealtimeMessage(inStatus))
{
return hasThruFlag(ThruFilterFlags::realtime);
}
else if (inStatus == SystemExclusive)
{
return hasThruFlag(ThruFilterFlags::system);
}
else
{
// Unknown message or junk
return false;
}
}
// This method is called upon reception of a message
// and takes care of Thru filtering and sending.
template<class SerialPort>

View File

@ -70,6 +70,23 @@ enum MidiFilterMode
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
};
struct ThruFilterFlags
{
enum
{
off = 0
, channelSame = 1
, channelDifferent = 2
, channel = channelSame | channelDifferent
, system = 4
, realtime = 8
, all = channel | system | realtime
};
};
typedef byte ThruFlags;
// -----------------------------------------------------------------------------
/*! \brief Enumeration of Control Change command numbers.