跳转至

模块定制系统设计文档 v1.0

日期:2026-03-28 作者:前端工程设计


1. 背景与目标

问题: 每当出现新算法,前端需要手写代码(moduleLibrary.ts + 专用 Dialog)并重新发包,才能让调音师使用。XML 兼容模式虽然支持从 XML 文件导入控件,但 XML 本身无法灵活修改,且整体界面(卡片网格)与正常创作模式割裂。

目标: 1. 任何新算法的 UI 都可以通过可视化工具定义,无需写代码无需发包 2. 正常 DSP 模块(native)和 XML 兼容模块(legacy)用统一的定义机制 3. 兼容调音模式使用与正常模式相同的 Canvas 画布,保持操作一致性 4. Preset / Profile 在所有模式下统一可用 5. 模块定义存储在后端(支持多设备共享、codegen 输入)


2. 三种工作模式

模式 标识符 功能
全能模式 omnipotent 链路搭建 + 模块创建/编辑 + 实时调参 + 测试验证
调音模式 tuning 只读链路,调整可调参数,自动测试验证
兼容调音模式 compat 只显示 legacy 模块,Canvas 连线(装饰性),XmlTuningDialog 调参

3. 统一模块定义 Schema

所有模块(native + legacy)用同一套 JSON 描述,由后端 API 持久化。

type ModuleDefType = 'native' | 'legacy'

interface ModuleDefinition {
  moduleId: string            // 唯一 ID,如 "gain_v1", "legacy_ChannelGain_20ch"
  moduleType: ModuleDefType
  displayName: string
  category: string            // "gain" | "delay" | "eq" | "dynamics" | "legacy" | ...
  version: string             // "1.0.0"
  description?: string

  // 所有模块都有端口(legacy 端口仅用于画布连线,不发 DSP 拓扑)
  ports: PortDescriptor[]

  // native 模块专用:标准 DSP 参数定义
  nativeConfig?: {
    params: NativeParamDef[]  // 含 type/range/role/display
  }

  // legacy 兼容模块专用:控件布局定义
  legacyConfig?: {
    defaultHardware: string   // "legacy" | "tcp:host:port" | "serial:COMx"
    defaultDataType: XmlDataType  // 'float32' | 'int16' | ...
    defaultConversion: string // "identity" | "dBtoLinear"
    widgets: XmlWidgetDef[]   // 含 tab/mixer/eq/combobox/button
  }
}

关键设计:legacy 模块也带 ports 这样 Canvas 画布可以在兼容调音模式中为 legacy 模块绘制连线(视觉上的信号流),但 linkStore 对 legacy 连线标记 decorativeOnly: true,不纳入 DSP 拓扑下发。


4. 后端 API(WebSocket)

消息(前端→后端) 响应(后端→前端) 说明
list_module_defs module_defs_list { defs: ModuleDefinition[] } 启动时拉取全量
save_module_def { def } module_def_saved { moduleId, success } 新建或覆盖
delete_module_def { moduleId } module_def_deleted { moduleId } 删除
export_module_defs module_defs_export { json } 导出为 JSON 文件

5. Module Designer(全能模式左侧面板 Tab)

左侧面板新增 "模块定义" Tab,在全能模式下可见。

5.1 布局

[左侧面板]
  Tab: 模块库  |  连接  |  档案  |  音频引擎  |  ★模块定义

  [模块定义 Tab 内容]
  ┌─────────────────────────────────────┐
  │  [+ 新建 Native]  [+ 新建 Legacy]   │
  │  搜索: [_______________]            │
  ├─────────────────────────────────────┤
  │  ▸ gain_v1          native  [✏] [×] │
  │  ▸ delay_v1         native  [✏] [×] │
  │  ▸ legacy_Mixer20ch legacy  [✏] [×] │
  │  ...                                │
  └─────────────────────────────────────┘

点击 [✏] 打开 ModuleDefEditor.vue(右侧主区域或浮动面板)。

