feat: Add SysEx support for USB MIDI
This commit is contained in:
parent
5b96487362
commit
1ccd7c3ce8
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue