feat: Add SysEx support for USB MIDI

This commit is contained in:
Francois Best 2018-11-03 21:47:37 +01:00
parent 5b96487362
commit 1ccd7c3ce8
3 changed files with 195 additions and 2 deletions

View File

@ -80,8 +80,11 @@ struct CodeIndexNumbers
case midi::SystemReset:
return CodeIndexNumbers::singleByte;
// System Exclusive
case midi::SystemExclusive:
return CodeIndexNumbers::sysExStart;
case 0xf7:
return CodeIndexNumbers::sysExEnds1Byte;
// System Common Messages
case midi::TimeCodeQuarterFrame:
@ -119,7 +122,7 @@ struct CodeIndexNumbers
case sysExEnds2Bytes:
return 2;
case systemCommon1Byte:
case systemCommon1Byte: // also sysExEnds1Byte
case singleByte:
return 1;

View File

@ -40,6 +40,68 @@ bool composeTxPacket(Buffer& inBuffer, midiEventPacket_t& outPacket)
const byte cin = midi::CodeIndexNumbers::fromStatus(status);
const byte messageLength = midi::CodeIndexNumbers::getSize(cin);
if (status == 0xf0)
{
// Start of SysEx, check if it can end in one go.
if (bufferLength == 2 && inBuffer.peek(1) == 0xf7)
{
outPacket.header = midi::CodeIndexNumbers::sysExEnds2Bytes;
outPacket.byte1 = status;
outPacket.byte2 = 0xf7;
outPacket.byte3 = 0x00;
inBuffer.pop(2);
return true;
}
if (bufferLength >= 3 && inBuffer.peek(2) == 0xf7)
{
outPacket.header = midi::CodeIndexNumbers::sysExEnds3Bytes;
outPacket.byte1 = status;
outPacket.byte2 = inBuffer.peek(1);
outPacket.byte3 = 0xf7;
inBuffer.pop(3);
return true;
}
}
if ((status & 0x80) == 0x00)
{
// First byte is data, consider it's part of a running SysEx message.
// We look for the SysEx end byte in the next 2 bytes
// At this point, bufferLength should be 2 or more to continue.
if (bufferLength == 1)
{
return false; // Not enough data
}
if (bufferLength == 2)
{
const bool isSysExEnd = inBuffer.peek(1) == 0xf7;
if (!isSysExEnd)
{
return false; // Not enough data (eg: 0x12 0x42)
}
// eg: 0x42 0xf7
outPacket.header = midi::CodeIndexNumbers::sysExEnds2Bytes;
outPacket.byte1 = status;
outPacket.byte2 = inBuffer.peek(1);
outPacket.byte3 = 0x00;
inBuffer.pop(2);
return true;
}
else
{
// bufferLength > 2
const byte byte3 = inBuffer.peek(2);
outPacket.header = byte3 == 0xf7
? midi::CodeIndexNumbers::sysExEnds3Bytes
: midi::CodeIndexNumbers::sysExContinue;
outPacket.byte1 = status;
outPacket.byte2 = inBuffer.peek(1);
outPacket.byte3 = byte3;
inBuffer.pop(3);
return true;
}
}
if (bufferLength < messageLength) {
return false; // Not enough data in the buffer to compose a full packet.
}
@ -53,7 +115,6 @@ bool composeTxPacket(Buffer& inBuffer, midiEventPacket_t& outPacket)
return true;
// todo: handle interleaved RealTime messages
// todo: handle SysEx
}
template<typename Buffer>

View File