5.2 ModuleDefEditor.vue(native 模块)

模块信息: ID / 名称 / 分类 / 版本

端口:
  [+] 添加端口
  ─ input  channels:20  sr:48000  required:true  [×]
  ─ output channels:20  sr:48000  required:true  [×]

参数列表:
  [+] 添加参数
  ─ enable  bool   system   [✏详情] [×]
  ─ gainDb  float  tunable  [-96~24 dB, step:0.1, perCh:true] [✏] [×]
  ...

[保存]  [取消]  [预览]

5.3 ModuleDefEditor.vue(legacy 模块)

模块信息: ID / 名称 / 分类 / 版本
通信配置: Hardware / DataType / Conversion

端口(可选,用于画布连线):
  ...

控件列表:
  [+ 推子(Mixer)]  [+ EQ]  [+ 下拉(ComboBox)]  [+ 按钮(Button)]  [+ 选项卡(Tab)]

  ─ [Mixer] Channel Gain  PID:0x302  ch:0~19  mute:✓  phase:✓  range:-96~24dB [✏] [×]
  ─ [Button] Bypass  PID:0x200  ch:0  [✏] [×]
  ─ [EQ] EQ 10band  PID:0x400  bands:10  freq:20~20k  gain:-14~14 [✏] [×]
  ─ [Tab] 均衡设置  tabs:[EQ1, EQ2]  [✏] [×]

[保存]  [取消]  [预览对话框]

6. 兼容调音模式(compat)Canvas

6.1 与正常模式的差异

特性 正常模式 兼容调音模式
模块来源 native + legacy 仅 legacy
链路连线 发送 DSP 拓扑 仅视觉,不发拓扑
双击模块 打开 NativeTuningDialog 打开 XmlTuningDialog
参数同步 set_param WebSocket legacy_set_param WebSocket
Preset/Profile ✅(完全一致)
模块定制 可通过 Module Designer 可通过 Module Designer

6.2 实现方案

linkStore 的 connection 上增加 decorativeOnly?: boolean 字段。 Canvas(LinkEditor)绘制时不区分,但 DSP 拓扑下发时过滤掉 decorativeOnly: true 的连线。

App.vue 中根据模块的 moduleType 决定打开哪种对话框:

if (module.moduleType === 'legacy') {
  openXmlTuningDialog(instanceId)
} else {
  openNativeTuningDialog(instanceId)
}


7. Widget 系统改进

7.1 当前状态(已实现)

控件 组件 状态
推子(Mixer) XmlMixerWidget.vue ✅ 基本可用,含 mute/phase
EQ XmlEQWidget.vue ✅ 表格形式,freq/gain/q/filterType
下拉(ComboBox) XmlComboBoxWidget.vue ✅ 基本可用
按钮(Button) XmlButtonWidget.vue ✅ 基本可用
选项卡(Tab) XmlTabWidget.vue ✅ Tab 导航 + 递归渲染
分发渲染 XmlWidgetRenderer.vue ✅ 按 type 分发

7.2 需要改进

  1. Tab 内容分组:XmlTabWidget 的 tab-content 使用简单 flex,Tab 内的 mixer 无法横向滚动。需提取 XmlWidgetGroupLayout.vue 组件,统一 Dialog Body 和 Tab Content 的分组逻辑。

  2. Mixer 多通道场景:当一个 tab 内有多个相同 PID 的 mixer(多通道),应横向排列并支持横向滚动。

  3. 参数初始化:XmlTuningDialog 的 initLocalParams 目前不递归处理 Tab 内的控件 ——已有 flatWidgets 函数,但需确保 Tab 嵌套时的所有子控件都初始化到 localParams

  4. 验证 Demo:添加 src/components/xml-tuning/WidgetDemo.vue,用内联 mock 数据展示所有控件类型,方便不联后端时验证。

7.3 XmlWidgetGroupLayout.vue(新增)

