Working on thru.

This commit is contained in:
Francois Best 2013-07-07 14:13:32 +02:00
parent 8c9d289216
commit d78cace1b2
3 changed files with 146 additions and 126 deletions

View File

@ -117,7 +117,8 @@ public:
inline void setInputChannel(Channel inChannel); inline void setInputChannel(Channel inChannel);
public: public:
static inline MidiType getTypeFromStatusByte(byte inStatus); static inline MidiType getTypeFromStatusByte(StatusByte inStatus);
static inline byte getMessageLength(StatusByte inStatus);
static inline bool isChannelMessage(MidiType inType); static inline bool isChannelMessage(MidiType inType);
static inline bool isRealtimeMessage(MidiType inType); static inline bool isRealtimeMessage(MidiType inType);
@ -210,7 +211,6 @@ private:
private: private:
ThruFlags mThruFlags; ThruFlags mThruFlags;
bool mForwardCurrentMessage;
#endif // MIDI_BUILD_THRU #endif // MIDI_BUILD_THRU

View File

@ -485,7 +485,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 done, store it. // When the message is recomposed, notify the caller.
const byte extracted = mSerial.read(); const byte extracted = mSerial.read();
@ -511,10 +511,10 @@ bool MidiInterface<SerialPort>::parse()
// so the running status does not apply here. // so the running status does not apply here.
// It will be updated upon completion of this message. // It will be updated upon completion of this message.
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) if (mPendingMessageIndex >= (mPendingMessageExpectedLenght - 1))
{ {
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = (mPendingMessage[0] & 0x0F)+1; mMessage.channel = (mPendingMessage[0] & 0x0F) + 1;
mMessage.data1 = mPendingMessage[1]; mMessage.data1 = mPendingMessage[1];
// Save data2 only if applicable // Save data2 only if applicable
@ -530,71 +530,57 @@ bool MidiInterface<SerialPort>::parse()
} }
} }
switch (getTypeFromStatusByte(mPendingMessage[0])) mPendingMessageExpectedLenght = getMessageLength(mPendingMessage[0]);
if (mPendingMessageExpectedLenght == 1)
{ {
// 1 byte messages // RealTime and 1 byte messages -> send & handle rightaway
case Start: if (shouldMessageBeForwarded(mPendingMessage[0]))
case Continue: {
case Stop: mSerial.write(mPendingMessage[0]);
case Clock: }
case ActiveSensing:
case SystemReset:
case TuneRequest:
// Handle the message type directly here.
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = 0;
mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.valid = true;
// \fix Running Status broken when receiving Clock messages. mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
// Do not reset all input attributes, Running Status must remain unchanged. mMessage.channel = 0;
//resetInput(); mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.valid = true;
// We still need to reset these // \fix Running Status broken when receiving Clock messages.
mPendingMessageIndex = 0; // Do not reset all input attributes, Running Status must remain unchanged.
mPendingMessageExpectedLenght = 0; //resetInput();
return true; // We still need to reset these
break; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0;
// 2 bytes messages return true;
case ProgramChange: }
case AfterTouchChannel: else if (mPendingMessageExpectedLenght == 0xff)
case TimeCodeQuarterFrame: {
case SongSelect: // SysEx: The message can be any length
mPendingMessageExpectedLenght = 2; // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
break; mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;
mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = SystemExclusive;
// 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 else if (mPendingMessageExpectedLenght == 0)
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes {
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // Parse error
mRunningStatus_RX = InvalidType; resetInput();
mMessage.sysexArray[0] = SystemExclusive; return false;
break;
case InvalidType:
default:
// This is obviously wrong. Let's get the hell out'a here.
resetInput();
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;
@ -603,7 +589,6 @@ 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
{ {
@ -612,70 +597,74 @@ 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
switch (extracted) if (isRealtimeMessage(extracted))
{ {
case Clock: if (shouldMessageBeForwarded(extracted)
case Start: {
case Continue: mSerial.write(extracted);
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
// the MIDI class, and recompose the message it was // the MIDI class, and recompose the message it was
// interleaved into. Oh, and without killing the running status.. // interleaved into. Oh, and without killing the running status..
// This is done by leaving the pending message as is, // This is done by leaving the pending message as is,
// it will be completed on next calls. // it will be completed on next calls.
mMessage.type = (MidiType)extracted; mMessage.type = (MidiType)extracted;
mMessage.data1 = 0; mMessage.data1 = 0;
mMessage.data2 = 0; mMessage.data2 = 0;
mMessage.channel = 0;
mMessage.valid = true;
return true;
}
else if (extracted == 0xF7)
{
if (mMessage.sysexArray[0] == SystemExclusive)
{
if (shouldMessageBeForwarded(SystemExclusive)
{
mSerial.write(extracted);
}
// Store the last byte (EOX)
mMessage.sysexArray[mPendingMessageIndex++] = 0xF7;
mMessage.type = SystemExclusive;
// Get length
mMessage.data1 = mPendingMessageIndex & 0xFF;
mMessage.data2 = mPendingMessageIndex >> 8;
mMessage.channel = 0; mMessage.channel = 0;
mMessage.valid = true; mMessage.valid = true;
resetInput();
return true; return true;
}
break; }
else
// End of Exclusive {
case 0xF7: // Parse error: unexpected status byte
if (mMessage.sysexArray[0] == SystemExclusive)
{
// Store the last byte (EOX)
mMessage.sysexArray[mPendingMessageIndex++] = 0xF7;
mMessage.type = SystemExclusive;
// Get length
mMessage.data1 = mPendingMessageIndex & 0xFF;
mMessage.data2 = mPendingMessageIndex >> 8;
mMessage.channel = 0;
mMessage.valid = true;
resetInput();
return true;
}
else
{
// 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))
{ {
// "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
@ -689,7 +678,7 @@ bool MidiInterface<SerialPort>::parse()
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
if (isChannelMessage(mMessage.type)) if (isChannelMessage(mMessage.type))
mMessage.channel = (mPendingMessage[0] & 0x0F)+1; mMessage.channel = (mPendingMessage[0] & 0x0F) + 1;
else else
mMessage.channel = 0; mMessage.channel = 0;
@ -823,7 +812,7 @@ const byte* MidiInterface<SerialPort>::getSysExArray() const
return mMessage.sysexArray; return mMessage.sysexArray;
} }
/*! \brief Get the lenght of the System Exclusive array. /*! \brief Get the length 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.
@ -868,7 +857,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(byte inStatus) MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(StatusByte inStatus)
{ {
if ((inStatus < 0x80) || if ((inStatus < 0x80) ||
(inStatus == 0xF4) || (inStatus == 0xF4) ||
@ -879,6 +868,38 @@ MidiType MidiInterface<SerialPort>::getTypeFromStatusByte(byte inStatus)
else return (MidiType)inStatus; else return (MidiType)inStatus;
} }
template<class SerialPort>
byte MidiInterface<SerialPort>::getMessageLength(StatusByte inStatus)
{
const MidiType type = getTypeFromStatusByte(inStatus);
if (isRealtimeMessage(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)
{ {
@ -1088,7 +1109,7 @@ bool MidiInterface<SerialPort>::shouldMessageBeForwarded(StatusByte inStatus) co
else else
{ {
// Unknown message or junk // Unknown message or junk
return false; return hasThruFlag(ThruFilterFlags::junk);
} }
} }

View File

@ -61,27 +61,26 @@ enum MidiType
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/*! Enumeration of Thru filter modes */ /*! Enumeration of Thru filter modes
enum MidiFilterMode @see setThruFilterMode
{ */
Off = 0, ///< Thru disabled (nothing passes through).
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
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.
};
struct ThruFilterFlags struct ThruFilterFlags
{ {
enum enum
{ {
off = 0 off = 0 ///< Thru disabled (nothing passes through).
, channelSame = 1 , channelSame = (1 << 0) ///< Only the messages on the Input Channel will be sent back.
, channelDifferent = 2 , channelDifferent = (1 << 1) ///< All the messages but the ones on the Input Channel will be sent back.
, channel = channelSame | channelDifferent , channel = channelSame | channelDifferent
, system = 4 , systemExclusive = (1 << 2)
, realtime = 8 , systemCommon = (1 << 3)
, all = channel | system | realtime , 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 | system
}; };
}; };