订单满 $200 免全球运费。
返回实验室

MIDI SysEx 协议实战

开发者2025-03-01

System Exclusive(SysEx)是 MIDI 协议中最强大的扩展机制。与固定的通道消息不同,SysEx 允许制造商定义任意长度和格式的自定义消息——从简单的设备查询到复杂的固件升级,一切皆有可能。本文从实战角度出发,结合 LdA MS-3 协议,讲解 SysEx 的核心概念和实际应用。

1. SysEx 消息基础

每条 SysEx 消息以 0xF0 开始、0xF7 结束。中间包含制造商标识(1 或 3 字节)和自定义数据。制造商标识由 MMA 或 AMEI 分配——知名品牌如 Roland (0x41)、Yamaha (0x43)、Korg (0x42) 各有其 ID。LdA 目前使用临时 ID 0x7D(需向 MMA 申请正式 ID)。

┌──────┬──────────┬──────────┬────────────┬────────┬──────┐
│ 0xF0 │ Maker ID │  Data    │    ...     │  0xF7  │
│ Start│ 1-3 bytes│ Variable │            │  End   │
└──────┴──────────┴──────────┴────────────┴────────┘

1-Byte IDs: 0x00-0x7F (e.g. Roland = 0x41)
3-Byte IDs: 0x00 0x00 0x00 format (0x00 0x20 0x00+)
重要须知

SysEx 消息中只能包含系统实时消息(如 Timing Clock 0xF8)。其他任何消息类型都会中断 SysEx 传输。此外,所有数据字节的最高位必须为 0(即值 < 0x80)。

2. 通用 SysEx 消息

MMA 定义了通用 SysEx 消息,使用特殊 ID 0x7E(非实时)和 0x7F(实时),所有设备都应当响应。最常用的通用 SysEx 是 Identity Request/Reply——这是检测 MIDI 设备身份的标准化方法。

身份查询 (Identity Request)

F0 7E [Channel] 06 01 F7

Channel: 0x7F = 广播(所有设备响应),0x00-0x0F = 指定通道

身份回复 (Identity Reply)

F0 7E [Channel] 06 02 [MakerID] [Family] [Model] [Version] F7

Maker ID:    1-byte = 0x7D (LdA)
Device Family: 2 bytes (MSB, LSB)
Device Model:  2 bytes
Version:       4 bytes (Major.Minor.Patch.Build)

3. LdA SysEx 协议详解

LdA 设备使用统一的 SysEx 协议格式,在标准 SysEx 基础上增加了设备 ID、命令字节和 XOR 校验和,以确保通信可靠性和数据完整性。

LdA 消息格式

F0 7D [DevID] [Command] [Data...] [Checksum] F7

Maker ID:  0x7D (LdA temporary)
Dev ID:    0x01=MS-3, 0x02=LS-4p3, 0xFF=broadcast
Command:   See command table below
Data:      0-N bytes, command-specific
Checksum:  XOR of Maker ID ⊕ DevID ⊕ Command ⊕ Data[0..N-1]

响应格式

所有命令的响应使用 ResponseCode = Command | 0x80(命令字节最高位置 1)。错误响应统一使用 ResponseCode = 0x8F。

命令集

范围类别重点命令
0x00-0x0F系统0x00=查询固件, 0x01=设备信息, 0x03=状态, 0x04=Ping
0x10-0x1F预设管理0x10=读预设, 0x11=写预设, 0x13=切换预设, 0x14=全备份, 0x16=恢复出厂
0x20-0x2FMIDI控制0x20=发送MIDI, 0x21=配置预设MIDI
0x30-0x3FLoop控制0x30=读Loop, 0x31=切换单个, 0x32=批量设置
0x40-0x4F系统配置0x40=读配置, 0x42=读MIDI通道
0xF0-0xFF固件升级0xF0=进Bootloader, 0xF3=传数据块

4. 校验和与错误处理

LdA 协议使用简单的 XOR 校验和来检测传输错误。计算方式:对 Maker ID (0x7D)、Dev ID、Command 和所有 Data 字节按位异或。接收方验证校验和,不匹配时返回 ErrorCode 0x03。

// Checksum calculation in C
uint8_t calc_checksum(uint8_t maker, uint8_t dev,
                       uint8_t cmd, uint8_t *data,
                       size_t len) {
  uint8_t cs = maker ^ dev ^ cmd;
  for (size_t i = 0; i < len; i++) cs ^= data[i];
  return cs;
}

// Example: Query Firmware (cmd=0x00, no data)
// cs = 0x7D ^ 0x01 ^ 0x00 = 0x7C
// Message: F0 7D 01 00 7C F7
错误码含义处理建议
0x01命令不支持检查命令字节
0x02数据格式错误检查数据字节范围(0-0x7F)
0x03校验和不匹配重新计算校验和,重试
0x04设备忙碌等待100ms后重试
0x05存储区错误检查存储/恢复出厂设置