@ -304,6 +304,135 @@ TEST(MidiUsbPacketInterfaceTx, TimeCodeQuarterFrame)
EXPECT_EQ(0, buffer.getLength());
}
// System Exclusive --
TEST(MidiUsbPacketInterfaceTx, SysExNotEnoughData)
{
midiEventPacket_t packet;
Buffer buffer;
buffer.write(0x12);
buffer.write(0x42);
EXPECT_FALSE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(2, buffer.getLength());
}
TEST(MidiUsbPacketInterfaceTx, SysExSinglePacket)
{
midiEventPacket_t packet;
Buffer buffer;
// Two-byte SysEx (utterly useless)
buffer.write(0xf0);
buffer.write(0xf7);
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExEnds2Bytes, packet.header);
EXPECT_EQ(0xf0, packet.byte1);
EXPECT_EQ(0xf7, packet.byte2);
EXPECT_EQ(0x00, packet.byte3);
EXPECT_EQ(0, buffer.getLength());
// Single-data byte SysEx (non-spec conformant ?)
buffer.write(0xf0);
buffer.write(0x12);
buffer.write(0xf7);
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExEnds3Bytes, packet.header);
EXPECT_EQ(0xf0, packet.byte1);
EXPECT_EQ(0x12, packet.byte2);
EXPECT_EQ(0xf7, packet.byte3);
EXPECT_EQ(0, buffer.getLength());
}
TEST(MidiUsbPacketInterfaceTx, SysExTwoPackets)
{
midiEventPacket_t packet;
Buffer buffer;
const byte deviceIdentityRequest[6] = {
0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7
};
buffer.write(deviceIdentityRequest, 6);
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExStart, packet.header);
EXPECT_EQ(0xf0, packet.byte1);
EXPECT_EQ(0x7e, packet.byte2);
EXPECT_EQ(0x7f, packet.byte3);
EXPECT_EQ(3, buffer.getLength());
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExEnds3Bytes, packet.header);
EXPECT_EQ(0x06, packet.byte1);
EXPECT_EQ(0x01, packet.byte2);
EXPECT_EQ(0xf7, packet.byte3);
EXPECT_EQ(0, buffer.getLength());
}
TEST(MidiUsbPacketInterfaceTx, SysExMultiplePacketsEndingWith1Byte)
{
midiEventPacket_t packet;
Buffer buffer;
const byte message[7] = {
0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0xf7
};
buffer.write(message, 7);
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExStart, packet.header);
EXPECT_EQ(0xf0, packet.byte1);
EXPECT_EQ(0x01, packet.byte2);
EXPECT_EQ(0x02, packet.byte3);
EXPECT_EQ(4, buffer.getLength());
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExContinue, packet.header);
EXPECT_EQ(0x03, packet.byte1);
EXPECT_EQ(0x04, packet.byte2);
EXPECT_EQ(0x05, packet.byte3);
EXPECT_EQ(1, buffer.getLength());
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExEnds1Byte, packet.header);
EXPECT_EQ(0xf7, packet.byte1);
EXPECT_EQ(0x00, packet.byte2);
EXPECT_EQ(0x00, packet.byte3);
EXPECT_EQ(0, buffer.getLength());
}
TEST(MidiUsbPacketInterfaceTx, SysExMultiplePacketsEndingWith2Bytes)
{
midiEventPacket_t packet;
Buffer buffer;
const byte message[8] = {
0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xf7
};
buffer.write(message, 8);
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExStart, packet.header);
EXPECT_EQ(0xf0, packet.byte1);
EXPECT_EQ(0x01, packet.byte2);
EXPECT_EQ(0x02, packet.byte3);
EXPECT_EQ(5, buffer.getLength());
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExContinue, packet.header);
EXPECT_EQ(0x03, packet.byte1);
EXPECT_EQ(0x04, packet.byte2);
EXPECT_EQ(0x05, packet.byte3);
EXPECT_EQ(2, buffer.getLength());
EXPECT_TRUE(midi::composeTxPacket(buffer, packet));
EXPECT_EQ(midi::CodeIndexNumbers::sysExEnds2Bytes, packet.header);
EXPECT_EQ(0x06, packet.byte1);
EXPECT_EQ(0xf7, packet.byte2);
EXPECT_EQ(0x00, packet.byte3);
EXPECT_EQ(0, buffer.getLength());
}
// -----------------------------------------------------------------------------
TEST(MidiUsbPacketInterfaceRx, PacketParsing)