ADR-AIOS-14 · PC 端第三方插件 DLL 动态加载决议
1. 上下文(Context)
1.1 触发事件
2026-06-01 19:00 用户 + Cline-AIOS 在 PLAN 模式下对 ADR-AIOS-09 v1.1(2026-05-30 accepted · 583 行)做系统性复核,发现 ADR-09 与 dsp-algo v7 架构文档(docs/03-platform/40-dsp-algo/10-architecture-v7.md · 2455 行)严重平行重复设计,95% 内容已在 v7 实现。用户 19:18 拍板路线 B:撤回 ADR-09 + 4 个未派 fork prompt 全部 abort + 起新 ADR 仅保留净新增内容。
1.2 用户原话(2026-06-01 决策)
"其实当前的架构已经能够满足大部分场景了,剩下一个 PC 端的 dll 动态加载,这个其实和 audioweaver 是一样的同样的算法代码编译成 dll 后,适配 dsp 的静态库编译,就实现了 PC 可以加载,DSP 也可以加载的配套问题;如果这个问题能够解决我们直接走方案 B 就好了,直接删除现在启用的 P1.UA9 相关的 prompt,这些任务我都没有执行,可以直接归档做不实施处理,然后新起 ADR"
1.3 上游依赖(必读 · 不重复)
本 ADR 的"模块 ABI / 内存模型 / chain 调度引擎 / 注册表"全部复用 dsp-algo v7 已有设计 · 不重新发明。读本 ADR 前必读:
- dsp-algo v7 架构文档:
docs/03-platform/40-dsp-algo/10-architecture-v7.md(2455 行 · 2026-05-12) - §3 平台抽象层(
DynChainPlatformOps/platform_static.c嵌入式 /platform_dynamic.cPC) - §4.1
WireInstance(buffer + sampleRate + 3 × wireInfo 位域 · 12 字节 POD · 输入 wire 指针零拷贝指向上游输出) - §4.2
ModuleFuncTable(getMemSize / init / process / setParam / getParam / destroy 6 函数指针 · 本 ADR 强制复用 · 不引入第二套 ABI) - §4.3
module_type_id.h(高 16 位 vendor + 低 16 位模块号 · 0x1001~0x1008 已分配 builtin · 0xAAAA0000+ 留给第三方 vendor) - §4.4
modules_config.h(编译期DYNCHAIN_ENABLE_*0/1 开关 · 模块裁剪) - §4.5 公开 API(
DynChain_GetMemSize/Init/Process/SetParam/RequestRebuild/RegisterAllModules) -
§5
ModuleRegistry(typeNumId查找表 ·ModuleRegistry_Register(typeNumId, typeName, &funcs)单一注册函数) -
ADR-AIOS-04 §2.1.2 XiForge 架构 vendor 段位规范(0xAAAA0000~0xFFFEFFFF 给 21845 家 xivst 厂商)
1.4 真值核查 · v7 已实现 vs 净新增范围
| 能力 | v7 是否实现 | ADR-09 重复发明 | ADR-14 处置 |
|---|---|---|---|
| 统一 module ABI(6 函数指针) | ✅ ModuleFuncTable |
❌ 重造 XisoundModuleFactory |
复用 v7 · 拒绝重造 |
| 模块注册表(O(N) 查找) | ✅ ModuleRegistry |
❌ 重造 PluginRegistry |
复用 v7 · 仅扩展新增 1 个加载器函数 |
| 数据流跨 dll(零拷贝) | ✅ WireInstance + Wire_ChanPtr |
❌ 重造 process(in[], out[], frames) |
复用 v7 · 第三方 dll 直接用 WireInstance |
| chain 拓扑解析 + 排序 | ✅ DynChain_Parser + Kahn BFS |
❌ 完全没碰 | 复用 v7 · 与本 ADR 无关 |
| 链路热重建(淡出→重建→淡入) | ✅ DynChain_RequestRebuild 状态机 |
⚠️ 模糊提"热卸载安全协议" | 复用 v7 · 第三方 dll 卸载走 Rebuild 流程 |
| 平台抽象(PC malloc / DSP 静态池) | ✅ platform_static.c + platform_dynamic.c |
❌ 完全没碰 | 复用 v7 · 一套代码两份内存策略 |
| 模块编译裁剪 | ✅ modules_config.h 编译开关 |
❌ 重造 per-module STATIC 拆分 | 复用 v7 · DSP 第三方走 vendor 段位 + 加 ENABLE 开关 + 重编 |
| PC 端 LoadLibrary 加载第三方 dll | ❌ v7 未实现 | ✅ ADR-09 §2.3 提了 | 本 ADR 唯一净新增 |
结论:本 ADR 净新增内容 = 1 个 framework API + 1 个 dll 入口约定 + 错误处理协议。其余全部复用 v7。
2. 决议(Decision)
2.1 主决议 · PC/DSP 同源代码 · 双编译目标
核心理念(对齐 AudioWeaver):
模块源码(modules/<vendor>/<name>/*.c · 一份代码)
├── PC 编译目标:<vendor>-<name>.dll
│ - 链接 framework PC host SDK lib(导出 ModuleRegistry_Register)
│ - 单一导出符号 xisound_plugin_register(host 启动时 LoadLibrary 调用)
│ - 部署到 plugins/thirdparty/<vendor>/
│ - host 运行时扫描加载 · 注册到 v7 ModuleRegistry
└── DSP 编译目标:vendor-name.a 静态库
- 链接进 DSPAlgoStatic 总包
- 修改 modules_config.h 加 DYNCHAIN_ENABLE_VENDOR_NAME 1
- 修改 module_registry_all.c 加 #if 块 + ModuleRegistry_Register 调用
- 重编固件烧录
PC 与 DSP 共用同一份 module 源码 · 仅编译目标不同。第三方 vendor 拿到 SDK 后:
- PC:cmake -DTARGET=PC → 输出 <vendor>-<name>.dll
- DSP:把源码交给 Xisound 内部团队 · 走"加 type_id + 加 config 开关 + 重编"流程
2.2 PC 端第三方 DLL 入口约定
单一导出符号(每个第三方 dll 必须导出):
// xisound_plugin_register: 第三方 dll 唯一对外入口
// host 通过 GetProcAddress("xisound_plugin_register") 获取函数指针
// dll 内部对自己提供的每个 module 调用 host 传入的 registerFn
// → 把 ModuleFuncTable 注册到 v7 ModuleRegistry
typedef void (*ModuleRegistryRegisterFn)(
uint32_t typeNumId, // 0xAAAA0000+ · 第三方 vendor 段位(ADR-04)
const char *typeName, // 字符串类型名 · 用于日志和 ParseLinkFrame 兜底查找
const ModuleFuncTable *pFuncs // v7 §4.2 函数表 · 6 个函数指针
);
// dll 必须导出此函数 · 名字固定为 "xisound_plugin_register"
__declspec(dllexport)
void xisound_plugin_register(ModuleRegistryRegisterFn registerFn);
dll 内部典型实现(第三方 vendor "FooEffect" 提供 reverb + chorus 两个 module):
// foo-effect.c (vendor 自己写 · SDK 提供模板)
#include "module_interface.h" // v7 SDK header
// vendor 段位 0xAAAA0001 (FooEffect:reverb_v1)
#define MODULE_TYPE_FOO_REVERB_V1 0xAAAA0001u
#define MODULE_TYPE_FOO_CHORUS_V1 0xAAAA0002u
// 每个 module 实现 v7 ModuleFuncTable 的 6 函数 · 与 builtin module 完全相同
extern const ModuleFuncTable kFooReverbFuncs;
extern const ModuleFuncTable kFooChorusFuncs;
// 唯一导出符号
__declspec(dllexport)
void xisound_plugin_register(ModuleRegistryRegisterFn registerFn) {
registerFn(MODULE_TYPE_FOO_REVERB_V1, "foo_reverb_v1", &kFooReverbFuncs);
registerFn(MODULE_TYPE_FOO_CHORUS_V1, "foo_chorus_v1", &kFooChorusFuncs);
}
关键设计:
- ❌ 不引入 ADR-09 的 XisoundPluginInfo / XisoundModuleDescriptor / XisoundModuleFactory 三层结构
- ✅ 直接复用 v7 ModuleFuncTable · 第三方 dll module 与 builtin module 完全等价
- ✅ host 加载完毕后 · v7 ModuleRegistry 内部不区分"builtin / 第三方" · DynChain_Parser 统一查表
2.3 host 加载器(framework 净新增 1 个 API)
新增 API(在 framework/dynchain_loader_pc.c 实现 · v7 公开 API 集合扩展):
/**
* DynChain_LoadDynamicPlugins
* 扫描指定目录的 *.dll · 加载并注册到全局 ModuleRegistry。
* 仅 PC 平台实现 · DSP 平台编译时 #ifdef 排除本文件。
*
* @param pluginsDir 插件目录绝对路径(如 "C:/Xisound/plugins/thirdparty")
* @param pPlatform v7 平台抽象(用于 log + 错误报告)
* @param pStats 输出:加载统计(loaded / failed / skipped 计数)
* @return DYNCHAIN_OK · 即使部分 dll 失败也不阻塞 host 启动
*
* 加载流程:
* 1. 枚举 pluginsDir/<vendor>/*.dll
* 2. 对每个 dll:
* a. LoadLibraryW(dllPath) 失败 → log + skip + failed++
* b. GetProcAddress("xisound_plugin_register")
* 缺失 → FreeLibrary + log + skip + failed++
* c. registerFn(&ModuleRegistry_Register)
* dll 内部循环调 ModuleRegistry_Register
* 遇到 typeNumId 冲突 → 后者 skip + log warn + skipped++
* d. 记录 LoadedPluginEntry{ dllPath, hModule, registeredCount }
* e. loaded++
* 3. 返回 stats
*
* 注意:本 API 不卸载已加载 dll · 卸载走 DynChain_UnloadPlugin(下一版本)
*/
typedef struct LoadDynamicPluginsStats_ {
uint32_t scanned; /* 扫描到的 dll 总数 */
uint32_t loaded; /* 成功加载并注册的 dll 数 */
uint32_t failed; /* LoadLibrary 或 GetProcAddress 失败 */
uint32_t skipped; /* typeNumId 冲突跳过的 module 数 */
} LoadDynamicPluginsStats;
int32_t DynChain_LoadDynamicPlugins(
const char *pluginsDir,
const DynChainPlatformOps *pPlatform,
LoadDynamicPluginsStats *pStats /* 可为 NULL */
);
host 启动整合点(dll/dspalgo_dll.c 现有初始化链):
void DSPAlgo_Init(void) {
// 现有 v7 流程
DynChain_RegisterAllModules(); // builtin module 编译期注册
// 新增:第三方 dll 运行时加载(PC only)
#ifdef _WIN32
LoadDynamicPluginsStats stats = {0};
DynChain_LoadDynamicPlugins(
"C:/ProgramData/Xisound/plugins/thirdparty",
Platform_GetDynamicOps(),
&stats
);
// 日志:scanned=N loaded=M failed=K skipped=L
#endif
// 现有 v7 流程继续(DynChain_GetMemSize / Init / Process 等待用户下发 link frame)
}
2.4 DSP 端不变(明确说明 · 防回归)
DSP 端永远走静态注册 · 本 ADR 不引入任何 DSP 动态加载机制:
| 第三方 .a 集成步骤 | 操作 |
|---|---|
| 1. 申请 vendor 段位 | 联系 Xisound · 分配 0xAAAA????~0xFFFE???? 段 |
| 2. 提供源码 + .a | 第三方提供模块源码(C/C++) + ADSP-21489 编译产物 .a |
| 3. 加 type_id 常量 | 在 module_type_id.h 加 MODULE_TYPE_VENDOR_NAME_V1 0xAAAA0001u |
| 4. 加裁剪开关 | 在 modules_config.h 加 #define DYNCHAIN_ENABLE_VENDOR_NAME 1 |
| 5. 加注册块 | 在 module_registry_all.c 加 #if DYNCHAIN_ENABLE_VENDOR_NAME 块 + ModuleRegistry_Register 调用 |
| 6. 重编 + 烧录 | cmake --build 出新固件 → 烧录到 DSP |
→ 这套流程v7 已经实现 · 本 ADR 不修改。
2.5 业务行为契约 5 必填段(对齐 .clinerules v1.8 §"业务行为契约必填段")
① 输入/输出契约
DynChain_LoadDynamicPlugins API 契约:
| 字段 | 类型 | 单位/约束 |
|---|---|---|
pluginsDir |
const char* |
UTF-8 编码 · 绝对路径 · 不存在则跳过(不报错) |
pPlatform |
DynChainPlatformOps* |
v7 §3 平台抽象 · 必填 · NULL 返回 ERR_INVALID |
pStats.scanned |
uint32_t |
扫描到的 dll 文件数(含失败) |
pStats.loaded |
uint32_t |
LoadLibrary + GetProcAddress + register 全部成功的 dll 数 |
pStats.failed |
uint32_t |
任一阶段失败的 dll 数 · 详情见 log |
pStats.skipped |
uint32_t |
typeNumId 冲突跳过的 module 数(非 dll) |
第三方 dll 入口契约:
- 必须导出符号:xisound_plugin_register(C linkage · 无名字修饰)
- 函数签名:void (ModuleRegistryRegisterFn registerFn)
- 调用 registerFn 的 typeNumId 必须落在 vendor 段(0xAAAA0000~0xFFFEFFFF)
- 调用次数 = dll 提供的 module 数 · 1-N 任意
② 收敛/成功判据
| 判据 | 阈值 | 含义 |
|---|---|---|
| host 启动不阻塞 | 任一 dll 失败均不阻塞 | 加载错误隔离 · 仅缺该 module |
| typeNumId 唯一性 | 全局表无冲突 | builtin + 第三方累计无重号 |
| ModuleRegistry 大小 | builtin + 加载成功 module 数 | log 中 stats.loaded × N 与 ModuleRegistry_Count 匹配 |
| 端到端拖入可用 | 第三方 module 出现在 ModuleLibrary | 前端拉 /api/plugins/list 看到 vendor 段 typeName |
| 实时处理无差别 | 第三方 module 与 builtin 同样跑 DynChain_Process | 注入 1kHz 正弦 · 输出符合 module 设计预期 |
③ 失败回退路径(5 类)
| 失败 | 触发 | UI 表现 | 恢复路径 |
|---|---|---|---|
| dll 文件损坏 | LoadLibraryW 返回 NULL |
日志 ERR · ModuleLibrary 不显示该 vendor 段 | 重装 dll 或删除 |
| 入口符号缺失 | GetProcAddress("xisound_plugin_register") NULL |
同上 + 日志注明 "missing entry" | vendor 重编 dll · 必须导出符号 |
| register 调用抛出异常 | dll 内部 register 函数 access violation | 同上 + 日志栈跟踪(若有) | vendor 修 bug |
| typeNumId 冲突 | 后到 register 调用与已注册 module 重号 | 日志 WARN · 后者 skip · 已注册仍可用 | vendor 改用未占段位 |
| host stop 时 dll 未释放 | 进程退出未 FreeLibrary | OS 进程清理(无影响) | MVP 不处理 · 留下版本 DynChain_UnloadPlugin |
④ 用户操作流(端到端 5 步)
- vendor 发布:第三方按 SDK 模板编 dll · 部署到
plugins/thirdparty/<vendor-name>/<dll-name>.dll - 用户启动 Xisound:host 启动时
DSPAlgo_Init→DynChain_LoadDynamicPlugins→ 扫描加载 · 日志输出 stats - 打开 xilink:ModuleLibrary 面板拉取
/api/plugins/list· 看到 builtin 和第三方分类 - 拖入 stage:用户从 ModuleLibrary 拖第三方 module 到 stage · 前端走与 builtin 相同的 instance 创建流程(typeNumId 走 v7 ParseLinkFrame)
- 运行验证:用户连接 source → 第三方 module → sink · 启动链路 · 注入测试信号 · 听/看输出符合预期
⑤ 端到端真值 e2e
测试用例:tests/e2e/dynamic-plugin-load.test.ts
test('[ADR-14] 第三方 dll 加载 + 端到端音频验证', async () => {
// Setup: 部署 hello-world plugin 到 plugins/thirdparty/test/hello-effect.dll
// 该 dll 实现 1 个 module:gain × 2(typeNumId = 0xAAAA0001)
// vendor 编译产物路径:tests/fixtures/hello-effect-v1.dll
await fs.copyFile(
'tests/fixtures/hello-effect-v1.dll',
'plugins/thirdparty/test/hello-effect.dll'
);
// Step 1: 启动 host
await startHost();
// Step 2: 验证 stats
const stats = await api.get('/api/plugins/load-stats');
expect(stats.loaded).toBeGreaterThanOrEqual(1);
expect(stats.failed).toBe(0);
// Step 3: 验证 ModuleLibrary 暴露
const modules = await api.get('/api/plugins/list');
const helloModule = modules.find(m => m.typeNumId === 0xAAAA0001);
expect(helloModule).toBeDefined();
expect(helloModule.typeName).toBe('hello_effect_gain2x_v1');
// Step 4: 拖入 chain · 注入 1kHz 正弦 -20dBFS · 持续 1s
const chain = await createChain([
{ type: 'source', uid: 'source#0' },
{ type: 'hello_effect_gain2x_v1', uid: 'hello#0' },
{ type: 'sink', uid: 'sink#0' }
], [
{ from: 'source#0:0', to: 'hello#0:0' },
{ from: 'hello#0:0', to: 'sink#0:0' }
]);
await injectSine(chain, { freq: 1000, levelDb: -20, durationMs: 1000 });
// Step 5: 断言输出 = 输入 + 6dB(gain × 2 = +6dB)
const peak = await measurePeakDb(chain.output);
expect(peak).toBeCloseTo(-14, 1); // -20 + 6 = -14 dBFS · ±1dB 容差
});
通过判据:loaded ≥ 1 + ModuleLibrary 暴露 + chain 处理输出 -14dBFS ± 1dB。
2.6 边界铁律(强制约束)
- ABI 复用 v7:任何 PC 第三方 dll 必须用 v7
ModuleFuncTable· 禁止引入第二套 module ABI - DSP 永远静态注册:任何"DSP 动态加载"提议 = 拒绝 · 维持 v7 编译期注册
- typeNumId 冲突后者 skip:不允许 vendor 覆盖 builtin · 不允许多个 vendor 占同一 typeNumId
- 加载错误不阻塞 host:任何 dll 加载失败 → log + skip · host 仍启动
- 入口符号唯一性:第三方 dll 仅导出
xisound_plugin_register· 禁止暴露内部符号 - MVP 不做沙箱 / 签名 / 热加载 / 卸载:本 ADR 仅落"启动时加载" · 其余留下季度
- PC SDK 与 DSP 模块源码同源:PC 与 DSP 编译共用 module 源码 · vendor 不允许提供 PC-only 实现而 DSP 不可移植
3. 后果(Consequences)
3.1 正面后果
✅ 工作量大幅下降:ADR-09 17.5d → ADR-14 6.5d(63% 减少 · v7 复用 80%) ✅ 零 ABI 漂移风险:不引入第二套接口 · v7 ABI 自动覆盖 PC + DSP + 第三方 ✅ AudioWeaver 模式精确对齐:第三方 dll 走单一 register 入口 · host 不感知"builtin / thirdparty"差异 ✅ DSP 端零修改:v7 既有架构无变更 · 嵌入式团队无新工作量 ✅ PC/DSP 同源代码:第三方 vendor 一份 module 源码 · 双编译目标 ✅ 错误隔离:dll 加载错误不阻塞 host · 故障域局限到单 module
3.2 负面后果与缓解
| 后果 | 影响 | 缓解 |
|---|---|---|
| ⚠️ 第三方 dll 崩溃拖死 host | in-process 加载 · 无沙箱 | MVP 不处理 · 下季度 ADR-14-R1 评估 plugin-host-process(VST3 sandbox 风格) |
| ⚠️ 无签名校验 | 用户随便放 dll 即加载 | MVP 走"plugins/thirdparty/ 用户白名单目录" · 后续 PKI 留商务 |
| ⚠️ typeNumId 段位冲突 | 多 vendor 协调成本 | Xisound 内部维护 vendor 段位分配表(类比 IEEE OUI) |
| ⚠️ 卸载 dll 走 chain rebuild | 用户体验:必须停链路才能卸载 | 复用 v7 DynChain_RequestRebuild 状态机 · 不重新发明 |
| ⚠️ 第三方需要 v7 SDK | vendor 学习曲线 | xivst-sdk 提供 hello-world 模板 + abi-reference.md 文档 |
3.3 关键非目标(Non-Goals)
- ❌ 不引入新 module ABI(强制复用 v7 ModuleFuncTable)
- ❌ 不实施进程隔离 / 沙箱(MVP 走 in-process)
- ❌ 不建立 PKI 签名 / xivst 认证中心(留商务侧)
- ❌ 不实施热加载 / 卸载(MVP 仅启动时扫描)
- ❌ 不实施 DSP 端动态加载(永久禁止)
- ❌ 不修改 protocol-v1.0(本 ADR 只在 framework 层加载 · 不需契约协调)
4. 实施清单(Implementation)
4.1 立即行动(本 ADR accepted 后立即派 · 优先级 P1)
| # | U-thread | 部门 | CPU | 工作量 | 描述 |
|---|---|---|---|---|---|
| 1 | P6.UB14-load-dynamic-plugins-api | DSP | ClaudeB | 2.0d | framework/dynchain_loader_pc.c 新增 · 实现 DynChain_LoadDynamicPlugins + LoadDynamicPluginsStats · 错误处理 5 类全 · 单元测试覆盖 4 失败路径 + 1 happy path |
| 2 | P6.UB14-pc-host-sdk-export | DSP | ClaudeB | 1.0d | dll/dspalgo_dll.c 导出 ModuleRegistry_Register 给第三方 dll 链接 · 整理 PC host SDK lib (xisound-host.lib) |
4.2 短期解锁(优先级 P2)
| # | U-thread | 部门 | CPU | 工作量 | 描述 |
|---|---|---|---|---|---|
| 3 | P5.UB14-plugin-management-api | 后端 | ClaudeB | 1.5d | C# IPluginRegistry Service · /api/plugins/list + /api/plugins/load-stats REST endpoint · 通过 P/Invoke 拉 v7 ModuleRegistry |
| 4 | P1.UB14-module-library-from-registry | 前端 | ClaudeA | 1.0d | ModuleLibrary 改造:hardcode → /api/plugins/list · builtin / thirdparty 分类显示 · vendor 名标记 |
4.3 中期协调(下个迭代 · 优先级 P2)
| # | U-thread | 部门 | CPU | 工作量 | 描述 |
|---|---|---|---|---|---|
| 5 | xivst-sdk.UB14-pc-only-bootstrap | DSP+Docs | ClaudeB+Cline-AIOS | 2.0d | xivst-sdk-1.0 PC-only 包 · include/(v7 module_interface.h)+ lib/windows/(xisound-host.lib)+ templates/hello-effect-v1/ + docs/abi-reference + pc-build-guide |
总计:6.5d(vs ADR-09 17.5d · 减少 63%)
4.4 长期演进(下季度+)
| # | 议题 | 触发 |
|---|---|---|
| 6 | plugin-host-process 进程隔离(VST3 sandbox) | 第三方插件首次 crash host 后 |
| 7 | DynChain_UnloadPlugin 热卸载 + 卸载安全协议 |
开发者反馈强烈 |
| 8 | xivst PKI 签名机构 | 商务侧合作伙伴 ≥3 家 |
| 9 | 第三方 plugin marketplace UI | 生态 ≥5 个 vendor |
5. 验收(Acceptance)
5.1 文档验收
- ADR-AIOS-14 主文件落盘(本文件 · status: proposed)
- ADR-AIOS-09 main → ref/ 完成 + frontmatter superseded ✅(Step 2 已落)
- DASHBOARD.md 加 ADR-14 派发清单 + ADR-09 撤回历史段
- P_arch/PROCESS.md 关闭 ADR-09 子事件 + 加 ADR-14 子事件
- P6/P5/P1 PROCESS.md 关闭 4 UA9 fork + 加 4 UB14 fork
5.2 实施验收(§4.1-§4.3 全部 fork 完成)
| 检查项 | 通过条件 |
|---|---|
| P6.UB14-load-dynamic-plugins-api | DynChain_LoadDynamicPlugins 实装 · 单元测试 ≥5 case 全过 · 5 类失败路径全验证 |
| P6.UB14-pc-host-sdk-export | xisound-host.lib 导出 ModuleRegistry_Register · vendor 可链接 |
| P5.UB14-plugin-management-api | /api/plugins/list + /load-stats 200 OK · 返回 builtin + 第三方合并列表 |
| P1.UB14-module-library-from-registry | ModuleLibrary 改 API 数据源 · 拖拽创建实例正常(builtin + 第三方) |
| xivst-sdk.UB14-pc-only-bootstrap | hello-effect-v1.dll 编译成功 · 部署到 plugins/thirdparty/test/ · host 启动加载成功 · ModuleLibrary 显示 |
5.3 端到端真值验收(必跑)
§2.5 e2e 真值脚本 tests/e2e/dynamic-plugin-load.test.ts 全过(loaded ≥1 + 输出 -14dBFS ± 1dB)。
5.4 边界铁律验收(长期)
- 任何"第二套 module ABI"提议 → 拒绝 · 强制复用 v7
- 任何"DSP 动态加载"提议 → 拒绝 · 维持 v7 静态注册
- 任何"in-process sandbox"提议 → 起 ADR-14-R1 · 不直接改本 ADR
6. 替代方案(Alternatives Considered)
6.1 替代方案 A(已废弃 · 即 ADR-AIOS-09)· 自定义 plugin_abi.h + per-module STATIC
- 描述:见
ADR/ref/ADR-AIOS-09-plugin-architecture.md - 废弃理由:与 v7 架构 95% 重叠 · 重复发明 ABI · DSP 端拆分伪需求 · 工作量 17.5d 过高
- 本 ADR 选择:复用 v7 + 仅净新增 PC LoadLibrary
6.2 替代方案 B(已废弃)· VST3 / LV2 / CLAP ABI 直接复用
- 描述:不自定义 ABI · 直接用 VST3 等开源协议
- 废弃理由:VST3 太重(COM 风格)· LV2 Linux 强耦合 · CLAP 不稳定 · 都不支持 DSP 嵌入式
- 本 ADR 选择:复用 v7 ModuleFuncTable(轻量 + PC/DSP 双轨)
6.3 替代方案 C(已废弃)· 进程外加载(VST3 sandbox)
- 描述:第三方 dll 跑独立进程 · 走 IPC
- 废弃理由:实时音频跨进程延迟不可接受 · MVP 复杂度过高
- 本 ADR 选择:in-process 加载 · 沙箱留下季度
7. 相关决议与文档
7.1 上游依赖(必读)
- dsp-algo v7 架构文档:
docs/03-platform/40-dsp-algo/10-architecture-v7.md(2455 行) - ADR-AIOS-04 §2.1.2:Module UID vendor 段位规范
7.2 被取代
- ADR-AIOS-09:
ADR/ref/ADR-AIOS-09-plugin-architecture.md(superseded · 2026-06-01)
7.3 平行 / 协调
- ADR-AIOS-08 XiLink Stage UX:本 ADR 提供 ModuleLibrary 数据源 API
- ADR-AIOS-10 控制信号画布:不直接相关
- ADR-AIOS-12 XiTest 实时架构:不直接相关
7.4 相关 PROCESS.md
processes/P6-dsp-algo/PROCESS.md(主战场 · 2 fork UB14)processes/P5-backend-csharp/PROCESS.md(1 fork UB14)processes/P1-xilink/PROCESS.md(1 fork UB14)processes/P_arch/PROCESS.md(本 ADR 子事件)processes/P_arch/ADR-AIOS-14-pc-dynamic-plugin-loading/PROCESS.md(子进程 PCB · 待创建)
8. 状态流转(Status History)
| 日期 | 状态 | 操作 | 说明 |
|---|---|---|---|
| 2026-06-01 19:00 | 起草触发 | Cline-AIOS 在 PLAN 模式复核 ADR-09 · 发现与 v7 架构 95% 重叠 | 5 路 subagent 调研 |
| 2026-06-01 19:18 | 用户拍板 | 路线 B:撤回 ADR-09 + abort 4 fork prompt + 起新 ADR | 用户原话 §1.2 |
| 2026-06-01 19:25 | proposed | ADR-AIOS-14 v1.0 落盘 | 等用户 accept ADR-AIOS-14 |
| 2026-06-02 09:33 | accepted ✅ | 用户 09:32 同步指令 accept ADR-AIOS-14 + start P6.UB14-load-dynamic-plugins-api |
5 fork 解锁 · fork 1 立即 dispatched(ClaudeB 2.0d) |
| 2026-06-02 11:01 | F1 zombie | stop P6.UB14-load-dynamic-plugins-api(3c83f25) |
framework loader API 就位 · ABI 基石 |
| 2026-06-02 11:40 | F2+F3 双 zombie | 双 stop:F2 P6.UB14-pc-host-sdk-export(c440023)+ F3 P5.UB14-plugin-management-api(8d53b68) |
host SDK 导出 + C# REST + P/Invoke 三层就位 |
| 2026-06-02 13:50 | F4 zombie | stop P1.A14.F4-module-library-from-registry(e90b044)+ hotfix 489dd40 双数据源并存 |
前端 ModuleLibrary 改 hardcode → /api/plugins/list 闭环(⚠️ DEVIATION 教训记录) |
| 2026-06-02 15:25 | fulfilled 🏆🏆🏆 | stop F5 xivst-sdk.A14.F5-pc-only-bootstrap(47da15c · ClaudeB+Cline-AIOS · 21 文件 +1205/-10)· e2e 真值用户亲测:/api/plugins/list 返回 hello_effect_gain2x_v1 + load-stats 1/1/0/0 |
5 fork 全 zombie · status accepted+impl → fulfilled · 等用户照 ADR-AIOS-14-acceptance-checklist.md 36 项手动验收即正式归档 |
| 2026-06-02 | fork 5 zombie ✅ | ClaudeB 完成 xivst-sdk-1.0 · commit 47da15c · e2e 通过: /api/plugins/list 返回 hello_effect_gain2x_v1 + load-stats(scanned=1 loaded=1 failed=0 skipped=0) |
ADR-14 整体闭环 5/5 fork zombie |
9. 关键术语表(Glossary)
| 术语 | 定义 |
|---|---|
| ModuleFuncTable | v7 §4.2 · 6 个函数指针(getMemSize/init/process/setParam/getParam/destroy)· 本 ADR 强制复用 |
| ModuleRegistry | v7 §5 · 全局 typeNumId 查找表 · ModuleRegistry_Register(typeNumId, typeName, &funcs) 注册 |
| WireInstance | v7 §4.1 · 12 字节 POD · 输入 wire 指针零拷贝指向上游输出 wire · 跨 dll 边界透明 |
| xisound_plugin_register | 第三方 PC dll 唯一导出符号 · void xisound_plugin_register(ModuleRegistryRegisterFn) |
| DynChain_LoadDynamicPlugins | 本 ADR 净新增 framework API · PC only · 扫 plugins/thirdparty/ 加载 dll |
| vendor 段位 | ADR-04 §2.1.2 · 0xAAAA0000~0xFFFEFFFF 给 21845 家第三方 vendor |
| xivst-sdk | 第三方 PC 插件开发包 · include + lib + templates + docs · DSP 端不发 SDK |
10. 决议者签名(Approvers)
- 拍板:用户(2026-06-01 19:18 · "路线 B · 直接删除 P1.UA9 相关 prompt · 归档不实施 · 起新 ADR")
- 起草:Cline-AIOS · cwd=
d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/ - 真值核查:Cline-AIOS 读 ADR-09 全文 + v7 架构 §1-§7(2455 行前 1300 行)+ ADR-04/08/10 调研
- API 评审(待):ClaudeB(P6 主战场)在 P6.UB14-load-dynamic-plugins-api 实施前评审
- 后续修订:任何修改本 ADR 必须先 fork ADR-14-RX(R = Revision)子进程
本 ADR = ADR-AIOS-09 的精简重制 · 仅保留 PC 端 LoadLibrary 1 项净新增 · 其余全部复用 dsp-algo v7 架构 · 工作量 6.5d(vs ADR-09 17.5d · 减少 63%)· 严守"v7 ABI 不二造"边界铁律 · DSP 端零修改。