跳转至
MIGRATED

通信协议设计 (v4.0)


1. 概述

本协议描述车载音频 DSP 调音工具的三层通信体系:

  1. 前端 ↔ 后端:Vue3 前端与 ASP.NET Core 8 后端之间的 WebSocket JSON 协议,以及 REST 补充接口
  2. 后端 ↔ DSP:后端与 DSP 硬件(或 PC 仿真)之间的 TLV 二进制帧协议
  3. 参数 ID 命名规范:贯穿前后端和 DSP 的参数寻址格式
┌──────────────────────────────────────────┐
│               Frontend (Vue3 + Pinia)    │
│  ┌────────────┐ ┌────────┐ ┌──────────┐  │
│  │Chain Builder│ │ Tuning │ │Test/Verify│ │
│  └────────────┘ └────────┘ └──────────┘  │
└────────────────┬─────────────────────────┘
                 │ JSON / WebSocket (ws://0.0.0.0:5000/ws)
                 │ HTTP REST (/api/*)
┌────────────────▼─────────────────────────┐
│               Backend (ASP.NET Core 8)   │
│  ┌──────────┐ ┌──────┐ ┌─────────────┐   │
│  │WebSocket │ │ REST │ │AudioEngine  │   │
│  │ Handler  │ │ API  │ │(NAudio+DLL) │   │
│  └──────────┘ └──────┘ └─────────────┘   │
│  ┌────────────────────────────────────┐   │
│  │        DebugService                │   │
│  │  (metrics / log_stream / WAV)      │   │
│  └────────────────────────────────────┘   │
└──────┬───────────────────────┬────────────┘
       │ Binary / TCP or Serial │ DLL call (in-process)
┌──────▼──────┐         ┌──────▼──────────────┐
│ DSP Hardware│         │  PC Sim             │
│  (Embedded) │         │  DynamicChain.dll   │
└─────────────┘         └─────────────────────┘

连接信息

项目 值
协议 WebSocket
地址 ws://0.0.0.0:5000/ws
文本编码 UTF-8
最大帧大小 65536 bytes
心跳周期 5 秒(前端发送 ping,后端回 pong)
重连策略 断线后 3s 自动重试,指数退避最大 30s

消息格式规范

每条 WebSocket 消息必须包含 type 字段:

{ "type": "<消息类型>", ...其他字段 }

2. 前端 → 后端 消息(完整列表)

2.1 基础通信

ping — 心跳探测

{ "type": "ping" }

响应:pong


2.2 参数操作

set_param — 设置单个参数

{
  "type": "set_param",
  "instanceId": "gain#1",
  "paramId": "gainDb",
  "value": -3.5,
  "channel": 0
}
字段 类型 必填 说明
instanceId string 是 模块实例 ID(格式:moduleType#index)
paramId string 是 参数名(不含通道后缀)
value number / bool / string 是 参数值(UI 单位)
channel int 可选 通道索引(存在时 key 追加 #channel)

后端存储 key:${instanceId}.${paramId} 或 ${instanceId}.${paramId}#${channel}

响应:set_param_ack

副作用: 1. 触发 DSP 二进制帧发送(异步,不阻塞响应) 2. 广播 param_update 给其他所有已连接客户端


set_params — 兼容别名

与 set_param 相同格式,后端视为等价处理(向后兼容)。


get_param — 查询单个参数

{
  "type": "get_param",
  "instanceId": "gain#1",
  "paramId": "gainDb#0"
}

响应:get_param_ack


get_all_params — 查询实例全部参数

{
  "type": "get_all_params",
  "instanceId": "gain#1"
}

用于前端重连后恢复该模块的所有参数状态。

响应:get_all_params_ack


2.3 链路管理

从 v3.0 起,write_link 消息的 payload 字段从旧版扁平 DSPLink 格式升级为 LinkConfig v2.2 格式,支持多端口 mixer 节点与子图(SubGraph)嵌套。完整示例如下:

{
  "type": "write_link",
  "payload": {
    "version": "2.2",
    "rootChainId": "root",
    "global": { "channels": 20, "sampleRate": 48000, "frameSize": 240 },
    "chains": {
      "root": {
        "id": "root",
        "name": "Main Chain",
        "nodes": [
          {
            "instanceId": "gain#1",
            "moduleType": "channel_gain_v1",
            "order": 0,
            "enabled": true,
            "position": {"x": 100, "y": 200},
            "ports": [
              {"id":"input","direction":"input","channels":-1,"sampleRate":-1,"label":"Input","required":true},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Output","required":true}
            ]
          },
          {
            "instanceId": "mixer#1",
            "moduleType": "audio_mixer_v1",
            "order": 1,
            "enabled": true,
            "position": {"x": 400, "y": 200},
            "ports": [
              {"id":"input_0","direction":"input","channels":-1,"sampleRate":-1,"label":"Ch A","required":true},
              {"id":"input_1","direction":"input","channels":-1,"sampleRate":-1,"label":"Ch B","required":false},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Mix","required":true}
            ]
          },
          {
            "instanceId": "group#1",
            "moduleType": "subgraph",
            "subGraphId": "eq_stage",
            "position": {"x": 700, "y": 200},
            "ports": [
              {"id":"input","direction":"input","channels":2,"sampleRate":48000,"label":"In","required":true},
              {"id":"output","direction":"output","channels":2,"sampleRate":48000,"label":"Out","required":true}
            ]
          }
        ],
        "edges": [
          {"id":"e1","fromModule":"gain#1","fromPort":"output","toModule":"mixer#1","toPort":"input_0","channels":2,"sampleRate":48000},
          {"id":"e2","fromModule":"mixer#1","fromPort":"output","toModule":"group#1","toPort":"input","channels":2,"sampleRate":48000}
        ]
      },
      "eq_stage": {
        "id": "eq_stage",
        "name": "EQ Stage",
        "externalPorts": [
          {"id":"input","direction":"input","channels":2,"sampleRate":48000,"label":"In","required":true},
          {"id":"output","direction":"output","channels":2,"sampleRate":48000,"label":"Out","required":true}
        ],
        "nodes": [
          {
            "instanceId": "eq#1",
            "moduleType": "common_eq_v1",
            "order": 0,
            "enabled": true,
            "position": {"x": 200, "y": 150},
            "ports": [
              {"id":"input","direction":"input","channels":-1,"sampleRate":-1,"label":"In","required":true},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Out","required":true}
            ]
          }
        ],
        "edges": []
      }
    }
  }
}

后端处理: 1. 将 payload 序列化为 JSON,写入 ./data/current_link.json 2. 调用 ChainFlattener 展平所有子图,构造 set_link v3 二进制帧下发到 DSP 3. 批量提取各链路节点参数,更新 paramStore 4. 下发各模块初始参数(set_param 帧序列)

响应:write_link_ack


ChainDefinition 字段说明

字段 类型 必填 说明
id string 是 链路唯一标识符,与 chains 对象的 key 相同
name string 否 可读名称,仅供 UI 显示
nodes ChainNode[] 是 本链路内的所有节点列表
edges ChainEdge[] 是 本链路内的所有连线列表
externalPorts PortDescriptor[] 否 仅子图链路需要,定义子图对外暴露的端口

ChainNode 字段说明

字段 类型 必填 说明
instanceId string 是 模块实例 ID,同一链路内唯一
moduleType string 是 模块类型字符串;子图节点固定为 "subgraph"
subGraphId string 子图节点必填 指向 chains 中对应子图的 id
order int 否 处理顺序(升序),子图节点可省略
enabled bool 否 模块是否启用,默认 true
position {x, y} 否 画布坐标,仅供 UI 使用
ports PortDescriptor[] 是 本节点的端口描述列表

PortDescriptor 字段说明

字段 类型 说明
id string 端口 ID,在同一节点内唯一(如 "input_0"、"output")
direction string "input" 或 "output"
channels int 端口通道数;-1 表示继承全局/上游通道数
sampleRate int 端口采样率(Hz);-1 表示继承
label string UI 显示标签
required bool 是否为必连端口;false 表示可选输入

SubGraphNode 补充说明

SubGraphNode 是 ChainNode 的子类型,当 moduleType == "subgraph" 时生效:

字段 类型 说明
subGraphId string 引用 LinkConfig.chains 中的子图 id
ports PortDescriptor[] 与被引用子图的 externalPorts 一一对应,通道/采样率已解析

后端 ChainFlattener 会将子图节点递归展开为平坦模块列表,子图内部节点的 instanceId 在展平后采用点分路径格式(如 "group#1.eq#1")。

ChainEdge 字段说明

字段 类型 必填 说明
id string 是 边的唯一 ID(同一链路内唯一)
fromModule string 是 源节点 instanceId
fromPort string 是 源节点端口 ID
toModule string 是 目标节点 instanceId
toPort string 是 目标节点端口 ID
channels int 否 该连线上已解析的通道数
sampleRate int 否 该连线上已解析的采样率(Hz)

{ "type": "read_link" }

从 ./data/current_link.json 加载链路 JSON。

响应:read_link_ack


2.4 DSP 连接

connect_dsp — 连接 DSP 硬件

TCP 模式:

{
  "type": "connect_dsp",
  "protocol": "tcp",
  "host": "192.168.1.100",
  "port": 9000
}

串口模式:

{
  "type": "connect_dsp",
  "protocol": "serial",
  "comPort": "COM3",
  "baudRate": 115200
}

响应:connect_dsp_ack

副作用:连接成功后广播 dsp_status 给所有客户端


disconnect_dsp — 断开 DSP 连接

{ "type": "disconnect_dsp" }

响应:disconnect_dsp_ack

副作用:广播 dsp_status 给所有客户端


get_status — 查询后端及 DSP 状态

{ "type": "get_status" }

响应:dsp_status


get_dsp_chain — 从 DSP 读取链路结构(仅 test_verify 模式)

从 DSP 实时读取当前运行的链路结构(只读):

{ "type": "get_dsp_chain" }

响应:get_dsp_chain_ack


2.5 工作模式

set_mode — 切换工作模式

切换工作模式,广播给所有连接客户端:

{
  "type": "set_mode",
  "mode": "tuning"
}
mode 取值 说明
chain_builder 链路构建模式(全功能,可增删模块、修改连接)
tuning 调音模式(链路锁定,仅 tunable 参数可调)
test_verify 测试验证模式(链路只读,可读写 tunable 参数,可读取 DSP 链路)

响应:mode_changed(广播给所有客户端)


2.6 PC 仿真

sim_start — 启动 PC 端算法仿真

后端调用 DynamicChain.dll + NAudio 启动音频仿真处理:

{
  "type": "sim_start",
  "input": {
    "type": "soundcard",
    "deviceName": "立体声混音 (Realtek HD Audio)"
  },
  "output": {
    "type": "soundcard",
    "deviceName": "扬声器 (Realtek HD Audio)"
  }
}

文件输入模式:

{
  "type": "sim_start",
  "input": { "type": "file", "filePath": "./test_audio/white_noise_2ch.wav" },
  "output": { "type": "soundcard", "deviceName": "扬声器" }
}

响应:sim_status(广播给所有客户端)


sim_stop — 停止 PC 端算法仿真

{ "type": "sim_stop" }

响应:sim_status(广播给所有客户端)


list_audio_devices — 枚举系统音频设备

{ "type": "list_audio_devices" }

响应:audio_devices_ack


2.7 调试

debug_subscribe — 订阅调试指标周期推送

{
  "type": "debug_subscribe",
  "intervalMs": 500
}

后端开始按 intervalMs 间隔周期推送 debug_metrics。


debug_unsubscribe — 取消订阅调试指标

{ "type": "debug_unsubscribe" }

wav_capture_start — 在指定模块节点启动 WAV 音频抓取

{
  "type": "wav_capture_start",
  "instanceId": "ut_delay_20ch#1",
  "capturePoint": "output",
  "durationMs": 1000
}
字段 说明
instanceId 目标模块实例 ID
capturePoint "input" 或 "output"
durationMs 抓取时长(毫秒)

响应:wav_capture_ack(立即返回 captureId),抓取完成后推送 wav_capture_done


3. 后端 → 前端 消息(完整列表)

3.1 基础响应

pong — 心跳响应

{
  "type": "pong",
  "timestamp": 1711900000000
}

timestamp 为 UTC 毫秒时间戳,前端用于计算 RTT。


error — 错误响应

{
  "type": "error",
  "message": "未知指令类型: unknown_cmd"
}

3.2 参数响应

set_param_ack — 参数设置确认

成功时:

{
  "type": "set_param_ack",
  "instanceId": "gain#1",
  "paramId": "gainDb#0",
  "success": true
}

失败时:

{
  "type": "set_param_ack",
  "instanceId": "gain#1",
  "paramId": "gainDb#0",
  "success": false,
  "error": "DSP 未连接"
}


get_param_ack — 参数查询响应

{
  "type": "get_param_ack",
  "instanceId": "gain#1",
  "paramId": "gainDb#0",
  "value": "-3.5",
  "success": true
}

注:value 字段为字符串(paramStore 以字符串存储所有值)。


get_all_params_ack — 全量参数响应

{
  "type": "get_all_params_ack",
  "instanceId": "gain#1",
  "params": {
    "enable": "true",
    "smoothTime": "10",
    "gainDb#0": "-3.0",
    "gainDb#1": "-3.0",
    "mute#0": "false",
    "mute#1": "false",
    "phase#0": "false"
  }
}

键为去掉 ${instanceId}. 前缀后的 paramId,值均为字符串。


param_update — 多端参数广播

某客户端设置参数后,后端广播给其余所有已连接客户端:

{
  "type": "param_update",
  "instanceId": "gain#1",
  "paramId": "gainDb#0",
  "value": "-3.5"
}

3.3 链路响应

成功时:

{
  "type": "write_link_ack",
  "success": true
}

失败时:

{
  "type": "write_link_ack",
  "success": false,
  "error": "链路格式无效"
}


成功时:

{
  "type": "read_link_ack",
  "success": true,
  "link": { "version": "2.2", "rootChainId": "root", "chains": {...} }
}

文件不存在时:

{
  "type": "read_link_ack",
  "success": false,
  "link": null
}


get_dsp_chain_ack — DSP 链路读取响应

{
  "type": "get_dsp_chain_ack",
  "success": true,
  "link": { "version": "2.2", "rootChainId": "root", "chains": {...} }
}

3.4 DSP 状态

connect_dsp_ack — DSP 连接结果

成功时:

{
  "type": "connect_dsp_ack",
  "success": true,
  "protocol": "tcp",
  "target": "192.168.1.100:9000",
  "error": null
}

失败时:

{
  "type": "connect_dsp_ack",
  "success": false,
  "protocol": "tcp",
  "target": "",
  "error": "连接超时"
}


disconnect_dsp_ack — DSP 断开确认

{
  "type": "disconnect_dsp_ack",
  "success": true
}

dsp_status — DSP 状态推送

客户端连接时主动推送一次,DSP 连接/断开时全体广播:

{
  "type": "dsp_status",
  "connected": true,
  "protocol": "tcp",
  "target": "192.168.1.100:9000"
}

3.5 模式

mode_changed — 模式变更广播

{
  "type": "mode_changed",
  "mode": "tuning"
}

3.6 PC 仿真

sim_status — 仿真状态推送

仿真状态变更时广播给所有客户端:

正常运行时:

{
  "type": "sim_status",
  "running": true,
  "status": "running",
  "inputDevice": "立体声混音 (Realtek HD Audio)",
  "outputDevice": "扬声器 (Realtek HD Audio)"
}

失败时:

{
  "type": "sim_status",
  "running": false,
  "status": "error",
  "error": "WASAPI 独占模式被其他程序占用"
}


audio_devices_ack — 音频设备列表

{
  "type": "audio_devices_ack",
  "inputs": [
    { "id": "dev-001", "name": "立体声混音 (Realtek HD Audio)" },
    { "id": "dev-002", "name": "麦克风阵列 (USB Audio)" }
  ],
  "outputs": [
    { "id": "dev-010", "name": "扬声器 (Realtek HD Audio)" },
    { "id": "dev-011", "name": "耳机 (USB Audio)" }
  ]
}

3.7 调试推送

debug_metrics — 调试指标周期推送

订阅后按 intervalMs 间隔推送:

{
  "type": "debug_metrics",
  "timestamp": 1711900000000,
  "latency": {
    "totalMs": 22.5,
    "byModule": [
      { "instanceId": "channel_gain#1",  "latencyMs": 0.0 },
      { "instanceId": "ut_delay_20ch#1", "latencyMs": 20.0 }
    ]
  },
  "compute": [
    { "instanceId": "channel_gain#1",  "cpuUs": 45 },
    { "instanceId": "ut_delay_20ch#1", "cpuUs": 95 }
  ],
  "memory": {
    "poolUsed": 204800,
    "poolTotal": 1048576,
    "byModule": [
      { "instanceId": "channel_gain#1",  "bytes": 440 },
      { "instanceId": "ut_delay_20ch#1", "bytes": 77056 }
    ]
  }
}

log_stream — 实时日志流推送

算法库或后端产生的日志实时推送:

{
  "type": "log_stream",
  "level": "INFO",
  "source": "dsp",
  "message": "DynamicChain: LoadConfig done, 2 modules",
  "timestamp": 1711900000000
}
level 说明
DEBUG 低优先级诊断信息(高频,默认不显示)
INFO 正常运行信息
WARN 警告(如 MemPool 占用超过 80%)
ERROR 错误事件
source 说明
dsp 来自 DynamicChain 算法库
backend 来自 ASP.NET Core 后端

wav_capture_ack — WAV 抓取启动确认

{
  "type": "wav_capture_ack",
  "captureId": "a3f2b1c0"
}

wav_capture_done — WAV 抓取完成通知

{
  "type": "wav_capture_done",
  "captureId": "a3f2b1c0",
  "fileSizeBytes": 2097152
}

前端收到后通过 REST 接口下载:GET /api/debug/wav/a3f2b1c0


4. REST 接口

方法 路径 说明
GET /api/status 查询后端及 DSP 状态(JSON)
GET /api/link 获取当前链路快照(JSON)
GET /api/debug/wav/{captureId} 下载 WAV 抓取文件(二进制流)
GET /api/debug/metrics 获取最近一次调试指标快照(JSON)
GET /api/audio/devices 获取系统音频设备列表(JSON)

/api/status 响应示例:

{
  "status": "online",
  "clients": 2,
  "version": "3.0",
  "dsp": {
    "connected": true,
    "protocol": "tcp",
    "target": "192.168.1.100:9000"
  },
  "paramCount": 48
}


5. DSP 二进制帧协议(后端 ↔ DSP)

后端与 DSP 硬件之间使用 TLV 风格二进制帧。

5.1 通用帧结构

Offset  Size  字段
──────  ────  ──────────────────────────────────────
0       1     帧头 0x55
1       1     帧头 0xAA
2       1     命令字 CMD
3       2     序列号 SEQ (uint16 LE, 每帧自增,溢出归零)
5       2     数据长度 LEN (uint16 LE,仅 DATA 段字节数)
7       N     数据段 DATA(由 CMD 决定格式)
7+N     1     校验 CRC8(计算范围:偏移 0 ~ 7+N-1)

总帧长 = 8 + N bytes


5.2 命令字定义

CMD 值 方向 说明
set_param 0x01 后端→DSP 设置单个模块参数
set_link 0x02 后端→DSP 下发完整链路配置
ping 0x03 后端→DSP 心跳探测(保留)
param_ack 0x81 DSP→后端 参数设置确认(保留)
link_ack 0x82 DSP→后端 链路加载确认(保留)

5.3 CMD = 0x01:set_param DATA 格式

Offset    Size  字段
──────    ────  ──────────────────────────────────────
0         1     instanceId 字节长度 (N1)
1         N1    instanceId (UTF-8,如 "gain#1")
1+N1      1     paramId 字节长度 (N2)
2+N1      N2    paramId (UTF-8,已含通道后缀,如 "gainDb#0")
2+N1+N2   1     值类型 VALUE_TYPE
3+N1+N2   V     值字节 VALUE(长度由 VALUE_TYPE 决定)

值类型编码:

VALUE_TYPE 值 字节数 说明
float32 0x01 4 IEEE 754 单精度,LE
int32 0x02 4 有符号整数,LE
uint8 0x03 1 布尔值(1=true, 0=false)或 uint8 整数
uint32 0x04 4 无符号整数,LE(用于延迟 samples 等)
string 0x05 N UTF-8 字符串(无长度前缀,由 LEN 推导)

后端类型推断规则(SerializeValue() 函数): 1. 如果 paramId 包含关键字 "Samples" 或 "Idx" → uint32 (0x04) 2. "true" / "false" → uint8 (0x03) 3. 否则尝试 float.TryParse → float32 (0x01) 4. 否则尝试 int.TryParse → int32 (0x02) 5. 其余 → string (0x05)


v3 采用固定宽度的模块表和连线表,后端 ChainFlattener 负责将 LinkConfig v2.2 中的子图递归展平后再构造此帧。DSP 仅见到平坦的模块列表,对子图结构无感知。

set_link v3 binary frame (CMD=0x02):

Header (6 bytes):
  [CMD: uint8 = 0x02]
  [DATA_LEN: uint32 LE]   -- bytes following CMD byte
  [VERSION: uint8 = 0x03]
  [NUM_MODULES: uint8]
  [NUM_CONNECTIONS: uint8]
  [RESERVED: uint8]

Module Table (NUM_MODULES × 88 bytes per entry):
  Per module entry (88 bytes total):
    instanceId:  char[16]      null-terminated, e.g. "gain#1\0..."
    typeNumId:   uint32 LE     e.g. 0x10010001
    numPorts:    uint8         number of valid entries in ports[8]
    RESERVED:    uint8[3]
    ports[8]:                  8 × 8 bytes = 64 bytes
      Per port (8 bytes):
        portId:    char[6]     null-terminated, e.g. "input\0", "out\0.."
        direction: uint8       0=input, 1=output
        required:  uint8       0=optional, 1=required
        channels:  uint16 LE   0=inherit
        sampleRate: uint16 LE  sample rate / 1000 (e.g. 48 for 48000 Hz) — saves space

  Note: 16 + 4 + 4 + 64 = 88 bytes per module
  Maximum 32 modules → 32 × 88 = 2816 bytes

Connection Table (NUM_CONNECTIONS × 8 bytes per entry):
  Per connection entry (8 bytes):
    fromModuleIdx: uint8   index into module table
    fromPortIdx:   uint8   index into fromModule's ports[] array
    toModuleIdx:   uint8
    toPortIdx:     uint8
    channels:      uint16 LE   resolved channel count on this wire
    sampleRate:    uint16 LE   sample rate / 1000

  Maximum 64 connections → 64 × 8 = 512 bytes

Total maximum frame payload: 6 + 2816 + 512 = 3334 bytes

设计说明: - 连线表使用模块索引(而非字符串 ID)引用模块,节省帧空间并加快 DSP 端解析速度 - 子图由后端 ChainFlattener 展平;DSP 仅见到平坦模块列表,不感知子图层次结构 - 模块表中的 instanceId 字符串采用点分展平命名(如 "group#1.eq#1"),用于参数寻址(例如 "group#1.eq#1.gainDb#0"),与前端展平后的路径保持一致

DSP 端解析后的行为: 1. 停止音频处理(静音保护) 2. 调用 DynamicChain_Reset() 清空旧链路 3. 按顺序 DynamicChain_AddModule() 重建链路 4. 恢复音频处理


向后兼容说明:v1 与 v2 格式(旧固件)

以下 v1/v2 格式仅供尚未升级至 v3 的旧固件参考。后端可根据 targetVersion 配置决定使用哪种格式。

v1 格式(原始版本):

Offset  Size   字段
──────  ─────  ──────────────────────────────────────
0       2      模块数量 (uint16 LE)
2       ...    模块条目列表(按 order 升序排列)

每个模块条目(v1):
  0     4      moduleTypeId (uint32 LE)
  4     1      instanceId 字节长度 (N)
  5     N      instanceId (UTF-8,如 "gain#1")

v2 格式(含端口配置字段):

Offset  Size   字段
──────  ─────  ──────────────────────────────────────
0       2      模块数量 (uint16 LE)
2       ...    模块条目列表(按 order 升序排列)

每个模块条目(v2):
  0     4      moduleTypeId (uint32 LE)
  4     1      instanceId 字节长度 (N)
  5     N      instanceId (UTF-8,如 "gain#1")
  5+N   2      inputChannels  (uint16 LE)
  7+N   2      outputChannels (uint16 LE)
  9+N   4      inputSampleRate  (uint32 LE)
  13+N  4      outputSampleRate (uint32 LE)

DSP 可根据帧 LEN 字段推断版本(v2 每个模块条目比 v1 多 12 字节)。


模块类型 ID 定义:

moduleType 字符串 moduleTypeId 说明
channel_gain_v1 0x10010001 20 通道增益
adaptive_gain_v1 0x10010002 自适应增益
speaker_level_v1 0x10010003 扬声器电平
headroom_v1 0x10010004 Headroom 增益
audio_mixer_v1 0x10010005 多输入混音器
ut_delay_20ch_v1 0x10020001 20 通道延迟
common_eq_v1 0x10030001 通用参数 EQ
eq_4ch_v1 0x10030002 4 通道 EQ
source_eq_v1 0x10030003 音源 EQ
speed_eq_v1 0x10030004 车速 EQ
ut_eq_20ch_v1 0x10030005 20 通道 EQ
limiter_v1 0x10040001 限幅器
mdrc_v1 0x10040002 多频段压缩
surround_v1 0x10050001 环绕声
upmixer_v1 0x10050002 上混(如 2ch→12ch)
distortion_v1 0x10060001 失真效果
virtual_bass_v1 0x10060002 虚拟低音
loudness_v1 0x10060003 响度补偿

5.5 CRC8 算法

多项式 0x07(CRC-8/SMBUS),计算范围:偏移 0 到 DATA 末尾(不含 CRC 字节本身):

byte Crc8(byte[] data, int start, int len) {
    byte crc = 0;
    for (int i = start; i < start + len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++)
            crc = (crc & 0x80) != 0 ? (byte)((crc << 1) ^ 0x07) : (byte)(crc << 1);
    }
    return crc;
}

5.6 完整帧示例

示例 1:set_param — 设置 gain#1 的 gainDb#0 = -3.5(float32)

55 AA                         帧头
01                            CMD = set_param
00 00                         SEQ = 0
13 00                         LEN = 19 bytes

DATA (19 bytes):
  06                          instanceId len = 6
  67 61 69 6E 23 31           "gain#1"
  08                          paramId len = 8
  67 61 69 6E 44 62 23 30     "gainDb#0"
  01                          VALUE_TYPE = float32
  00 00 60 C0                 -3.5 (IEEE 754 LE)

XX                            CRC8

示例 2:set_param — 设置 delay#1 的 delaySamples#1 = 48(uint32)

55 AA                         帧头
01                            CMD = set_param
01 00                         SEQ = 1
12 00                         LEN = 18 bytes

DATA (18 bytes):
  07                          instanceId len = 7
  64 65 6C 61 79 23 31        "delay#1"
  10                          paramId len = 16
  64 65 6C 61 79 53 61 6D
  70 6C 65 73 23 31           "delaySamples#1"
  04                          VALUE_TYPE = uint32
  30 00 00 00                 48 (LE)

XX                            CRC8
55 AA                         帧头
02                            CMD = set_link
02 00                         SEQ = 2
16 00                         LEN = 22 bytes

DATA (22 bytes):
  02 00                       模块数量 = 2

  条目 0 (gain#1):
    01 00 01 10               moduleTypeId = 0x10010001 (channel_gain_v1, LE)
    06                        instanceId len = 6
    67 61 69 6E 23 31         "gain#1"

  条目 1 (delay#1):
    01 00 02 10               moduleTypeId = 0x10020001 (ut_delay_20ch_v1, LE)
    07                        instanceId len = 7
    64 65 6C 61 79 23 31      "delay#1"

XX                            CRC8

6. 参数 ID 命名规范

格式定义

格式 示例 说明
paramId enable 全局参数(无通道后缀)
paramId#channel gainDb#0 逐通道参数(channel 从 0 起)
paramId#band bandFreq#2 逐频段参数
paramId#band#channel bandFreq#2#0 逐频段逐通道

WebSocket 消息中的参数 ID

前端发送 set_param 时,paramId 字段不含通道后缀,通道通过独立的 channel 字段传递:

{ "type": "set_param", "instanceId": "gain#1", "paramId": "gainDb", "value": -3.0, "channel": 0 }

后端合并为存储 key:"gain#1.gainDb#0"

DSP 二进制帧中的参数 ID

DSP 帧中的 paramId 字段已包含通道后缀,直接传递完整字符串:

"gainDb#0"          → gain#1 模块通道 0 的增益
"delaySamples#3"    → delay#1 模块通道 3 的延迟
"enable"            → 模块使能(无通道后缀)

instanceId 格式规范

instanceId = "${moduleTypeShortName}#${index}"

规则:
  1. moduleTypeShortName 为 moduleType 去掉 "_v${N}" 后缀后的名称
     channel_gain_v1  → channel_gain
     ut_delay_20ch_v1 → ut_delay_20ch
  2. index 从 1 开始,同类型第 N 个实例的 index 为 N
  3. 总长度不超过 31 字节(ASCII)
  4. 子图展平后采用点分路径,如 "group#1.eq#1"(总长度不超过 15 字节,
     受 set_link v3 模块表 instanceId char[16] 限制)

示例:
  channel_gain#1        第 1 个 channel_gain_v1 实例
  ut_delay_20ch#1       第 1 个 ut_delay_20ch_v1 实例
  channel_gain#2        第 2 个 channel_gain_v1 实例
  group#1.eq#1          子图 group#1 内的 eq#1(展平后)

Gain 模块参数 ID 列表

paramId 类型 单位 通道相关 说明
enable bool — 否 模块使能
smoothTime float ms 否 增益平滑时间
gainDb#${ch} float dB 是 通道 ch 增益(-96 ~ +24 dB)
mute#${ch} bool — 是 通道 ch 静音
phase#${ch} bool — 是 通道 ch 相位翻转

Delay 模块参数 ID 列表

paramId 类型 单位 通道相关 说明
enable bool — 否 模块使能
delaySamples#${ch} uint32 samples 是 通道 ch 延迟(0 ~ 960 samples)

7. 链路配置 JSON 格式(v2.2)

v2.2 在 v2.1 基础上,将单链路平坦模块列表升级为多链路 chains 映射,支持子图嵌套(SubGraph)与多端口连线(multi-port edge)。旧版 v2.0/v2.1 的单一 input/output 连线假设已被 ChainEdge.fromPort / toPort 取代,可明确指向具名端口 ID。

注意:ChainEdge.fromPort / toPort 替代旧版单一 input/output 假设,明确指向具名端口 ID。这使得 Mixer 等多输入模块的不同输入端口(如 input_0、input_1)可以在同一连线表中被唯一区分。

LinkConfig 字段说明

字段 类型 必填 说明
version string 是 格式版本,当前为 "2.2"
rootChainId string 是 根链路 id,对应 chains 中的顶层入口
global object 是 全局音频参数:channels、sampleRate、frameSize
chains map 是 所有链路定义,key 为链路 id

ChainDefinition 字段说明

字段 类型 必填 说明
id string 是 链路唯一标识符
name string 否 可读名称
nodes ChainNode[] 是 本链路内所有节点
edges ChainEdge[] 是 本链路内所有连线
externalPorts PortDescriptor[] 子图必填 子图对外暴露的端口,与父图中 SubGraphNode 的 ports 对应

PortDescriptor 字段说明

字段 类型 说明
id string 端口 ID,节点内唯一
direction string "input" 或 "output"
channels int 通道数;-1 表示继承
sampleRate int 采样率(Hz);-1 表示继承
label string UI 显示标签
required bool 是否为必连端口
{
  "type": "write_link",
  "payload": {
    "version": "2.2",
    "rootChainId": "root",
    "global": { "channels": 20, "sampleRate": 48000, "frameSize": 240 },
    "chains": {
      "root": {
        "id": "root",
        "name": "Main Chain",
        "nodes": [
          {
            "instanceId": "gain#1",
            "moduleType": "channel_gain_v1",
            "order": 0,
            "enabled": true,
            "position": {"x": 100, "y": 200},
            "ports": [
              {"id":"input","direction":"input","channels":-1,"sampleRate":-1,"label":"Input","required":true},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Output","required":true}
            ]
          },
          {
            "instanceId": "mixer#1",
            "moduleType": "audio_mixer_v1",
            "order": 1,
            "enabled": true,
            "position": {"x": 400, "y": 200},
            "ports": [
              {"id":"input_0","direction":"input","channels":-1,"sampleRate":-1,"label":"Ch A","required":true},
              {"id":"input_1","direction":"input","channels":-1,"sampleRate":-1,"label":"Ch B","required":false},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Mix","required":true}
            ]
          },
          {
            "instanceId": "group#1",
            "moduleType": "subgraph",
            "subGraphId": "eq_stage",
            "position": {"x": 700, "y": 200},
            "ports": [
              {"id":"input","direction":"input","channels":2,"sampleRate":48000,"label":"In","required":true},
              {"id":"output","direction":"output","channels":2,"sampleRate":48000,"label":"Out","required":true}
            ]
          }
        ],
        "edges": [
          {"id":"e1","fromModule":"gain#1","fromPort":"output","toModule":"mixer#1","toPort":"input_0","channels":2,"sampleRate":48000},
          {"id":"e2","fromModule":"mixer#1","fromPort":"output","toModule":"group#1","toPort":"input","channels":2,"sampleRate":48000}
        ]
      },
      "eq_stage": {
        "id": "eq_stage",
        "name": "EQ Stage",
        "externalPorts": [
          {"id":"input","direction":"input","channels":2,"sampleRate":48000,"label":"In","required":true},
          {"id":"output","direction":"output","channels":2,"sampleRate":48000,"label":"Out","required":true}
        ],
        "nodes": [
          {
            "instanceId": "eq#1",
            "moduleType": "common_eq_v1",
            "order": 0,
            "enabled": true,
            "position": {"x": 200, "y": 150},
            "ports": [
              {"id":"input","direction":"input","channels":-1,"sampleRate":-1,"label":"In","required":true},
              {"id":"output","direction":"output","channels":-1,"sampleRate":-1,"label":"Out","required":true}
            ]
          }
        ],
        "edges": []
      }
    }
  }
}

8. 消息流程图

8.1 正常参数设置流程

Frontend                 Backend                  DSP Hardware
   │                        │                         │
   │── set_param ──────────►│                         │
   │                        ├── Binary Frame (0x01) ──►│
   │◄─ set_param_ack ───────┤                         │
   │                        │                         │
   │              [其他客户端]                          │
   │                        ├── param_update ─────────►│ (广播)

8.2 链路下发流程

Frontend                 Backend                  DSP Hardware
   │                        │                         │
   │── write_link ─────────►│                         │
   │                        ├─ 持久化 JSON 到文件      │
   │                        ├─ ChainFlattener 展平子图  │
   │                        ├─ Binary Frame (0x02) ──►│
   │◄─ write_link_ack ──────┤                         │
   │                        │            [DSP 重建链路] │
   │── set_param (初始参数)──►│                         │
   │◄─ set_param_ack ───────┤                         │

8.3 客户端重连恢复流程

Frontend                 Backend
   │                        │
   │── WebSocket Connect ──►│
   │◄─ dsp_status ──────────┤  (后端主动推送当前 DSP 状态)
   │                        │
   │── read_link ───────────►│
   │◄─ read_link_ack ───────┤  (返回链路 JSON)
   │                        │
   │── get_all_params ──────►│  (针对每个模块实例)
   │◄─ get_all_params_ack ──┤

8.4 DSP 连接流程

Frontend                 Backend                  DSP Hardware
   │                        │                         │
   │── connect_dsp ─────────►│                         │
   │                        │── TCP/Serial Connect ───►│
   │◄─ connect_dsp_ack ─────┤                         │
   │◄─ dsp_status ──────────┤  (广播给所有客户端)      │

8.5 工作模式切换流程

Frontend                 Backend               其他客户端
   │                        │                      │
   │── set_mode ────────────►│                      │
   │                        ├─ 更新内部模式状态      │
   │◄─ mode_changed ────────┤                      │
   │                        ├── mode_changed ──────►│ (广播)

8.6 PC 仿真启动流程

Frontend                 Backend              DynamicChain.dll + NAudio
   │                        │                            │
   │── sim_start ───────────►│                            │
   │                        ├── 加载当前链路 JSON          │
   │                        ├── Init DLL + open audio ──►│
   │                        │                            │ (音频处理开始)
   │◄─ sim_status ──────────┤  (running=true, 广播)      │
   │                        │                            │
   │── sim_stop ────────────►│                            │
   │                        ├── Stop audio + unload ────►│
   │◄─ sim_status ──────────┤  (running=false, 广播)     │

8.7 调试订阅流程

Frontend                 Backend (DebugService)
   │                        │
   │── debug_subscribe ─────►│  (intervalMs=500)
   │                        ├─ 启动定时器
   │◄─ debug_metrics ───────┤  (每 500ms 推送一次)
   │◄─ debug_metrics ───────┤
   │◄─ debug_metrics ───────┤
   │         ...             │
   │── debug_unsubscribe ───►│
   │                        ├─ 停止定时器

8.8 WAV 抓取流程

Frontend                 Backend              DynamicChain.dll
   │                        │                       │
   │── wav_capture_start ───►│                       │
   │                        ├── Hook module output ─►│
   │◄─ wav_capture_ack ─────┤  (captureId="a3f2b1c0")│
   │                        │     [抓取 durationMs]   │
   │                        │◄─ 音频数据回调 ─────────┤
   │                        ├─ 写 WAV 文件            │
   │◄─ wav_capture_done ────┤  (fileSizeBytes=2097152)│
   │                        │                       │
   │── GET /api/debug/wav/a3f2b1c0 ──────────────────►│ (REST 下载)

8.9 子图导航流程(纯前端操作,不涉及 WebSocket)

子图导航流程(纯前端操作,不涉及 WebSocket):

用户双击 SubGraphNode      →  useChainStore.navigateInto(subGraphId)
                           →  currentChainId 更新为 subGraphId
                           →  ChainCanvas 重绘子图内容
                           →  BreadcrumbNav 显示导航路径

用户点击面包屑返回         →  useChainStore.navigateBack()
                           →  currentChainId 恢复为父图 id
                           →  ChainCanvas 重绘父图

用户点击保存/下发链路      →  前端发送 write_link(含完整 LinkConfig v2.2,含子图定义)
                           →  后端 ChainFlattener 展平子图
                           →  后端生成 set_link v3 二进制帧下发 DSP

子图导航是纯前端的视图切换行为,不产生任何 WebSocket 消息。前端通过 useChainStore 中的 currentChainId 状态决定 ChainCanvas 当前渲染哪个 ChainDefinition。只有在用户主动保存或下发链路时,才触发一次 write_link 消息,其 payload 包含完整的 LinkConfig v2.2(含所有子图定义)。


9. 消息类型汇总表

前端 → 后端

type 分组 说明
ping 基础通信 心跳探测
set_param 参数操作 设置单个参数
set_params 参数操作 兼容别名(与 set_param 等价)
get_param 参数操作 查询单个参数
get_all_params 参数操作 查询实例全量参数
write_link 链路管理 保存并下发链路配置;payload: LinkConfig v2.2(含子图定义)
read_link 链路管理 加载链路配置
connect_dsp DSP 连接 连接 DSP 硬件(TCP 或串口)
disconnect_dsp DSP 连接 断开 DSP 连接
get_status DSP 连接 查询后端及 DSP 状态
get_dsp_chain DSP 连接 从 DSP 读取链路结构(仅 test_verify)
set_mode 工作模式 切换工作模式
sim_start PC 仿真 启动 PC 端算法仿真
sim_stop PC 仿真 停止 PC 端算法仿真
list_audio_devices PC 仿真 枚举系统音频设备
debug_subscribe 调试 订阅调试指标周期推送
debug_unsubscribe 调试 取消订阅调试指标
wav_capture_start 调试 启动模块节点 WAV 抓取

多端口连线与子图对协议层透明:前端无需新增消息类型,所有结构变化均封装在 write_link 的 LinkConfig v2.2 payload 内。

后端 → 前端

type 分组 推送方式 说明
pong 基础响应 单播(请求方) 心跳响应
error 基础响应 单播(请求方) 错误响应
set_param_ack 参数响应 单播(请求方) 参数设置确认
get_param_ack 参数响应 单播(请求方) 参数查询响应
get_all_params_ack 参数响应 单播(请求方) 全量参数响应
param_update 参数响应 广播(其他客户端) 多端参数同步广播
write_link_ack 链路响应 单播(请求方) 链路保存确认
read_link_ack 链路响应 单播(请求方) 链路加载响应
get_dsp_chain_ack 链路响应 单播(请求方) DSP 链路读取响应
connect_dsp_ack DSP 状态 单播(请求方) DSP 连接结果
disconnect_dsp_ack DSP 状态 单播(请求方) DSP 断开确认
dsp_status DSP 状态 广播(所有客户端) DSP 状态推送
mode_changed 模式 广播(所有客户端) 模式变更广播
sim_status PC 仿真 广播(所有客户端) 仿真状态推送
audio_devices_ack PC 仿真 单播(请求方) 音频设备列表
debug_metrics 调试推送 单播(订阅方) 调试指标周期推送
log_stream 调试推送 广播(所有客户端) 实时日志流推送
wav_capture_ack 调试推送 单播(请求方) WAV 抓取启动确认
wav_capture_done 调试推送 单播(请求方) WAV 抓取完成通知

新增消息类型(v4.0 补充)

前端 → 后端:Preset 管理

// 保存当前模块参数为一套 preset
{ "type": "save_preset", "instanceId": "gain#1", "presetId": "bass_boost", "name": "Bass Boost" }
// 响应: preset_save_ack

// 加载 preset 参数到 UI
{ "type": "load_preset", "instanceId": "gain#1", "presetId": "bass_boost" }
// 响应: preset_data

// 列出某模块的所有 preset
{ "type": "list_presets", "instanceId": "gain#1" }
// 响应: preset_list

// 删除 preset
{ "type": "delete_preset", "instanceId": "gain#1", "presetId": "bass_boost" }
// 响应: preset_delete_ack

前端 → 后端:Profile 管理

// 保存 ambiance profile
{
  "type": "save_profile",
  "profileId": "hifi_mode", "name": "HIFI",
  "modulePresets": { "gain#1": "flat", "eq#1": "hifi_eq", "delay#1": "default" }
}
// 响应: profile_save_ack

{ "type": "list_profiles" }            // 响应: profile_list
{ "type": "delete_profile", "profileId": "hifi_mode" }  // 响应: profile_delete_ack

// 切换音效模式(向 DSP 发 CMD=0x05)
{ "type": "set_ambiance", "ambianceIndex": 2 }
// 响应: set_ambiance_ack

前端 → 后端:ParamBin 生成

// 将指定 profiles 编译为 ParamBin 并下发 DSP(CMD=0x04)
{
  "type": "generate_param_bin",
  "targetProfiles": ["hifi_mode", "dynamic_mode", "rock_mode"],
  "modulePresetLimits": { "gain#1": 3, "eq#1": 3, "delay#1": 2 }
}
// 响应: param_bin_ack

前端 → 后端:多维参数 set_param(新增 dimIndices)

{
  "type": "set_param",
  "instanceId": "eq#1",
  "paramId": "bandFreq",
  "dimIndices": [2, 5],    // 新增可选字段:dim0=channel=2, dim1=band=5
  "value": "120.0"
}
// 如果 dimIndices 不存在则向后兼容(使用旧 channel 字段)
// 后端编码:channelIdx = (2<<8)|5 = 0x0205

前端 → 后端:子图同步校验

// 触发后端向 DSP 查询平坦模块列表(仅校验,不重建层次结构)
{ "type": "read_dsp_chain" }
// 响应: dsp_chain_echo + 可能跟随 chain_sync_warning

注:get_dsp_chain 行为变更 —— 返回 Backend 存储的 LinkConfig(含子图),不询问 DSP:

{ "type": "get_dsp_chain" }
// 响应: dsp_chain(LinkConfig v2.2,含子图层次结构,来自 current_link.json)


后端 → 前端:新增响应消息

// Preset 响应
{ "type": "preset_save_ack", "instanceId": "gain#1", "presetId": "bass_boost" }
{ "type": "preset_data", "instanceId": "gain#1", "presetId": "bass_boost",
  "params": { "gainDb[0]": "3.0", "gainDb[1]": "-1.5", "bandFreq[2][5]": "120.0" } }
{ "type": "preset_list", "instanceId": "gain#1",
  "presets": [{"presetId":"flat","name":"Flat"},{"presetId":"bass_boost","name":"Bass Boost"}] }
{ "type": "preset_delete_ack", "instanceId": "gain#1", "presetId": "bass_boost" }

// Profile 响应
{ "type": "profile_save_ack", "profileId": "hifi_mode" }
{ "type": "profile_list",
  "profiles": [{"profileId":"hifi_mode","name":"HIFI"},{"profileId":"dynamic","name":"动感"}] }
{ "type": "profile_delete_ack", "profileId": "hifi_mode" }

// Ambiance 响应
{ "type": "set_ambiance_ack", "ambianceIndex": 2 }
{ "type": "param_bin_ack", "byteSize": 4096, "numAmbiances": 3, "numModules": 8 }

// 同步校验响应
{ "type": "dsp_chain_echo",
  "modules": [{"instanceId":"gain#1","typeId":"channel_gain_v1"},
               {"instanceId":"eq#1","typeId":"common_eq_v1"}] }
{ "type": "chain_sync_warning",
  "missing": ["group#1.eq#1"], "extra": [],
  "message": "DSP 模块列表与存储配置不匹配,建议重新下发链路" }

更新的 DSP Binary 帧格式

set_param CMD=0x01(v3,数字 ID)

旧版(字符串):[CMD=0x01][LEN]["gain#1.gainDb#2\0"][f32] ≈ 30 字节
新版(数字):  [CMD=0x01][LEN=9][moduleIdx:u8][paramTypeId:u16LE][channelIdx:u16LE][type:u8=0x01][value:f32LE]
              = 14 字节(节省 53%)

channelIdx 编码:
  标量/全局:0x0000
  1D(通道 2):0x0002
  2D(通道2 band5):0x0205  ← (2<<8)|5

load_param_bin CMD=0x04

[CMD=0x04][DATA_LEN:u32LE][ ParamBin 数据 ]

ParamBin 内部格式:
  Header (8B):       "PCFG" + version:u8 + numAmbiances:u8 + numModules:u8 + reserved:u8
  Module Table:      numModules × 24B = instanceId:char[16] + typeNumId:u32 + numPresets:u8 + pad:u8[3]
  Ambiance Table:    numAmbiances × (16+numModules)B = name:char[16] + presetIndices:u8[numModules]
  Param Data:        per module per preset:
                       paramCount:u16 + (paramTypeId:u16 + channelIdx:u16 + value:f32)[paramCount]

set_ambiance CMD=0x05

[CMD=0x05][DATA_LEN=1:u32LE][ambianceIdx:u8] = 6 字节
DSP 响应:[CMD=0x85][DATA_LEN=1][ambianceIdx:u8]

get_chain CMD=0x06(用于同步校验)

请求:[CMD=0x06][DATA_LEN=0:u32LE] = 5 字节
响应:[CMD=0x86][DATA_LEN:u32LE][numModules:u8][pad:u8[3]]
      per module (32B): instanceId:char[24] + typeNumId:u32 + pad:u8[4]

paramTypeId 编号规范

格式:u16(高字节=模块类别,低字节=参数序号)

模块类别:
  0x01 = gain    0x02 = filter/EQ    0x03 = delay
  0x04 = dynamics  0x05 = mixer     0x06 = routing

已定义示例:
  0x0101 Gain.gainDb      0x0102 Gain.mute      0x0103 Gain.enable
  0x0201 EQ.bandFreq      0x0202 EQ.bandGain    0x0203 EQ.bandQ
  0x0204 EQ.bandType      0x0205 EQ.bandEnable  0x0206 EQ.moduleEnable
  0x0301 Delay.delaySamples  0x0302 Delay.enable


新增消息类型(v5.1 补充)

2.2 参数操作(续)

apply_params — 前端主导批量参数推送

前端将 preset 参数应用到自身状态后,通过此消息批量推送给后端。后端写入 paramStore 并向 DSP 下发每个参数。

{
  "type": "apply_params",
  "instanceId": "gain#1",
  "params": {
    "gainDb#0": "-3.5",
    "gainDb#1": "-3.5",
    "mute#0":   "false",
    "mute#1":   "false",
    "enable":   "true"
  }
}
字段 类型 必填 说明
instanceId string 是 模块实例 ID
params object 是 参数键值对;key 为含通道后缀的 paramId(如 "gainDb#0"),value 为字符串

后端行为: 1. 遍历 params,对每条记录:写入 paramStore["{instanceId}.{paramId}"],并异步调用 TryFlushToDspAsync 2. 所有布尔值经 ToStoreString() 确保以小写 "true" / "false" 存储 3. 不广播 param_update(批量覆盖场景,广播由前端自行决定是否补发)

响应:apply_params_ack

{
  "type": "apply_params_ack",
  "instanceId": "gain#1",
  "success": true
}

与 set_param 对比:

特性 set_param apply_params
参数数量 单个 批量
广播 param_update 是 否
主要用途 UI 实时调参 Preset 加载后批量推送
响应类型 set_param_ack apply_params_ack

get_module_params — 从后端读回参数(回读校验)

前端"Read from backend"操作,用于校验后端 paramStore 中的当前值是否与前端期望一致。

{
  "type": "get_module_params",
  "instanceId": "gain#1"
}
字段 类型 必填 说明
instanceId string 是 模块实例 ID

后端行为:直接从 paramStore 中过滤该实例的所有参数键,去掉 "{instanceId}." 前缀后返回。不与 DSP 通信。

响应:module_params

{
  "type": "module_params",
  "instanceId": "gain#1",
  "params": {
    "gainDb#0": "-3.5",
    "gainDb#1": "-3.5",
    "mute#0":   "false",
    "mute#1":   "false",
    "enable":   "true"
  }
}

所有值均为字符串,布尔参数保证为小写 "true" / "false"。


2.7 Preset 管理(更新)

save_preset — 保存模块参数快照(已更新)

{
  "type": "save_preset",
  "instanceId": "gain#1",
  "presetId": "bass_boost",
  "name": "Bass Boost",
  "params": {
    "gainDb#0": "3.0",
    "gainDb#1": "3.0",
    "mute#0":   "false",
    "enable":   "true"
  }
}
字段 类型 必填 说明
instanceId string 是 模块实例 ID
presetId string 是 Preset 标识符(文件名)
name string 是 可读名称(UI 显示)
params object 否 前端提供的参数快照(前端权威模式);若省略则后端从 paramStore 快照(向后兼容)

后端行为(当 params 字段存在时): 1. 将 params 中每条记录写入 paramStore(经 ToStoreString() 保证布尔小写) 2. 为每条记录调用 TryFlushToDspAsync 异步下发 DSP 3. 将完整 preset(含 params 字段)序列化保存到 data/presets/{instanceId}/{presetId}.json

后端行为(params 字段缺失时,向后兼容): - 从 paramStore 中快照该实例的当前参数并保存文件

响应:preset_save_ack


list_presets — 列出模块的所有 preset(已更新)

{ "type": "list_presets", "instanceId": "gain#1" }

响应:preset_list(现在每个条目包含 params 字段)

{
  "type": "preset_list",
  "instanceId": "gain#1",
  "presets": [
    {
      "presetId": "flat",
      "name": "Flat",
      "params": {
        "gainDb#0": "0.0",
        "gainDb#1": "0.0",
        "mute#0":   "false",
        "enable":   "true"
      }
    },
    {
      "presetId": "bass_boost",
      "name": "Bass Boost",
      "params": {
        "gainDb#0": "3.0",
        "gainDb#1": "3.0",
        "mute#0":   "false",
        "enable":   "true"
      }
    }
  ]
}

更新说明:原版 preset_list 每个条目只含 presetId 和 name(元数据)。新版从文件中读取完整 preset 并在列表条目中包含 params 字段。前端在启动时收到 preset_list 后,将 params 存入本地 presetDataStore[instanceId][presetId],后续 preset 加载可直接通过 apply_params 推送,无需再发 load_preset 往返请求。


3. 后端 → 前端 消息(v5.1 新增)

apply_params_ack — 批量参数应用确认

{
  "type": "apply_params_ack",
  "instanceId": "gain#1",
  "success": true
}

module_params — 后端参数回读响应

{
  "type": "module_params",
  "instanceId": "gain#1",
  "params": {
    "gainDb#0": "-3.5",
    "gainDb#1": "-3.5",
    "mute#0":   "false",
    "enable":   "true"
  }
}

params 对象的 key 为不含 instanceId. 前缀的 paramId,value 均为字符串;布尔值保证小写。


9. 消息类型汇总表(v5.1 增量)

在原有汇总表基础上,新增以下条目:

前端 → 后端

type 分组 说明
apply_params 参数操作 批量写入 paramStore 并下发 DSP;前端主导 preset 加载
get_module_params 参数操作 从 paramStore 读回指定实例的全部参数,用于前端回读校验

后端 → 前端

type 分组 推送方式 说明
apply_params_ack 参数响应 单播(请求方) 批量参数应用确认
module_params 参数响应 单播(请求方) 后端参数回读响应

已更新消息

type 方向 变更说明
save_preset 前端→后端 新增可选 params 字段;前端可直接传参数快照(前端权威模式),省略时后端从 paramStore 快照(向后兼容)
preset_list 后端→前端 每个 preset 条目新增 params 字段,前端可在启动时预加载所有 preset 数据

参数值规范说明(v5.1)

所有经 WebSocket 传输并存入 paramStore 的布尔型参数值,统一使用小写字符串:

值类型 正确形式 错误形式(旧 C# 默认)
布尔真 "true" "True"
布尔假 "false" "False"

这一规范确保前端 JavaScript 使用 === 'true' 进行布尔比较时能够正确匹配,无需额外做大小写转换。后端通过 ToStoreString() 辅助函数在所有写入路径(HandleSetParam、HandleApplyParams、HandleSavePreset、HandleLoadPreset、HandleSetAmbiance)统一保证此规范。