5. 实战示例

示例 1: 查询 MS-3 固件版本

Send:    F0 7D 01 00 7C F7
              (checksum: 0x7D^0x01^0x00 = 0x7C)

Receive: F0 7D 01 80 01 02 03 00 7F F7
              (ResponseCode=0x80, v1.2.3.0, cs verified)

示例 2: 切换预设至预设 5

Send:    F0 7D 01 13 05 64 F7
              (cmd=0x13, PresetNum=5, cs=0x7D^1^0x13^5=0x64)

Receive: F0 7D 01 93 05 6F F7
              (ResponseCode=0x93, PresetNum=5)

示例 3: 打开 Loop 1,关闭 Loop 2、3

Send:    F0 7D 01 32 7F 00 00 4D F7
              (cmd=0x32, L1=0x7F(ON), L2=0x00, L3=0x00,
               cs=0x7D^1^0x32^0x7F^0^0=0x4D)

6. Web MIDI API 集成

以下展示在浏览器中使用 Web MIDI API 与 LdA 设备通信的完整示例。注意:Web MIDI API 需要 HTTPS 或 localhost,且用户必须主动授权(特别是 SysEx 访问)。

// 1. Request MIDI access with SysEx
const midi = await navigator.requestMIDIAccess({ sysex: true });

// 2. Find LdA device by name
function findLdADevice(midi) {
  const devices = { input: null, output: null };
  for (const [id, input] of midi.inputs) {
    if (input.name?.includes('MS-3') || input.name?.includes('LdA')) {
      devices.input = input;
      break;
    }
  }
  for (const [id, output] of midi.outputs) {
    if (output.name?.includes('MS-3') || output.name?.includes('LdA')) {
      devices.output = output;
      break;
    }
  }
  return devices;
}

// 3. Send LdA SysEx command
function sendLdACmd(output, devId, cmd, data = []) {
  const checksum = [0x7D, devId, cmd, ...data]
    .reduce((a, b) => a ^ b, 0);
  const msg = new Uint8Array([
    0xF0, 0x7D, devId, cmd, ...data, checksum, 0xF7
  ]);
  output.send(msg);
}

// 4. Listen for responses
function setupListener(input) {
  let sysexBuffer = [];
  input.onmidimessage = (event) => {
    const data = Array.from(event.data);
    if (data[0] === 0xF0) {
      sysexBuffer = data;
    } else if (sysexBuffer.length && data[data.length-1] !== 0xF7) {
      sysexBuffer.push(...data);
    }
    if (data[data.length-1] === 0xF7) {
      const full = sysexBuffer.length ? [...sysexBuffer, ...data] : data;
      const makerId = full[1];
      const devId = full[2];
      const responseCode = full[3];
      const cmd = responseCode & 0x7F;
      const payload = full.slice(4, -2);
      const checksum = full[full.length - 2];
      
      // Verify checksum
      const computed = full.slice(1, -2)
        .reduce((a, b) => a ^ b, 0);
      const valid = computed === checksum;
      
      console.log('LdA Response:', {
        cmd: '0x' + cmd.toString(16),
        payload,
        checksumValid: valid,
        isError: cmd === 0x0F
      });
      sysexBuffer = [];
    }
  };
}

// 5. Usage: Query firmware
const { input, output } = findLdADevice(midi);
setupListener(input);
sendLdACmd(output, 0x01, 0x00);  // DevID=MS-3, Cmd=Query Firmware

7. 最佳实践与陷阱

总是验证校验和

MIDI 传输可能出错(特别是通过 USB-MIDI 转换器)。始终在接收端验证校验和,不匹配时请求重发。

处理设备忙碌状态

设备处理 SysEx 命令可能需要时间(特别是写入操作)。收到 0x04 错误时,等待 100-500ms 后重试。批量操作间加入延迟。

分批传输大数据

预设备份或固件升级等大数据传输应分块进行(如每块 128 字节)。每块发送后等待设备响应,再发送下一块。

避免:数据字节 MSB 不为 0

SysEx 消息中所有数据字节必须 &lt; 0x80。如果需要传输 8-bit 数据,需要拆分为 7-bit 格式(每 7 个字节编码为 8 个 SysEx 字节)。

避免:忽略 SysEx 授权

Web MIDI API 中,SysEx 访问需要显式授权(`{ sysex: true }`)。部分浏览器可能阻止 SysEx 或要求用户交互。始终检查 `midi.access` 权限状态。

8. 总结

SysEx 协议为 MIDI 设备提供了超越标准消息的深度控制能力。LdA 协议通过规范的命令格式、XOR 校验和以及完善的错误码体系,构建了一套可靠且可扩展的设备通信方案。掌握 SysEx 意味着你可以编写自己的控制软件、实现自动化工作流、甚至扩展设备的功能边界。

📚 延伸阅读: MIDI 技术参考文档 完整的协议规范和技术细节