Working on thru.
This commit is contained in:
parent
8c9d289216
commit
d78cace1b2
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
239
src/MIDI.hpp
239
src/MIDI.hpp
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue