跳转至
FULFILLED

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.c PC)
  • §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.hMODULE_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 步)

  1. vendor 发布:第三方按 SDK 模板编 dll · 部署到 plugins/thirdparty/<vendor-name>/<dll-name>.dll
  2. 用户启动 Xisound:host 启动时 DSPAlgo_InitDynChain_LoadDynamicPlugins → 扫描加载 · 日志输出 stats
  3. 打开 xilink:ModuleLibrary 面板拉取 /api/plugins/list · 看到 builtin 和第三方分类
  4. 拖入 stage:用户从 ModuleLibrary 拖第三方 module 到 stage · 前端走与 builtin 相同的 instance 创建流程(typeNumId 走 v7 ParseLinkFrame)
  5. 运行验证:用户连接 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 边界铁律(强制约束)

  1. ABI 复用 v7:任何 PC 第三方 dll 必须用 v7 ModuleFuncTable · 禁止引入第二套 module ABI
  2. DSP 永远静态注册:任何"DSP 动态加载"提议 = 拒绝 · 维持 v7 编译期注册
  3. typeNumId 冲突后者 skip:不允许 vendor 覆盖 builtin · 不允许多个 vendor 占同一 typeNumId
  4. 加载错误不阻塞 host:任何 dll 加载失败 → log + skip · host 仍启动
  5. 入口符号唯一性:第三方 dll 仅导出 xisound_plugin_register · 禁止暴露内部符号
  6. MVP 不做沙箱 / 签名 / 热加载 / 卸载:本 ADR 仅落"启动时加载" · 其余留下季度
  7. 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 端零修改。