将 XmlTuningDialog 的分组渲染逻辑提取为独立组件:

输入:widgets: XmlWidgetDef[], paramOverrides, @param-change
输出:已分组的 DOM

分组规则:
- button 类型 → buttons-row(横向换行)
- 相同 subPID 的 mixer → mixer-group(横向滚动,带组标签)
- combobox → combos-section(横向排列)
- eq → eq-section(全宽表格)
- tab → tab-section(全宽)
- 其他 → other-section

XmlTuningDialog 和 XmlTabWidget 都使用此组件。


8. Preset / Profile 在各模式下

功能 全能模式 调音模式 兼容调音模式
模块级 Preset 保存/加载 ✅(含 legacy 模块)
Profile 激活/切换
Profile 编辑(保存映射) ❌ 只读
ParamBin 生成

legacy 模块的 Preset 使用 moduleName 作为 instanceId,与 XmlPresetPanel 已有实现一致。


9. Store 变更

9.1 新增 useModuleDefStore.ts

interface State {
  defs: Map<string, ModuleDefinition>   // moduleId → def
  loading: boolean
}

// Actions
fetchAll(): void                         // list_module_defs → 填充 defs
save(def: ModuleDefinition): Promise<void> // save_module_def
remove(moduleId: string): Promise<void>  // delete_module_def

// Computed
legacyDefs: computed  defs filtered by moduleType === 'legacy'
nativeDefs: computed  defs filtered by moduleType === 'native'

9.2 修改 useXmlTuningStore.ts

移除 loadXmlFile / loadJsonConfig 的直接解析逻辑(改为从 useModuleDefStore 读取 legacy 模块定义),保留 session 管理(硬件、dataType、对话框状态)。 XML 文件导入变为:解析 XML → 生成 ModuleDefinition → 通过 save_module_def 存后端 → 本地 sessions 引用。

9.3 修改 linkStore.ts

LinkConnection 增加 decorativeOnly?: boolean,拓扑下发时过滤此字段为 true 的连线。


10. 实现阶段

Phase 1(当前):控件完善 + Demo 验证

  1. 提取 XmlWidgetGroupLayout.vue,统一 Dialog Body 和 Tab Content 的分组
  2. 修复 XmlTuningDialog initLocalParams 对 Tab 嵌套的递归处理
  3. 添加 WidgetDemo.vue,用内联 mock 数据验证所有控件

Phase 2:Canvas 集成

  1. 兼容调音模式使用 LinkEditor Canvas(仅显示 legacy 模块)
  2. linkStore 支持 decorativeOnly 连线
  3. App.vue 根据 moduleType 路由到对应 Dialog

Phase 3:Module Designer

  1. useModuleDefStore.ts + WebSocket API
  2. 左侧面板 "模块定义" Tab
  3. ModuleDefEditor.vue(native + legacy 两种编辑器)

Phase 4:XML 导入集成

  1. XML 解析 → 生成 ModuleDefinition → 存后端
  2. 废弃直接从 XML 文件导入的流程

11. 文件变更总览

新增

  • src/stores/useModuleDefStore.ts
  • src/components/xml-tuning/XmlWidgetGroupLayout.vue
  • src/components/xml-tuning/WidgetDemo.vue
  • src/components/module-designer/ModuleDefEditor.vue
  • src/components/module-designer/NativeParamEditor.vue
  • src/components/module-designer/LegacyWidgetEditor.vue

修改

  • src/types/xmlTuning.ts — 无变更(兼容)
  • src/components/xml-tuning/XmlTuningDialog.vue — 使用 XmlWidgetGroupLayout
  • src/components/xml-tuning/XmlTabWidget.vue — tab-content 使用 XmlWidgetGroupLayout
  • src/stores/linkStore.ts — LinkConnection 增加 decorativeOnly 字段
  • src/components/LeftPanel.vue — 新增"模块定义" Tab(Phase 3)
  • src/App.vue — moduleType 路由对话框(Phase 2)