模块定制系统设计文档 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 需要改进
-
Tab 内容分组:XmlTabWidget 的 tab-content 使用简单 flex,Tab 内的 mixer 无法横向滚动。需提取
XmlWidgetGroupLayout.vue组件,统一 Dialog Body 和 Tab Content 的分组逻辑。 -
Mixer 多通道场景:当一个 tab 内有多个相同 PID 的 mixer(多通道),应横向排列并支持横向滚动。
-
参数初始化:XmlTuningDialog 的
initLocalParams目前不递归处理 Tab 内的控件 ——已有flatWidgets函数,但需确保 Tab 嵌套时的所有子控件都初始化到localParams。 -
验证 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 验证
- 提取
XmlWidgetGroupLayout.vue,统一 Dialog Body 和 Tab Content 的分组 - 修复 XmlTuningDialog
initLocalParams对 Tab 嵌套的递归处理 - 添加
WidgetDemo.vue,用内联 mock 数据验证所有控件
Phase 2:Canvas 集成
- 兼容调音模式使用 LinkEditor Canvas(仅显示 legacy 模块)
linkStore支持decorativeOnly连线- App.vue 根据 moduleType 路由到对应 Dialog
Phase 3:Module Designer
useModuleDefStore.ts+ WebSocket API- 左侧面板 "模块定义" Tab
ModuleDefEditor.vue(native + legacy 两种编辑器)
Phase 4:XML 导入集成
- XML 解析 → 生成
ModuleDefinition→ 存后端 - 废弃直接从 XML 文件导入的流程
11. 文件变更总览
新增
src/stores/useModuleDefStore.tssrc/components/xml-tuning/XmlWidgetGroupLayout.vuesrc/components/xml-tuning/WidgetDemo.vuesrc/components/module-designer/ModuleDefEditor.vuesrc/components/module-designer/NativeParamEditor.vuesrc/components/module-designer/LegacyWidgetEditor.vue
修改
src/types/xmlTuning.ts— 无变更(兼容)src/components/xml-tuning/XmlTuningDialog.vue— 使用 XmlWidgetGroupLayoutsrc/components/xml-tuning/XmlTabWidget.vue— tab-content 使用 XmlWidgetGroupLayoutsrc/stores/linkStore.ts— LinkConnection 增加 decorativeOnly 字段src/components/LeftPanel.vue— 新增"模块定义" Tab(Phase 3)src/App.vue— moduleType 路由对话框(Phase 2)