All wire formats were previously invented; now based on the normative spec
PDF and KissBox Wireshark dissector (docs/midi2_wireshark_dissector.lua):
- NetworkMidi2Protocol: command codes are single bytes (0x01–0xFF), not
ushorts; magic appears once per datagram not per-command; 4-byte command
headers [code][payloadWordLen][CSD1][CSD2]; Invitation uses word-padded
UTF-8 name + ASCII product ID with nameWordCount in CSD1; Ping payload is
uint32 PingId (1 word); Bye carries reason in CSD1 with no payload;
removed non-existent InvitationRefused command (rejection uses Bye reason
codes 0x40–0x45); added ByeReason enum with all spec-defined codes
- UmpDataCommand: sequence number is uint16 in CSD1:CSD2 of the command
header (not a uint32 prefix in the payload); payload is pure UMP words
- UmpFec: FEC works by packing multiple 0xFF UMP Data commands in one UDP
datagram (oldest history first, current last) — not a proprietary block
format; SendHistory now uses ushort sequence numbers
- UmpHelpers: fix GetWordCount MTSize table — was missing types 8–10 (2
words each) and 11–12 (3 words each), and had type D wrong (4 not 2)
- NetworkMidi2Session: sequence numbers are ushort; FEC via
UmpFec.EncodeDatagram; removed InitiatorToken (not in spec); Bye now
carries ByeReason; receive loop processes all UmpDataPackets per datagram
- All unit tests rewritten to verify correct wire format (77 tests pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>