跳转至
Phase 8 Implementation Plan · v1.0

Phase 8 实施计划 v1.0

Core Loop · 8 Demo 验收 · 16 天预算 · DSP/后端/前端三端改动清单 · ThreadChange-ready
作者:Cline · 日期:2026-05-08 · 版本:v1.0 · 基于 review v7.0
16
人天预算(6 阶段)
8
Demo 验收场景
3
端到端改动(DSP / C# / Vue)

Phase 8 实施计划 v1.0

文档目的

基于 Source/Sink 架构 Review v7.0 的 DQ1-DQ25 全员人类确认版,给出 Phase 8 可执行的实施计划:Core Loop + 8 Demo 验收 + 16 天预算分解 + DSP/后端/前端三端具体改动清单 + ThreadChange-ready 框架兼容性 + PortInfo pass-through propagate + Module Manifest + 监控指标全量。

Phase 8 唯一交付目标

前端 → 后端 → DSP 的音频流能打通(包括 source 产生信号、sink 输出、link 下发、参数实时改、监控可见)。其他都是锦上添花,超出就删。

本文档与兄弟文档的关系

  • 架构决策与 DQ 清单:Source/Sink 架构 Review v7.0
  • Phase 9+ 十议题、ThreadChange 详细设计、格式转换模块规范:系统能力路线图 v1.0
  • 本文档:Phase 8 落地计划(做什么 / 怎么做 / 多少天)

1. Core Loop · 音频流打通的最小闭环

1.1 端到端数据流

flowchart LR
    FE["前端<br/>拖拽 10 源之一 + gain + sink<br/>发 apply_link"]
    BE["后端 LinkFrameBuilder<br/>→ LinkPropagator(pass-through)<br/>→ DSPAlgo_SetLink"]
    DSP["DSP<br/>Init 新模块实例<br/>Process 线程开跑(单核)"]
    DEV["声卡<br/>播放 sine / wav / device passthrough"]
    METRICS["监控<br/>CPU 负载表 + underrun 计数"]
    PARAM["前端调 frequencyHz<br/>→ set_param<br/>→ lock-free 队列<br/>→ DSP 下 block 生效"]

    FE --> BE --> DSP --> DEV
    DSP -.-> METRICS -.-> FE
    PARAM --> BE
    BE -.-> DSP

1.2 Demo 1-8 验收清单(Phase 8 出厂标准)

# Demo 场景 验收标准
Demo 1 source_sine_v1 → gain → sink_device_v1 能听到 1kHz 纯音;UI 拖 gainDb 滑块时音量实时变化(30ms ramp 无 pop)
Demo 2 source_noise_pink_v1 → mixer_v2 → gain → sink_device_v1 混音路径可用;粉噪频谱符合 -3dB/oct(FFT 验证)
Demo 3 source_wav_v1 → gain → sink_wav_v1 文件播放 + 落盘;UI 显示进度条 readPosSec;拖动进度条 seek 生效
Demo 4 source_device_v1 (mic capture) → gain → sink_device_v1 (speaker) 麦克风到扬声器回路;Capture 独立线程工作;端到端延时 < 20ms
Demo 5 10 source 模块各自独立生成 + 参数实时调 10 模块 × 3 关键参数 = 30 个调音组合测试通过(见 §6 测试矩阵)
Demo 6 UI 显示 CPU 负载 + per-module 贡献 + 黄/红告警 整体 CPU / per-instance processTimeUs / 内存 breakdown / underrun/overrun 全量可见;70%→黄,90%→红
Demo 7 LoadPreset / SetAmbiance 切换能听到参数变化(无 pop) SetAmbiance 批量切换 10+ 参数时 100ms ramp 生效;听感无 click/pop
Demo 8 Save/Load Project 还原现场 保存工程 → 关闭 app → 重新打开 → Load Project → DSP 跑出信号与保存前一致;profileId / moduleActivePreset 高亮正确

1.3 Phase 8 scope 边界(严格执行)

包含

  • ✅ 10 source 模块全实现 + gain + mixer + sink 基础
  • ✅ DSP 单 Process 线程 + SetParam 队列 + Device capture 独立线程 + Wav 预读
  • ✅ ThreadChange-ready 框架(接口预留,不实现 thread_change_v1)
  • ✅ PortInfo pass-through propagate(单一格式链路)
  • ✅ Module Manifest 基础版(static metadata + runtime metrics)
  • ✅ 监控指标全量(DQ8 approved:CPU + 内存 + underrun/overrun + per-module)
  • ✅ Preset / Profile / SaveProject 基础骨架(DQ5 approved)
  • ✅ 参数平滑(gain/freq 30ms ramp;SetAmbiance 批量 100ms)
  • ✅ 结构性 vs 运行性参数分派

不包含(推到 Phase 9+,详见 roadmap):

  • thread_change_v1 实际实现
  • resampler_v1 / channel_remap_v1 / format_convert_v1 实际实现(只预留 TypeNumId + stub)
  • ❌ 多链路 / multi-session
  • ❌ DSP crash 自动恢复
  • ❌ 实时 WS 日志回流(Phase 8 只做文件 log)
  • ❌ i18n / 离线模式 / 插件扩展
  • ❌ SaveProject UI 完善(只做后端骨架 + 基础按钮)

2. 6 阶段 16 天预算分解

2.1 总览

阶段 主题 人天 主要产出
1 准备 1 分支 + baseline + TODO 更新
2 DSP 拆 10 模块 + source_common 3 10 个 source .c/.h + 注册 + 单测
3 后端简化 + LinkPropagator + Manifest C API 3.5 LinkFrameBuilder 去改写 + LinkPropagator + MetricsAggregator + ProjectService
4 前端拆 10 ModuleDef + PortInfo 3 moduleLibrary 拆分 + metricsStore + PortInfo 类型
5 监控实现(§4 全量) 2 DSP 时间戳采集 + metrics_update WS + 前端负载表
6 回归 + 新测试 2.5 UnitTest_Mixer 重写 + 3 × 10 = 30 个模块单测 + 8 Demo e2e
7 文档 + 合并 1 5khz incident post-mortem + phase8-handoff + master 合并
合计 16

ThreadChange 直接上多核的 fallback

若 Phase 8 实施中发现单核抽象无法兼容 ThreadChange(§5.5 § fallback),人天从 16 扩到 20-22。需人类确认后才启动 fallback。

2.2 阶段 1 · 准备(1 天)

  • 建分支 phase8-source-sink-split
  • UnitTest_Mixer 跑 baseline,保存 WAV + 报告作为回归基准
  • AlgoDepartment/TODOLIST.md §4 登记 Phase 8
  • 确认 04_development worktree 的 CLAUDE.md 已更新 source/sink 新语义
  • Q5 不兼容策略:删除 link.json 迁移脚本计划(老 link.json 直接报错)

2.3 阶段 2 · DSP 拆 10 模块 + source_common(3 天)

每模块约 0.3 人天(30 min/模块设计 + 1.5h 实现 + 30min 单测)

  • dsp_algo/modules/source/source_common.h / source_common.c
  • SourceCommon_PhaseAccumulator_Tick()
  • SourceCommon_DbToLinear()
  • SourceCommon_FillChannels()
  • SourceCommon_RandState 结构 + SourceCommon_GaussianFromUniform()
  • SourceCommon_ParamRamp_Linear() / SourceCommon_ParamRamp_IIR()(DQ9 approved)
  • 新建 10 个独立模块(参见 review §4.6 参数 schema):
  • source_sine.c / source_sweep.c / source_triangle.c / source_square.c / source_saw.c
  • source_noise_white.c / source_noise_pink.c / source_noise_brown.c
  • source_wav.c(内嵌 WAV 解码 + 原子 samples swap;用 dr_wav 或自写最小解码)
  • source_device.c(passthrough,由 ppHwIn 注入)
  • module_type_id.h 分配 TypeNumId:
  • 0x10080010-0x10080019 连续 10 位(10 个新 source)
  • 预留 0x100C0001 thread_change_v1(Phase 9+)
  • 预留 0x100D0001 resampler_v1 / 0x100D0002 channel_remap_v1 / 0x100D0003 format_convert_v1(Phase 9+)
  • module_registry_all.c 注册 10 个新模块 + 填 manifest 字段(见 §5)
  • dynchain_core.h 扩展 ModuleInstancecoreId(默认 0)+ processOrder(按拓扑序)字段(ThreadChange-ready)
  • dynchain_core.h 扩展 ModuleFuncTable 新增可选 hook(getLatencySamples / getMemoryFootprint / propagate;对应 AWE 风格)
  • 删除 source_module_gen.c(Q5 不兼容策略)
  • 每模块 ≥ 3 个单测(DQ17 approved):基础生成 / 参数边界 / 多通道(见 §6)

2.4 阶段 3 · 后端简化 + LinkPropagator + Manifest C API(3.5 天)

2.4.1 LinkFrameBuilder 去改写 + 去硬编码(0.5 天)

  • backend_csharp/Services/Link/LinkFrameBuilder.cs
  • 移除 source_v1 → source_gen_v1 改写逻辑
  • 移除 blockSize=64 / sampleRate=48000 硬编码,改从 LinkPropagator 输出读
  • 若前端下发带旧 source_v1 / source_gen_v1 moduleId,直接返回 unknown_module_id 错误
  • AudioEngineService.cs 降级为"管理器":
  • 删除 _pcSourceSlots / _pcSinkSlots / _deviceInjectionSlots 三套字典
  • 合并为 SourceChannels / SinkChannels(按 instanceId 索引)
  • 移除 BackgroundService ExecuteAsync 循环
  • _blockSize 从硬编码改为从 LinkPropagator

2.4.2 LinkPropagator 新建(1 天)

  • backend_csharp/Services/Link/LinkPropagator.cs 新建
  • 实现 pass-through propagate(单一格式链路,DQ23 approved):
  • 阶段 1 · prePropagate:为每个 source/sink 端点设置 rootSpec(从 deviceInfo / wavInfo / 系统默认)
  • 阶段 2 · propagate:按拓扑序遍历模块,默认 outSpecs[i] = inSpecs[0]
  • 阶段 3 · postPropagate:校验全链路格式一致(不一致时报错,Phase 8 不支持自动插入 resampler)
  • 输出 FlattenedLinkConfig 的 ConnEntry 数组(blockSize / sampleRate / channels 全填实)
  • 预留 coreIdArray 字段(全 0,Phase 9 启用)
  • 单测覆盖:单一格式链路 OK / 格式不一致报错 / 空链路边界

2.4.3 WasapiDrivenEngine + SetParam 无锁队列(0.5 天)

  • backend_csharp/Services/Audio/WasapiDrivenEngine.cs 新建
  • MMCSS "Pro Audio" 优先级
  • WASAPI render callback 驱动(硬件时钟)
  • 每 block 在 callback 内调 DSPAlgo_Process()
  • 实现 lock-free MPSC Channel<ParamUpdate> + block 边界 drain
  • 抽象 ICoreExecutor 接口(Phase 9 可启动多实例,ThreadChange-ready)
  • SetParam 线程池优先级设为 Normal / BelowNormal(Thread.CurrentThread.Priority = ThreadPriority.BelowNormal

2.4.4 MetricsAggregator 新建(0.5 天)

  • backend_csharp/Services/Metrics/MetricsAggregator.cs 新建
  • 每 100ms 从 DSP 拉 metrics(调用 §5.3 的 DSPAlgo_GetMetrics / DSPAlgo_GetModuleRuntimeMetrics
  • 聚合 EWMA / peak / 百分比
  • 广播 metrics_update WS 消息(见 §4.3)
  • 告警逻辑:cpuLoadPercent > 70% 黄 / > 90% 红(DQ4 approved)
  • 全链路 totalLatency 累计(DQ13 approved,按拓扑序加 latencySamples

2.4.5 ProjectService 新建 + PresetProfileService 微调(0.5 天)

  • backend_csharp/Services/Project/ProjectService.cs 新建(DQ5 approved · 基本骨架)
  • SaveProject(projectName):读 paramStore 回刷 paramValues → 写 runtimeState → 拷贝 presets/profiles 目录
  • LoadProject(projectPath):拷贝目录回 current_pro → apply_link + 自动展开 profile
  • WS 协议 save_project / load_project
  • PresetProfileService.cs 微调:
  • LinkController.ApplyLink 在 apply_link 成功后内部调用 HandleSetAmbiance(activeProfileId)(DQ6 选项 A approved)
  • activeProfileId 为空而 moduleActivePreset 有值 → 逐个 HandleLoadPreset

2.4.6 Manifest C API 绑定 P/Invoke(0.5 天)

  • P/Invoke 绑定:
  • DSPAlgo_GetModuleManifest(uint32_t typeNumId, ModuleManifest* out)
  • DSPAlgo_GetModuleRuntimeMetrics(const char* instanceId, ModuleRuntimeMetrics* out)
  • 把 manifest 数据随 module_list WS 消息下发给前端

2.5 阶段 4 · 前端拆 10 ModuleDef + PortInfo(3 天)

  • frontend_vue3/src/stores/moduleLibrary.ts 拆 10 个 ModuleDef(按 review §4.6 参数 schema)
  • UI 节点面板分组(Q4 独立卡片):
  • 「外部音源」:device / wav(2 个)
  • 「噪声生成器」:white / pink / brown(3 个)
  • 「周期信号」:sine / sweep / triangle / square / saw(5 个)
  • ParamDef 新增 structural: boolean 字段(默认 false)
  • 前端参数变更 handler 按 structural 分派:
  • structural=true → 弹确认框 "修改此参数会重建链路(~10ms 中断)" → 走 apply_link
  • structural=false → 直接 set_param
  • ModuleDef 新增 portInfo: PortInfo[] 字段(声明端口默认 spec + 约束)
  • ModuleDef 新增 runtime hint 字段(cpuCyclesHintPerFrame / memoryBytesEstimate / latencySamples 等,由 manifest 自动填充)
  • ModuleInstance 类型加 coreId?: number(默认 0,ThreadChange-ready)
  • audioEngineStore.ts 去硬编码 blockSize/sampleRate,改从 devicePropertyInfo 动态
  • link.json 加载时,遇到旧 source_v1 / source_gen_v1 节点直接报错,要求用户重建
  • metricsStore.ts 新建 · 订阅 metrics_update WS 消息
  • link 状态徽章 UI(DQ14 approved · RUNNING / FADEOUT 两态)
  • Save/Load Project 基础按钮(DQ5 · 菜单"保存工程"/"加载工程")

2.6 阶段 5 · 监控实现(§4 全量)(2 天)

  • DSP framework 在 DSPAlgo_Process 入口 + 每模块 Process 前后打时间戳(clock_gettime / QueryPerformanceCounter
  • 累加到 metricsStore(lock-free relaxed atomic)
  • 导出 DSPAlgo_GetMetrics() + DSPAlgo_GetModuleRuntimeMetrics() C API
  • 后端 MetricsAggregator 每 100ms pull → 广播 metrics_update
  • 前端组件:
  • CPU 负载表(类似 AWE MIPS 显示横条 + 数字,黄 70% / 红 90%)
  • 模块负载饼图(per-module cpuPercentOfBlock
  • 内存占用条(framework / modules / ring buffers 分色)
  • Underrun/Overrun 计数器(红色大字)
  • totalLatency 显示(DQ13 approved · 全链路累计)
  • 历史曲线(可选,过去 60 秒 CPU 负载折线)

2.7 阶段 6 · 回归 + 新测试(2.5 天)

  • UnitTest_Mixer 重写 link.json 使用新 10 模块
  • 新场景 mixer_multi_source.json:sine + pink_noise 经 mixer 混音,验证 P0 不复现
  • 新场景 wasapi_driven_only.json:无 BackgroundService 稳定运行
  • 新场景 portinfo_propagate.json:多模块单一格式链路 propagate
  • 新场景 setambiance_smooth.json:批量切 100ms ramp 无 pop 的自动化检测(FFT 分析)
  • 8 个 Demo 的 e2e 脚本(见 §1.2)
  • 10 个 source 模块每个 ≥ 3 单测(DQ17 approved · 见 §6 矩阵)
  • AutoTest 全套回归

2.8 阶段 7 · 文档 + 合并(1 天)

  • 更新 04_development/CLAUDE.md 说明新 source/sink 语义 + moduleId 拆分
  • incident-5khz-mixer-override.md(P3.1 post-mortem)
  • phase8-handoff.md(含本计划各阶段实际人天 + 未完成事项 + Phase 9 接驳点)
  • link.json 格式 header 加 "schemaVersion": "8.0"(DQ18 approved)
  • ff-only 合入 master

3. DSP 侧改动清单(dsp_algo)

3.1 新增文件

路径 职责
dsp_algo/modules/source/source_common.h 10 source 模块共享 API 声明
dsp_algo/modules/source/source_common.c 相位累加器 / dB→lin / 通道填充 / Box-Muller / 参数平滑
dsp_algo/modules/source/source_sine.c 正弦
dsp_algo/modules/source/source_sweep.c 扫频(线性 / 对数 / one_shot / loop / ping_pong)
dsp_algo/modules/source/source_triangle.c 三角(symmetry 可调)
dsp_algo/modules/source/source_square.c 方波(dutyCycle 可调)
dsp_algo/modules/source/source_saw.c 锯齿(direction rising/falling)
dsp_algo/modules/source/source_noise_white.c 白噪(gaussian/uniform + correlated/uncorrelated)
dsp_algo/modules/source/source_noise_pink.c 粉噪(Voss-McCartney / filter_network)
dsp_algo/modules/source/source_noise_brown.c 布朗噪(integratorLeakage)
dsp_algo/modules/source/source_wav.c WAV 内嵌解码 + samples 原子 swap
dsp_algo/modules/source/source_device.c WASAPI passthrough
dsp_algo/include/module_manifest.h ModuleManifest / ParamManifest / PortManifest / ModuleRuntimeMetrics / MemoryBreakdown

3.2 修改文件

路径 改动
dsp_algo/modules/source/source_module.c 重命名为 source_ext.c,作为 wav/device 的 passthrough 基类(或废弃)
dsp_algo/modules/source/source_module_gen.c 删除(Q5 不兼容)
dsp_algo/framework/dynchain_core.h ModuleInstanceint coreId(默认 0)+ int processOrderModuleFuncTable 加可选 hook(getLatencySamples / getMemoryFootprint / prePropagate / propagate / postPropagate
dsp_algo/framework/dynchain_core.c Process 线程调度抽象 Scheduler_Run(coreId)(Phase 8 只调用 core 0)
dsp_algo/framework/module_registry_all.c 注册 10 个新模块,ModuleRegistryEntry 扩展 manifest 字段(老模块填 NULL 兼容)
dsp_algo/include/module_type_id.h 分配 10 个新 TypeNumId(0x10080010-0x10080019)+ 预留 thread_change_v1 0x100C0001 + 预留 3 个格式转换 0x100D0001-3
dsp_algo/include/dspalgo_api.h 新增 DSPAlgo_GetModuleManifest / DSPAlgo_GetModuleRuntimeMetrics / DSPAlgo_GetMetrics C API;DSPAlgo_Process 签名加 int coreId(Phase 8 总传 0)
dsp_algo/include/linkframe_format.md 二进制帧 ConnEntry 扩展 uint8_t srcCoreId + dstCoreId(Phase 8 全 0)

3.3 ThreadChange-Ready 接口预留(Phase 9 启用)

  • DSPAlgo_Process(int coreId, ...):Phase 8 总传 0;Phase 9 由后端调度器分配
  • ModuleInstance.coreId / processOrder:Phase 8 全 0;Phase 9 按 link_graph 分段
  • ConnEntry.srcCoreId / dstCoreId:Phase 8 全 0;Phase 9 判断是否需要跨核 ring buffer
  • Scheduler_Run(coreId):Phase 8 只注册 1 个 core 线程;Phase 9 多线程并行
  • module_type_id.h:Phase 8 预留 0x100C0001 thread_change_v1;Phase 9 实现

4. 后端改动清单(backend_csharp)

4.1 新增文件

路径 职责
Services/Link/LinkPropagator.cs 三阶段 propagate(prePropagate / propagate / postPropagate),Phase 8 只做 pass-through
Services/Audio/WasapiDrivenEngine.cs MMCSS Pro Audio + WASAPI render callback 驱动 + ICoreExecutor 抽象
Services/Audio/LockFreeMPSCQueue.cs SetParam 无锁队列(基于 Channel<ParamUpdate>
Services/Metrics/MetricsAggregator.cs 100ms pull DSP metrics + EWMA / peak 聚合 + WS 广播
Services/Project/ProjectService.cs SaveProject / LoadProject(基本骨架,DQ5 approved)

4.2 修改文件

路径 改动
Services/Link/LinkFrameBuilder.cs 移除 source_v1 → source_gen_v1 改写;移除 blockSize/sampleRate 硬编码;从 LinkPropagator 读实填格式
Services/AudioEngineService.cs 降级为管理器;删除 3 套影子槽位;_blockSize 参数化
Services/Preset/PresetProfileService.cs ApplyLink 成功后内部调 HandleSetAmbiance(activeProfileId)(DQ6 选项 A)
Controllers/LinkController.cs apply_link 路径整合 LinkPropagator + PresetProfileService 自动展开
Program.cs / DI 容器 注册新服务(LinkPropagator / WasapiDrivenEngine / MetricsAggregator / ProjectService)

4.3 WS 协议新增/扩展

消息 方向 用途
metrics_update S→C 每 100ms 广播 CPU / 内存 / per-module / underrun/overrun / totalLatency
link_state_change S→C link 状态徽章(RUNNING / FADEOUT,DQ14)
save_project C→S { projectName, projectPath }
save_project_ack S→C { path, success, error? }
load_project C→S { projectPath }
load_project_ack S→C { link, runtimeState, success, error? }
error_event S→C 分级错误(warning / error / fatal),Phase 8 支持 underrun/overrun + fatal(DQ16)

5. 前端改动清单(frontend_vue3)

5.1 新增文件

路径 职责
src/stores/metricsStore.ts 订阅 metrics_update,暴露整体 + per-module 指标
src/components/Metrics/CpuLoadMeter.vue CPU 负载横条(黄 70% / 红 90%)
src/components/Metrics/ModuleLoadPieChart.vue per-module cpuPercentOfBlock 饼图
src/components/Metrics/MemoryBreakdown.vue framework / modules / ring buffers 分色条
src/components/Metrics/LatencyDisplay.vue totalLatency + 各链路分支延时
src/components/Project/SaveLoadMenu.vue 工程保存/加载按钮 + 基础对话框

5.2 修改文件

路径 改动
src/stores/moduleLibrary.ts 拆 10 个 ModuleDef,每个 ModuleDef 加 portInfo / paramInfo.structural / runtime hint
src/stores/linkStore.ts 加载老 link.json 遇 source_v1 / source_gen_v1 节点报错
src/stores/audioEngineStore.ts 移除 blockSize/sampleRate 硬编码,从 devicePropertyInfo 动态
src/types/module.ts ModuleInstancecoreId?: number(默认 0);ParamDefstructural: boolean;新增 PortInfo / PortSpec / ModuleRuntimeHint 类型
src/components/LinkEditor/ChainNode.vue 结构性参数修改时弹确认框 "修改此参数会重建链路"
src/components/Header/LinkStatusBadge.vue 新建 · 订阅 link_state_change 显示 RUNNING / FADEOUT

6. 测试矩阵(DQ17 approved · 每模块 ≥ 3 测试)

6.1 10 source 模块单测(30 个测试)

模块 测试 1 · 基础生成 测试 2 · 参数边界 测试 3 · 多通道
source_sine_v1 1kHz 正弦 FFT 峰值 ≥ -60dB amplitudeDb=-120 静音 / amplitudeDb=0 满幅不削波 2 通道 channelPhaseOffsetDeg=90 验证相位差
source_sweep_v1 20Hz→20kHz 对数扫频持续 10s durationSec=0.01 最短合法 ping_pong 模式往返一次
source_triangle_v1 1kHz 三角 FFT 谐波分布 symmetry=0.01 近锯齿 多通道同频相位差
source_square_v1 1kHz 方波奇次谐波 dutyCycle=0.01 PWM 尖脉冲 多通道对齐
source_saw_v1 1kHz 锯齿频谱 direction=falling 波形取反 多通道对齐
source_noise_white_v1 4s 样本 RMS ≈ amplitudeDb ±0.5dB seed=42 可复现 correlated 两通道相关系数 > 0.99 / uncorrelated < 0.1
source_noise_pink_v1 -3dB/oct 斜率(FFT 拟合) voss_mccartney vs filter_network 差异 <1dB 同上
source_noise_brown_v1 -6dB/oct 斜率 integratorLeakage=0.01 vs 0.1 低频截止 同上
source_wav_v1 已知 WAV 播放波形匹配 filePath="" 拒绝加载 / >512MB 拒绝加载 readPosSec seek 到 mid / 末尾 loop
source_device_v1 Mic → passthrough RMS 合理范围 deviceId="" 拒绝启用 多通道 inputChannels=[0,1,2,3]

6.2 集成测试场景

  • UnitTest_Mixer/link/link.json(重写) · 4 source + mixer + gain + sink 完整链路
  • mixer_multi_source.json · 2 source 经 mixer 验证 P0 不复现
  • wasapi_driven_only.json · 无 BackgroundService 稳定运行 60s 无 underrun
  • portinfo_propagate.json · 多模块单一格式链路 propagate 正确
  • setambiance_smooth.json · 批量切换 10 参数 100ms 无 pop(FFT 检测瞬态能量 < -40dB)

6.3 Demo 1-8 的 e2e 自动化脚本

参见 §1.2,每个 Demo 一个 e2e 脚本(Playwright / 后端集成测试)。


7. DQ 清单 → Phase 8 任务映射

DQ 决策 Phase 8 落地位置
DQ1 §12.8 线程模型 阶段 3 · WasapiDrivenEngine + SetParam 队列
DQ2 MAX_WAV_MEMORY_MB=512 阶段 2 · source_wav.c 加载时校验
DQ3 capture ring buffer N=8 阶段 3 · WasapiDrivenEngine capture ring
DQ4 CPU 告警 70%/90% 阶段 5 · MetricsAggregator + 前端 CpuLoadMeter
DQ5 SaveProject 基本骨架 阶段 3 · ProjectService + 阶段 4 · SaveLoadMenu
DQ6 选项 A 后端内部调 SetAmbiance 阶段 3 · PresetProfileService / LinkController
DQ7 preset 跨前端不考虑 不落地(删除 review §13.2 相关 backlog)
DQ8 监控全量 阶段 5 · DSP timestamps + per-module + 内存 + underrun/overrun
DQ9 平滑 30ms/100ms 阶段 2 · source_common ParamRamp
DQ10 Phase 8 单核 + ThreadChange-ready 阶段 2+3 · 接口预留(§3.3 + §4.1 ICoreExecutor)
DQ11 SetThreadAffinityMask 硬绑定 阶段 3 · WasapiDrivenEngine.SetAffinity(Phase 9 启用)
DQ12 bufferFrames=128 thread_change_v1 manifest 默认(Phase 9)
DQ13 totalLatency 累计 阶段 5 · MetricsAggregator
DQ14 link 状态徽章 阶段 4 · LinkStatusBadge + 后端 link_state_change
DQ15 单链路 Phase 8 不支持多 session
DQ16 underrun/overrun + fatal 广播 阶段 5 · error_event WS
DQ17 10 模块每个 ≥3 测试 阶段 6 · 测试矩阵 §6.1
DQ18 schemaVersion 8.0 阶段 7 · link.json header
DQ19 mode 前端 readonly + 后端校验 阶段 4 · ChainNode.vue 分派逻辑
DQ20 文件 log(无实时 WS) 阶段 5 不做实时回流;前端"打开日志"按钮
DQ21 resampler/remap/convert 只预留 ID 阶段 2 · module_type_id.h 分配
DQ22 i18n/offline/plugin 全 Phase 10+ 不落地
DQ23 PortInfo pass-through 阶段 3 · LinkPropagator
DQ24 Manifest static+runtime 阶段 2 · module_manifest.h + §5 API
DQ25 16 天预算 本章节总表

8. ThreadChange-Ready 框架兼容性清单(Phase 8 落地位置)

详见 roadmap §3 的完整原文。此处仅列 Phase 8 必须落地的接口预留:

8.1 DSP 侧(阶段 2 落地)

改动 Phase 8 行为 Phase 9 启用
DSPAlgo_Process(int coreId, ...) coreId=0 硬传 后端调度器按 link_graph 分段分配
ModuleInstance.coreId / processOrder 默认 0 Phase 9 按拓扑 + ThreadChange 分段
ModuleFuncTable 加 propagate hook 默认 pass-through(outSpecs[i]=inSpecs[0] Phase 9 resampler 等覆盖
LinkFrame ConnEntry 加 srcCoreId/dstCoreId 全 0 Phase 9 判断是否需要 cross-core ring
module_type_id.h 预留 thread_change_v1 0x100C0001 不注册实现 Phase 9 实现
Scheduler_Run(coreId) 只调 core 0 Phase 9 多线程并行

8.2 后端 C# 侧(阶段 3 落地)

改动 Phase 8 行为 Phase 9 启用
ICoreExecutor 接口 + WasapiDrivenEngine 实现 启动 1 个实例 Phase 9 启动 N 个实例 + affinity
LinkFrameBuilder coreId 字段 全填 0 Phase 9 按 LinkGraph 分段填
LinkGraph.CoreSegments: List<CoreSegment> 只有 1 段 Phase 9 分 N 段
WS metrics_updatecore.{coreId}.* 字段 只上报 core 0 Phase 9 多核各自上报

8.3 前端 TS 侧(阶段 4 落地)

改动 Phase 8 行为 Phase 9 启用
ModuleInstance.coreId?: number 默认 0 Phase 9 用户拖 ThreadChange 时分配
LinkGraph 可视化 单色背景 Phase 9 按 coreId 分区着色
moduleLibrary 注册 thread_change_v1 stub disabled(不可拖入) Phase 9 enable

8.4 不会推翻的部分(v7.0 承诺)

  • ✅ §12.3 SetParam lock-free MPSC 队列机制不变(Phase 9 每 core 一个独立队列)
  • ✅ §12.4 Device capture 独立线程 + ring buffer 机制不变
  • ✅ §12.5 Wav 预读入内存机制不变
  • ✅ §14 监控指标体系不变(仅扩展 per-core 维度)
  • ✅ §10 参数生命周期四层模型不变
  • ✅ §4.6 10 模块参数 schema 不变
  • ✅ link.json 结构不变(仅新增可选字段 coreId / processOrder

8.5 Fallback · Phase 8 直接做多核的条件

若 Phase 8 阶段 3 实施过程中发现某个设计无法兼容多核(比如 WASAPI render callback 强绑定单线程无法抽象成 ICoreExecutor):

  • 直接重构为多核模型,不做"Phase 8 先单核、Phase 9 再加"的双阶段
  • 人天预算扩充:Phase 8 从 16 → 20-22 天
  • 在 review §13.3 DQ 清单新增告警项,由人类批准转入多核 scope

9. 监控指标体系(DQ8 approved · 全量)

9.1 指标清单

9.1.1 CPU 负载(Process 线程内采集)

指标 单位 采样粒度 含义
block.processTimeUs μs 每 block 本次 DSPAlgo_Process() 全链路耗时
block.processTimeUs_ewma μs 每 block(EWMA α=0.1) 平滑耗时
block.processTimeUs_peak μs 累计 峰值(可 reset)
block.cpuLoadPercent_avg % 每 100 blocks processTimeUs_ewma / blockDurationUs × 100
block.cpuLoadPercent_peak % 累计 processTimeUs_peak / blockDurationUs × 100
underrunCount 累计 WASAPI render buffer 读空次数
overrunCount 累计 Capture ring buffer 写满次数

9.1.2 单模块(per-instance)

指标 说明
module.{instanceId}.processTimeUs 单模块 Process 耗时
module.{instanceId}.processTimeUs_ewma 平滑
module.{instanceId}.processTimeUs_peak 峰值
module.{instanceId}.cpuPercentOfBlock 占 block 总耗时百分比

9.1.3 内存(按 owner 分类)

指标 归属
memory.framework.rss framework
memory.total.rss 全部(Windows Working Set / Linux RSS)
memory.module.{instanceId}.bytes 单模块(含 wav samples 动态 buffer)
memory.ringBuffer.capture.{instanceId}.bytes 每 capture ring
memory.paramQueue.pending lock-free param queue pending 数

9.1.4 延时(DQ13 approved)

指标 含义
chain.totalLatencyMs 全链路累积延时(拓扑序加模块 latencySamples
chain.branchLatencyMs[branch] 多路径(mixer)各分支延时(Phase 9 扩展)

9.2 采集 / 聚合 / 推送分工

flowchart LR
    subgraph PT["Process 线程 · 仅采集"]
        T1["打时间戳 pre/post Process()"]
        T2["累加到 metricsStore<br/>(lock-free relaxed atomic)"]
    end

    subgraph SetT["SetParam 线程池 · 聚合 + 推送"]
        A1["每 100ms 读 metricsStore 快照"]
        A2["EWMA / peak / 百分比计算"]
        A3["WS broadcast 'metrics_update'"]
    end

    subgraph FE["前端 UI"]
        G1["CPU 负载表(整体 + per-module)"]
        G2["内存占用饼图"]
        G3["黄/红阈值告警(70%/90%)"]
    end

    T1 --> T2
    T2 -.->|只读| A1
    A1 --> A2 --> A3
    A3 --> FE

关键原则

  • Process 线程只写 metricsStore,不做聚合(避免每 block 多做除法)
  • SetParam 线程池做聚合(优先级最低,反正闲)
  • metricsStore 使用 relaxed atomic(Process 写 + 聚合线程读,允许轻微 torn read)

9.3 前端展示

  • CPU 负载表:横条 + 数字(类似 AWE MIPS),黄 >70%,红 >90%
  • 模块负载饼图:按 per-module cpuPercentOfBlock 占比绘制
  • 内存占用条:framework vs modules vs ring buffers 分色
  • Underrun/Overrun 计数器:红色大字
  • totalLatency 显示:全链路数字 + (可选)分支延时
  • 历史曲线(可选):过去 60s CPU 负载折线

10. PortInfo Pass-Through Propagate(DQ23 approved)

10.1 三阶段 propagate 协议

flowchart TB
    SL["SetLink 到达后端"]

    subgraph PP["propagate pass(LinkPropagator 服务)"]
        S1["阶段 1 · prePropagate<br/>为每个 source/sink 端点<br/>设置 rootSpec"]
        S2["阶段 2 · propagate<br/>按拓扑序遍历模块<br/>默认 outSpecs[i] = inSpecs[0]"]
        S3["阶段 3 · postPropagate<br/>校验全链路格式一致性<br/>生成最终 ConnEntry 数组"]
    end

    LF["下发 LinkFrame 到 DSP"]
    DSP["DSP DynChain_Init 基于实填的 ConnEntry 分配 Wire"]

    SL --> S1 --> S2 --> S3 --> LF --> DSP

10.2 PortSpec 三端统一数据结构

DSP C 侧

typedef struct {
    uint32_t sampleRate;      // Hz,0 = inherit
    uint32_t blockSize;       // frames/block,0 = inherit
    uint16_t numChannels;     // 1-1023,0 = inherit
    uint8_t  dataType;        // DSP_DTYPE_FLOAT32 等
    uint8_t  isComplex;       // 0/1
    uint16_t maxChannels;     // 上限
    uint32_t maxBlockSize;    // 上限
} port_spec_t;

后端 C# 侧

public record PortSpec(
    int SampleRate,       // 0 = inherit
    int BlockSize,        // 0 = inherit
    int NumChannels,      // 0 = inherit
    string DataType,      // "float32" / "int32" / ...
    bool IsComplex = false
);

前端 TS 侧

export interface PortSpec {
  sampleRate: number;      // 0 = inherit
  blockSize: number;       // 0 = inherit
  numChannels: number;     // 0 = inherit
  dataType: 'float32' | 'int32' | 'float64' | 'int16';
  isComplex?: boolean;
}

export interface PortInfo {
  portName: string;
  direction: 'input' | 'output';
  defaultSpec: PortSpec;
  constraints?: {
    minChannels?: number;
    maxChannels?: number;
    supportedDataTypes?: string[];
    supportedSampleRates?: number[];
  };
}

10.3 Phase 8 propagate 行为(pass-through)

  • 99% 模块outSpecs[i] = inSpecs[0](framework 默认实现,模块不实现 propagate 即可)
  • source 模块:输出 spec = source 自身声明的 spec(tone/noise 继承系统默认;wav 来自文件头;device 来自 WASAPI GetMixFormat)
  • sink 模块:只读 inSpec,不输出
  • 格式不一致:后端报 format_mismatch 错误,Phase 8 不自动插入 resampler(Phase 9 支持,见 roadmap §4.2)

10.4 修正文档表述

  • 全文移除 blockSize=64/48kHz=1.33ms 的硬编码表述
  • 监控 cpuLoadPercentprocessTimeUs / blockDurationUs × 100 动态算(§9.1.1)
  • blockDurationUs = blockSize / sampleRate × 1,000,000

11. Module Manifest(DQ24 approved · Static + Runtime)

11.1 Static Metadata

typedef struct ModuleManifest_ {
    // 版本与分类
    const char *version;         // "1.0.0"
    const char *category;        // "source" / "filter" / "mixer" / "sink" / "utility"
    const char *description;
    const char *vendor;
    const char *license;

    // 端口 schema
    int numInPorts;
    int numOutPorts;
    const PortManifest *inPorts;
    const PortManifest *outPorts;

    // 参数 schema
    int numParams;
    const ParamManifest *params;

    // 运行时估算
    uint32_t cpuCyclesHintPerFrame;
    uint32_t memoryBytesEstimate;
    uint32_t latencySamples;
    uint32_t groupDelaySamples;
    uint32_t tailSamples;

    // 支持的格式
    int numSupportedSampleRates;
    const uint32_t *supportedSampleRates;
    uint32_t minBlockSize;
    uint32_t maxBlockSize;
    uint8_t supportedDataTypes;  // bit mask
} ModuleManifest;

11.2 Runtime Metrics Hook

typedef struct {
    // v1 原有 6 个
    getMemSize, init, process, setParam, getParam, destroy,

    // v2 新增(所有可选,默认用 manifest 静态声明值)
    int  (*getLatencySamples)(ModuleInstance *inst);
    int  (*getMemoryFootprint)(ModuleInstance *inst, MemoryBreakdown *out);
    int  (*prePropagate)(ModuleInstance *inst, const port_spec_t *in, port_spec_t *out);
    int  (*propagate)(ModuleInstance *inst, const port_spec_t *in, port_spec_t *out);
    int  (*postPropagate)(ModuleInstance *inst);
} ModuleFuncTableV2;

typedef struct {
    uint64_t stateBytes;        // 模块 state struct
    uint64_t bufferBytes;       // 动态 buffer(wav samples 等)
    uint64_t workingSetBytes;
    uint64_t totalBytes;
} MemoryBreakdown;

11.3 C API 导出

int DSPAlgo_GetModuleManifest(uint32_t typeNumId, ModuleManifest *out);

int DSPAlgo_GetModuleRuntimeMetrics(
    const char *instanceId,
    ModuleRuntimeMetrics *out
);

typedef struct {
    uint32_t processTimeUs;
    uint32_t processTimeUs_ewma;
    uint32_t processTimeUs_peak;
    uint64_t processCallCount;
    MemoryBreakdown memory;
    uint32_t latencySamples;
    uint32_t groupDelaySamples;
} ModuleRuntimeMetrics;

11.4 Phase 8 模块 manifest 填写范围

  • 10 新 source 模块 + gain + mixer + sink_device + sink_wav(共 14 个)全部填 manifest(~30 分钟/模块)
  • 老模块(若 still in use)可暂填 NULL,framework 用默认

11.5 前端 ModuleDef 对齐

export interface ModuleDef {
  id: string;
  displayName: string;
  description: string;
  category: string;

  version: string;
  vendor: string;
  portInfo: PortInfo[];
  paramInfo: ParamInfo[];  // 含 role / structural / unit

  cpuCyclesHintPerFrame: number;
  memoryBytesEstimate: number;
  latencySamples: number;
  groupDelaySamples?: number;
  tailSamples?: number;
}

Phase 8 手工维护;Phase 9 考虑"framework 启动时从 DSP 导出 manifest JSON,前端动态加载"。


12. 附录 · 相关文档


版本历史

版本 日期 状态 变更
v1.0 2026-05-08 published 从 review v6.0 §6 / §14.5 / §15.8 / §16.4 / §17.6 / §19 拆分独立成文,基于 DQ1-DQ25 全确认版整合:6 阶段 16 天预算 + 8 Demo 验收 + DSP/C#/Vue 三端改动清单 + ThreadChange-ready 兼容性清单 + PortInfo pass-through 协议 + Module Manifest static+runtime + 监控指标全量(DQ8) + DQ → 任务映射表

Phase 8 实施计划 · v1.0 · 2026-05-08 · © Xisound AlgoDepartment · doc_id: D3-SYS-ARCH-002

反馈

对本计划有意见?请提 PR 或联系 algo-team@xisound.com。