Phase 8 实施计划 v1.0
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_developmentworktree 的 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.csource_noise_white.c/source_noise_pink.c/source_noise_brown.csource_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扩展ModuleInstance加coreId(默认 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_v1moduleId,直接返回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_updateWS 消息(见 §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_listWS 消息下发给前端
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_updateWS 消息 - 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 |
ModuleInstance 加 int coreId(默认 0)+ int processOrder;ModuleFuncTable 加可选 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 bufferScheduler_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 |
ModuleInstance 加 coreId?: number(默认 0);ParamDef 加 structural: 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 无 underrunportinfo_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_update 加 core.{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的硬编码表述 - 监控
cpuLoadPercent按processTimeUs / 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. 附录 · 相关文档
- 架构决策与 DQ 清单:Source/Sink 架构 Review v7.0
- Phase 9+ 十议题与 ThreadChange 详细设计:系统能力路线图 v1.0
- 格式规范:D0-company/05-standards/md-style-guide.md v1.1
- 命名规范:D0-company/05-standards/doc-numbering.md v1.0
- 分层规范:D0-company/05-standards/doc-code-sync-policy.md v1.0
版本历史
| 版本 | 日期 | 状态 | 变更 |
|---|---|---|---|
| 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。