系统数据模型与链路重构架构设计 (v2.0)
基于 AWE 源码分析(
awe_module_structure_analysis.md)+ 现有系统架构(doc/System_Architecture.md+doc/DSPAlgo_Architecture.md)综合推导输出目标:明确三层(前端 → 后端 → DSPAlgo)的完整数据结构,提供链路重构验证路径
一、三层数据模型总览
┌──────────────────────────────────────────────────────────────────────────────┐
│ 数据模型三层对照表 │
│ │
│ 前端 (TypeScript / Pinia) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ LinkConfig │ │
│ │ ├── global: { sampleRate, blockSize, dataType } │ │
│ │ └── chains: { root: ChainDefinition, ... } │ │
│ │ ├── nodes[]: ChainNode │ │
│ │ │ ├── instanceId / moduleType │ │
│ │ │ ├── ports[]: PortDescriptor ← Wire格式信息 │ │
│ │ │ └── params: ModuleParams ← 模块算法参数 │ │
│ │ └── edges[]: ChainEdge ← 连接关系+信号规格 │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ WebSocket: write_link (JSON) │
│ ▼ │
│ 后端 (C# ASP.NET Core 8) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ FlattenedLinkConfig │ │
│ │ ├── modules[]: FlatModuleEntry │ │
│ │ │ ├── instanceId / typeNumId │ │
│ │ │ ├── portCfg: PortCfg[] ← 解析后的确定性Wire规格 │ │
│ │ │ └── initParams[] ← 转换为数字ID的参数列表 │ │
│ │ └── connections[]: ConnectionEntry │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Binary Frame v3 (set_link CMD=0x02) + set_param CMD=0x01 │
│ ▼ │
│ DSPAlgo C层 (DynamicChain / AWE audiochain) │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ DynamicChain │ │
│ │ ├── instances[]: ModuleInstance │ │
│ │ │ ├── instanceId / typeId / typeNumId │ │
│ │ │ ├── portCfg: ModulePortCfg ← PortSpec[] │ │
│ │ │ ├── pState: void* ← 算法模块状态内存 │ │
│ │ │ └── ppOut: float** ← 每模块独立输出缓冲区 │ │
│ │ └── connections[]: DynChainConnection │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
二、前端数据结构(TypeScript)
2.1 全局链路配置
/** 顶层链路配置(对应保存文件 current_link.json) */
interface LinkConfig {
version: '3.0'
rootChainId: string
global: GlobalWireConfig // 链路全局 Wire 默认值
chains: Record<string, ChainDefinition>
}
/** 全局 Wire 默认规格(当 PortDescriptor.channels == -1 时继承此值) */
interface GlobalWireConfig {
sampleRate: number // 如 48000
blockSize: number // 如 240(帧大小)
dataType: WireDataType // 如 'fract32'
channels: number // 如 20(全局最大通道数,仅作参考)
}
type WireDataType = 'float32' | 'fract32' | 'int16' | 'int32' | 'int24'
2.2 链路定义
interface ChainDefinition {
id: string
name: string
nodes: ChainNode[]
edges: ChainEdge[]
externalPorts?: PortDescriptor[] // 仅子图链路(subgraph)需要
}
2.3 链路节点(模块实例)
interface ChainNode {
instanceId: string // 如 "gain#1", "xisnd#1"
moduleType: string // 如 "channel_gain_v1", "xisnddemom_v1"
order: number // 执行顺序(拓扑排序基础)
enabled: boolean
/** ① Wire 信息:各端口的信号规格(分离于算法参数) */
ports: PortDescriptor[]
/** ② 模块算法参数(具体参数结构按模块类型不同) */
params?: ModuleParams
/** UI 位置(仅前端使用,不下发DSP) */
position: { x: number; y: number }
size?: { width: number; height: number }
}
2.4 Wire 信息 — PortDescriptor(与 AWE WireInstance 一一对应)
/**
* 端口描述符 — 对应 AWE WireInstance 的格式信息
*
* AWE 对照:
* channels ← wireInfo1[9:0] NUM_CHANNELS
* maxBlockSize ← wireInfo1[26:10] MAX_BLOCKSIZE
* isComplex ← wireInfo1[27] IS_COMPLEX
* sampleSizeB ← wireInfo1[31:28] SAMPLE_SIZE_BYTES
* blockSize ← wireInfo2[16:0] BLOCK_SIZE
* dataType ← wireInfo2[22:17] DATA_TYPE
* rows/cols ← wireInfo3[9:0]/[19:10]
* isIPC ← wireInfo3[20]
* isPrivate ← wireInfo3[21]
* isClockMaster ← wireInfo3[22]
*/
interface PortDescriptor {
id: string // 端口ID,如 "input", "output", "input_0"
direction: 'input' | 'output'
required: boolean
// ── wireInfo1 对应字段 ──────────────────────────────
channels: number // -1 = 继承上游;正整数 = 固定值
maxBlockSize?: number // 最大帧大小(可选,默认继承 blockSize)
isComplex?: boolean // 是否复数信号,默认 false
sampleSizeBytes?: number // 每采样字节数(0=由dataType推导)
// ── wireInfo2 对应字段 ──────────────────────────────
blockSize: number // -1 = 继承全局;正整数 = 固定值
dataType: WireDataType | -1 // -1 = 继承全局
// ── wireInfo3 对应字段(矩阵信号,通常为0)─────────
rows?: number // 矩阵行数,默认0
cols?: number // 矩阵列数,默认0
isIPC?: boolean // 跨核IPC,默认false
isPrivate?: boolean // 私有wire,默认false
isClockMaster?: boolean // 时钟主源,默认false
// ── 辅助字段(UI显示)─────────────────────────────
sampleRate: number // -1 = 继承全局
label: string // 如 "主输入", "Ch A", "Left Out"
}
2.5 连接关系(边)
/**
* 连线 — 对应 AWE m_pBoundPin / DynChainConnection
* channels/sampleRate 由前端自动解析,不需用户手动填写
*/
interface ChainEdge {
id: string
fromModule: string // 上游节点 instanceId
fromPort: string // 上游端口 ID
toModule: string // 下游节点 instanceId
toPort: string // 下游端口 ID
// 解析后的确定性值(由前端 propagateWireInfo 计算)
channels: number
sampleRate: number
blockSize: number
dataType: WireDataType
accumulatedDelaySamples?: number // 累积延迟(用于延迟补偿显示)
}
2.6 模块算法参数(按模块类型分类)
2.6.1 模块参数基类
/** 所有模块参数的基类 */
interface ModuleParams {
/** 模块状态(对应 AWE packedFlags[25:24]) */
moduleState?: 'active' | 'bypass' | 'mute' | 'inactive'
}
2.6.2 Gain 模块参数(channel_gain_v1)
interface GainModuleParams extends ModuleParams {
enable: boolean // 模块使能
smoothTimeMs: number // 平滑时间(ms)
gainDb: number[] // 每通道增益(dB),索引=通道号
mute: boolean[] // 每通道静音
phase: boolean[] // 每通道相位翻转
}
2.6.3 Delay 模块参数(ut_delay_20ch_v1)
interface DelayModuleParams extends ModuleParams {
enable: boolean
delaySamples: number[] // 每通道延迟(采样点数)
}
2.6.4 Xisnd 主算法模块参数(xisnddemom_v1)
/**
* 对应 awe_modXisndDemomInstance 的公开参数(7个)
* TuningBuffer 和 AlgoMem 为运行时内存,前端不直接操作
*/
interface XisndModuleParams extends ModuleParams {
// Build 阶段参数(对应 AWE public args 0-6)
enable: number // INT32: 0=关闭, 1=开启
vehicleCfg: number // UINT32: 车型配置(Param ID 0x16670006)
algoChanNum: number // UINT32: 算法通道数(Param ID 0x16670002)
numOutChan: number // UINT32: 输出通道数
memSizeAlgo: number // UINT32: 算法内存大小(words)
memSizeArch: number // UINT32: 架构内存大小(words)
memSizeTune: number // UINT32: 调音内存大小(words)
// 在线调音参数(通过 TuningBuffer 通道传输,前端通过专用调音面板操作)
// tuningParams 存储待写入 TuningBuffer 的参数列表
tuningParams?: TuningParam[]
}
interface TuningParam {
paramId: number // xisnd 内部参数 ID(如 0x16670004)
data: number[] // 参数数据
}
2.6.5 Source 模块参数(source_v1,链路输入源)
/**
* 音源模块 - 链路输入端,无 DSP 算法实例
* 其 Build 参数直接决定输出 Port 的 Wire 规格
*/
interface SourceModuleParams extends ModuleParams {
blockSize: number // 每帧采样数(如 240)
numChannels: number // 输出通道数(如 16)
sampleRate: number // 采样率(如 48000)
dataType: WireDataType // 数据类型(如 'fract32')
isComplex: number // 0 = 实数信号,1 = 复数信号
}
2.7 前端完整数据示例(Gain + Delay + Xisnd 链路)
{
"version": "3.0",
"rootChainId": "root",
"global": {
"sampleRate": 48000,
"blockSize": 240,
"dataType": "fract32",
"channels": 20
},
"chains": {
"root": {
"id": "root",
"name": "主链路",
"nodes": [
{
"instanceId": "source#1",
"moduleType": "source_v1",
"order": 0,
"enabled": true,
"position": { "x": 100, "y": 200 },
"ports": [
{
"id": "output",
"direction": "output",
"required": true,
"channels": 20, "blockSize": 240, "sampleRate": 48000,
"dataType": "fract32", "isComplex": false,
"sampleSizeBytes": 4, "label": "音源输出"
}
],
"params": {
"blockSize": 240, "numChannels": 20,
"sampleRate": 48000, "dataType": "fract32", "isComplex": 0
}
},
{
"instanceId": "gain#1",
"moduleType": "channel_gain_v1",
"order": 1,
"enabled": true,
"position": { "x": 300, "y": 200 },
"ports": [
{ "id": "input", "direction": "input", "required": true,
"channels": -1, "blockSize": -1, "sampleRate": -1, "dataType": -1, "label": "输入" },
{ "id": "output", "direction": "output", "required": true,
"channels": -1, "blockSize": -1, "sampleRate": -1, "dataType": -1, "label": "输出" }
],
"params": {
"enable": true, "smoothTimeMs": 10.0,
"gainDb": [0.0, 0.0, -3.0, -3.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0],
"mute": [false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false],
"phase": [false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false,
false, false, false, false]
}
},
{
"instanceId": "xisnd#1",
"moduleType": "xisnddemom_v1",
"order": 2,
"enabled": true,
"position": { "x": 550, "y": 200 },
"ports": [
{ "id": "input", "direction": "input", "required": true,
"channels": 20, "blockSize": 240, "sampleRate": 48000,
"dataType": "fract32", "label": "算法输入" },
{ "id": "output", "direction": "output", "required": true,
"channels": 20, "blockSize": 240, "sampleRate": 48000,
"dataType": "fract32", "label": "算法输出" }
],
"params": {
"enable": 1,
"vehicleCfg": 0,
"algoChanNum": 20,
"numOutChan": 20,
"memSizeAlgo": 3000116,
"memSizeArch": 0,
"memSizeTune": 80000
}
}
],
"edges": [
{
"id": "e1",
"fromModule": "source#1", "fromPort": "output",
"toModule": "gain#1", "toPort": "input",
"channels": 20, "sampleRate": 48000, "blockSize": 240, "dataType": "fract32"
},
{
"id": "e2",
"fromModule": "gain#1", "fromPort": "output",
"toModule": "xisnd#1", "toPort": "input",
"channels": 20, "sampleRate": 48000, "blockSize": 240, "dataType": "fract32"
}
]
}
}
}
三、后端数据结构(C# + 链路展平)
3.1 展平链路配置
/** 后端接收 LinkConfig JSON 后,ChainFlattener 展平子图为平坦结构 */
class FlattenedLinkConfig
{
public string RootChainId { get; set; }
public GlobalWireConfig Global { get; set; }
public List<FlatModuleEntry> Modules { get; set; }
public List<FlatConnectionEntry> Connections { get; set; }
}
class GlobalWireConfig
{
public int SampleRate { get; set; }
public int BlockSize { get; set; }
public string DataType { get; set; } // "fract32" / "float32" / ...
public int Channels { get; set; }
}
3.2 展平模块条目
class FlatModuleEntry
{
public string InstanceId { get; set; } // "gain#1", "xisnd#1"
public string TypeName { get; set; } // "channel_gain_v1"
public uint TypeNumId { get; set; } // 0x10010001
public int Order { get; set; } // 拓扑顺序
public bool Enabled { get; set; }
/** Wire 信息:解析后的确定性端口规格(channels/sampleRate 已全部解析为正整数) */
public List<ResolvedPortSpec> Ports { get; set; }
/** 初始化参数列表(转换为 set_param 下发序列) */
public List<ParamSetEntry> InitParams { get; set; }
}
class ResolvedPortSpec
{
public string PortId { get; set; }
public int Direction { get; set; } // 0=in, 1=out
public int Required { get; set; }
public ushort Channels { get; set; } // 已解析,无-1
public uint SampleRate { get; set; } // 已解析
public ushort BlockSize { get; set; } // 已解析
public byte DataType { get; set; } // 枚举值
public byte IsComplex { get; set; }
public byte SampleSizeBytes { get; set; }
}
3.3 展平连接条目
class FlatConnectionEntry
{
public int FromModuleIdx { get; set; } // 在 Modules[] 中的索引
public int FromPortIdx { get; set; }
public int ToModuleIdx { get; set; }
public int ToPortIdx { get; set; }
public ushort Channels { get; set; }
public uint SampleRate { get; set; }
}
3.4 参数下发条目
class ParamSetEntry
{
public ushort ParamTypeId { get; set; } // 如 0x0101 (GainDb)
public ushort ChannelIdx { get; set; } // 1D: ch; 2D: (ch<<8)|band; 全局: 0
public float Value { get; set; }
}
3.5 后端转换流程
Frontend JSON (LinkConfig)
│
▼ ChainFlattener.Flatten()
FlattenedLinkConfig
├── 1. 递归展平 SubGraphNode → 前缀化 instanceId
├── 2. PropagateWireInfo() → 解析所有 -1 端口 → 确定性 channels/sampleRate
├── 3. TypeNameToNumId() → "channel_gain_v1" → 0x10010001
└── 4. ParamsToParamSetList() → ModuleParams → List<ParamSetEntry>
├── Gain: gainDb[0..N] → paramTypeId=0x0101, channelIdx=0..N
├── Delay: delaySamples[0..N] → paramTypeId=0x0301
└── Xisnd: enable=0x0103, vehicleCfg → mask→paramTypeId
│
▼ BinaryFrameBuilder.BuildSetLink()
Binary Frame v3 (CMD=0x02)
│ [版本:u8=0x03][模块数:u8][连接数:u8][预留:u8]
│ + 模块表(每条88字节)
│ + 连接表(每条12字节)
▼
DSP / PC DLL
四、DSPAlgo 数据结构(C 层)
4.1 模块实例在 DynamicChain 中的完整状态
/*
* DynamicChain 中的模块实例
* 收到 set_link Binary Frame v3 后,由 DynamicChain_ParseLinkFrame 填充
*/
typedef struct ModuleInstance_ {
/* ── 标识 ─────────────────────────────────────── */
char instanceId[32]; // "gain#1", "xisnd#1"
char typeId[32]; // "channel_gain_v1"
uint32_t typeNumId; // 0x10010001
/* ── 函数表(对应 AWE ModClassModule vtable)────── */
const ModuleFuncTable *def;
// def->getMemSize / init / process / setParam / getParam
// def->setParamNum / getParamNum (二进制协议路径)
/* ── 状态内存(从 MemPool 分配)─────────────────── */
void *pState;
// Gain: → GainModuleData { GainParams + GainChannelState[] }
// Delay: → DelayModuleData { DelayParams + DelayLineState[] }
// Xisnd: → awe_modXisndDemomInstance { ModuleInstanceDescriptor +
// enable + VehicleCfg + AlgoChanNum + NumOutChan +
// MemSize_Algo + MemSize_Arch + MemSize_Tune +
// TuningBuffer* + AlgoMem* }
/* ── Wire 端口规格(对应前端 PortDescriptor 解析后)─ */
ModulePortCfg portCfg;
// portCfg.ports[i]:
// portId[] direction required
// channels sampleRate
// (blockSize/dataType/isComplex 通过连接表隐含)
/* ── 每模块独立输出缓冲区(从 MemPool 分配)──────── */
float *outputBufferMem; // 连续内存 [ch × maxFrameSize]
float *ppOut[32]; // ppOut[ch] 指针数组
uint16_t resolvedOutChannels;
/* ── 调试观测 ─────────────────────────────────── */
CaptureHook inputCapture;
CaptureHook outputCapture;
DebugMetrics metrics; // { cpuUs, peakCpuUs }
} ModuleInstance;
4.2 Xisnd 模块内存布局(AlgoMem 详细)
AlgoMem (UINT32*, ~11.4 MB)
├─ [offset=0] adsp_v1_platform (8 bytes)
│ ├── ptr_FunTab → adsp_audio_vtbl_t (16B)
│ │ ├── end() 销毁算法实例
│ │ ├── process() 实时处理 (pIn, pOut)
│ │ ├── set_param() 设置算法参数(Param ID)
│ │ └── get_param() 读取算法参数
│ └── ptr_Param → HandlWrapper (44B)
│ ├── vehicleCfg / enable / AlgoChanNum
│ ├── AlgoMemSize = MemSize_Algo + MemSize_Arch
│ ├── TunMemSize / pTunMem → TuningBuffer
│ ├── mFmtInput → MediaFmt (28B)
│ │ └── num_chan / num_port / is_interLeaved /
│ │ fmt_stream / num_bytePerSmp / num_smpPerCh / smp_rate
│ └── mFmtOutput → MediaFmt (28B)
├─ [offset=sizeof(adsp_v1_platform)] adsp_audio_vtbl_t
├─ [offset=...] HandlWrapper
├─ [offset=...] MediaFmt (Input)
├─ [offset=...] MediaFmt (Output)
└─ [offset=...] 算法核心工作内存 (MemSize_Algo + MemSize_Arch words)
TuningBuffer (UINT32*, 80,000 words = ~312 KB)
├─ [0] isSet: 0x44332211 (WRITE) / 0xAABBCCDD (READ)
├─ [1] pID: 算法内部 Param ID
├─ [2] pSize: 数据长度(实际读取后回填)
└─ [3..] data: 参数数据
4.3 连接表与数据流
/* 连接表条目 — 对应前端 ChainEdge */
typedef struct DynChainConnection_ {
uint8_t fromModuleIdx; // 上游模块在 instances[] 中的索引
uint8_t fromPortIdx; // 上游模块 portCfg.ports[fromPortIdx]
uint8_t toModuleIdx; // 下游模块索引
uint8_t toPortIdx; // 下游模块端口索引
uint16_t channels; // 连线通道数(已解析)
uint32_t sampleRate; // 连线采样率
} DynChainConnection;
/*
* Process 执行时的数据路由(Process函数中收集 ProcessInput):
*
* 对于 xisnd#1(toModuleIdx=2):
* 遍历 connections[],找到 toModuleIdx==2 的条目:
* → gain#1(idx=1).ppOut[0..19] 作为 inputs[0].ppData
* 调用 xisnd_process(pState, inputs, numInputs=1, ppOut, outCh=20, nbSamples=240)
* → _pif->ptr_FunTab->process(_pif, pIn[], pOut[])
*/
五、链路重构完整流程
5.1 链路构建(Chain Builder 模式)
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 1: 前端 write_link │
│ │
│ 前端 LinkEditor 拖拽构建链路 │
│ → linkStore.exportLink() → JSON (LinkConfig v3.0) │
│ → WebSocket: { "type": "command", "payload": { "type": "write_link", │
│ "data": <LinkConfig JSON> } } │
└─────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 2: 后端解析与展平 │
│ │
│ HandleWriteLink(): │
│ 1. 持久化 → ./data/current_link.json │
│ 2. ChainFlattener.Flatten() → FlattenedLinkConfig │
│ ├── 递归展平子图(SubGraphNode → 前缀化instanceId) │
│ ├── PropagateWireInfo() → 所有-1端口解析为确定值 │
│ └── 生成 FlatModuleEntry[] + FlatConnectionEntry[] │
│ 3. 参数转换: │
│ ├── ModuleParams → List<ParamSetEntry>(数字paramTypeId) │
│ └── TypeNameToNumId("channel_gain_v1" → 0x10010001) │
│ 4. BinaryFrameBuilder.BuildSetLink(flatConfig) → byte[] (CMD=0x02) │
│ 5. IDspTransport.DeliverAsync(frame) │
│ ├── DSP硬件: TCP/Serial发送 │
│ └── PC仿真: P/Invoke → DynChain_ParseLinkFrame() │
└─────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 3: DSP 链路重构 │
│ │
│ DynamicChain_ParseLinkFrame(pChain, pData, dataLen): │
│ 1. DynamicChain_ClearAll() ← 清空旧链路(重置MemPool) │
│ 2. For each module entry: │
│ a. 从注册表查找 typeNumId → ModuleFuncTable │
│ b. 填充 portCfg(PortSpec[])← 前端 PortDescriptor 解析后的Wire规格 │
│ c. def->getMemSize(&portCfg) → 分配 MemPool 内存 │
│ d. def->init(pState, &portCfg) ← 初始化模块状态 │
│ e. 分配 per-module 输出缓冲区 (resolvedOutChannels × blockSize) │
│ 3. For each connection entry: │
│ → 填充 DynChainConnection { fromIdx, fromPortIdx, toIdx, toPortIdx, │
│ channels, sampleRate } │
│ 4. DynamicChain_TopoSort() ← 拓扑排序(Kahn's BFS) │
└─────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 4: 参数初始化下发 │
│ │
│ For each FlatModuleEntry.InitParams: │
│ → Binary Frame CMD=0x01: [moduleIdx:u8][paramTypeId:u16] │
│ [channelIdx:u16][type:u8][val:f32] │
│ → DSP: DynamicChain_SetParamNum(instanceId, paramTypeId, │
│ channelIdx, &value, 4) │
│ ├── Gain: GAIN_PARAM_GAIN_DB(0x0101), channelIdx=0..N, val=-3.0 │
│ └── Xisnd: XISND_PARAM_ENABLE(0x0103), channelIdx=0, val=1.0 │
│ XISND_PARAM_VEHICLE_CFG(~) → MASK_XisndDemom_VehicleCfg │
│ → S->VehicleCfg = value │
│ → awe_modXisndDemomSet(MASK=0x00000200) → set_param API │
└─────────────────────────────┬────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Step 5: 全量初始化 Xisnd(触发 mask=0xFFFFFFFF) │
│ │
│ DynChain_SetParam("xisnd#1", "initFull", &1, 4) │
│ → awe_modXisndDemomSet(pInstance, 0xFFFFFFFF) │
│ → 在 AlgoMem 中布局: │
│ adsp_v1_platform → adsp_audio_vtbl_t → HandlWrapper │
│ → MediaFmt(Input) → MediaFmt(Output) │
│ → pAlgoMem (算法核心) │
│ → platform_init(_pif) ← 初始化 xisnd 算法核心 │
└──────────────────────────────────────────────────────────────────────────┘
5.2 参数实时调整(Tuning 模式)
用户在 GainTuningDialog 调整 gainDb[2] = -6.0
│
▼ linkStore.setParamValue("gain#1", "gainDb#2", -6.0)
│ WebSocket: { "type": "command", "payload": {
│ "type": "set_param",
│ "instanceId": "gain#1",
│ "paramId": "gainDb#2",
│ "value": -6.0 } }
▼
后端 HandleSetParam():
│ paramStore["gain#1.gainDb#2"] = -6.0 (内存缓存)
│ Binary Frame CMD=0x01:
│ [moduleIdx=1][paramTypeId=0x0101][channelIdx=2][type=float32][-6.0f]
▼
DSP: DynamicChain_SetParamNum("gain#1", 0x0101, 2, &(-6.0f), 4)
→ Gain_SetParamNum → g->targetGainDb[2] = -6.0f
→ 下一帧 Gain_Process 中线性平滑过渡到新增益值
用户在 XisndDemom 调音面板下发 TuningBuffer 参数:
│ WebSocket: { "type": "command", "payload": {
│ "type": "set_param",
│ "instanceId": "xisnd#1",
│ "paramId": "tuningBuffer",
│ "data": { "isWrite": true, "paramId": 0x16670004, "payload": [...] } } }
▼
后端构建 TuningBuffer 内容:
TuningBuffer[0] = 0x44332211 (WRITE)
TuningBuffer[1] = 0x16670004 (paramId)
TuningBuffer[2] = payloadSize
TuningBuffer[3..] = payload data
→ Binary Frame CMD=0x01: [moduleIdx][MASK_TuningBuffer=0x8000][...]
▼
DSP: awe_modXisndDemomSet(MASK_XisndDemom_TuningBuffer)
→ _pif->ptr_FunTab->set_param(_pif, 0x16670004, ¶m)
六、验证架构(端到端可行性验证)
6.1 验证目标
验证链路: [source#1(20ch)] → [gain#1(20ch)] → [xisnd#1(20ch)]
验证点:
① 链路配置正确传递:前端 JSON → Binary Frame → DSP ModuleInstance
② Wire 规格正确解析:channels=20, sampleRate=48000, blockSize=240, dataType=fract32
③ 参数正确生效:gainDb[0]=-3dB → 输出信号幅值 = 0.707×输入
④ Xisnd 算法正确初始化:AlgoMem 分配成功,platform_init 返回 LIB_NO_ERROR
⑤ 实时处理通路:音频帧正确流经 gain → xisnd,输出无爆音
6.2 PC 仿真验证架构
┌──────────────────────────────────────────────────────────────────────────┐
│ PC 仿真验证环境 │
│ │
│ 前端 (http://localhost:5173) │
│ ├── LinkEditor: 拖拽构建 source → gain → xisnd 链路 │
│ ├── write_link → ws://localhost:5000/ws │
│ ├── GainTuningDialog: 调整 gainDb[0..19] │
│ └── XisndDemomDialog: 配置 vehicleCfg / algoChanNum │
│ 触发在线调音(TuningBuffer 写入) │
│ │ │
│ │ WebSocket │
│ ▼ │
│ 后端 (.NET8, localhost:5000) │
│ ├── ChainFlattener → FlattenedLinkConfig │
│ ├── BinaryFrameBuilder → set_link frame │
│ └── P/Invoke → DynamicChain.dll │
│ │ │
│ │ P/Invoke │
│ ▼ │
│ DynamicChain.dll (C 算法库) │
│ ├── DynChain_ParseLinkFrame() │
│ │ ├── ModuleRegistry_Lookup("channel_gain_v1") ← 已注册 │
│ │ ├── ModuleRegistry_Lookup("xisnddemom_v1") ← 需注册 │
│ │ └── 拓扑排序: [source#1=0] → [gain#1=1] → [xisnd#1=2] │
│ ├── DynChain_SetParam("xisnd#1", ...) ← 配置算法参数 │
│ ├── DynChain_Process(nbSamples=240) ← 音频处理 │
│ │ ├── Gain_Process(inputs[0]=source_out, ppOut, 20ch, 240) │
│ │ └── Xisnd_Process(inputs[0]=gain_out, ppOut, 20ch, 240) │
│ │ → _pif->ptr_FunTab->process(...) │
│ └── DynChain_GetMetrics() → JSON (cpuUs, latencyMs, memPool) │
│ │ │
│ NAudio WASAPI │ 音频 I/O │
│ 声卡输入 → DynChain_Process → 声卡输出 │
└──────────────────────────────────────────────────────────────────────────┘
6.3 验证步骤
Step 1: 注册 xisnddemom_v1 模块
─────────────────────────────────
// dynchain_module_registry.c
#include "ModXisndDemom.h"
static const ModuleFuncTable kXisndDemomFuncs = {
.getMemSize = XisndDemom_GetMemSize, // 返回 sizeof(awe_modXisndDemomInstance) + AlgoMem + TuningBuffer
.init = XisndDemom_Init, // 调用 awe_modXisndDemomConstructor 变体
.process = XisndDemom_Process, // 调用 awe_modXisndDemomProcess
.setParam = XisndDemom_SetParam, // 字符串路径(调试)
.getParam = XisndDemom_GetParam,
.setParamNum = XisndDemom_SetParamNum, // 数字路径(Binary 协议)
.getParamNum = XisndDemom_GetParamNum,
.getDefaultPorts = XisndDemom_GetDefaultPorts, // 返回 input(20ch,fract32) + output(20ch,fract32)
.destroy = XisndDemom_Destroy,
};
ModuleRegistry_Register(pReg,
MODULE_TYPE_XISNDDEMOM_V1, // 0x10070001
"xisnddemom_v1",
&kXisndDemomFuncs);
Step 2: 构建验证链路(前端)
─────────────────────────────────
拖拽: source#1 → gain#1 → xisnd#1
设置 gain#1.gainDb[0] = -3.0 dB
设置 xisnd#1.vehicleCfg = 0, algoChanNum = 20
Step 3: write_link 下发
─────────────────────────────────
前端 WebSocket → 后端解析 → Binary Frame v3
→ DynChain_ParseLinkFrame:
Module[0]: instanceId="gain#1", typeNumId=0x10010001, ports=[in(20ch), out(20ch)]
Module[1]: instanceId="xisnd#1", typeNumId=0x10070001, ports=[in(20ch,fract32), out(20ch,fract32)]
Connection[0]: from=0(output_port=1) → to=1(input_port=0), ch=20, sr=48000
Step 4: 参数验证
─────────────────────────────────
// 验证 Gain 参数
DynChain_GetParam(pChain, "gain#1", "gainDb#0", buf, 4, &written)
→ 期望: *((float*)buf) == -3.0f
// 验证 Xisnd 初始化
DynChain_SetParam(pChain, "xisnd#1", "initFull", &1.0f, 4)
→ awe_modXisndDemomSet(mask=0xFFFFFFFF)
→ platform_init() 返回 LIB_NO_ERROR (0x00000000)
Step 5: 音频处理验证(PC仿真)
─────────────────────────────────
// 注入已知信号(-3dBFS 正弦波)
float testInput[20][240]; // 20ch × 240 samples
生成 testInput[0][*] = 0.707f × sin(2π×1000×t/48000)
DynChain_Process(pChain, nbSamples=240)
// 验证 gain#1 输出: gainDb[0]=-3dB → 线性增益=0.707
// 期望 gain#1 output[0][*] ≈ 0.5f × sin(...)(-3dB×-3dB = -6dBFS)
// 通过 DynChain_SetCaptureHook 抓取 WAV 文件验证
DynChain_SetCaptureHook(pChain, "gain#1", "output", 100, "cap_gain_out", callback)
// 回调返回 WAV → 用 FFT 验证 1kHz 信号幅值误差 < 0.5dB
6.4 前端到 DSP 数据一致性验证矩阵
| 验证项 | 前端值 | 后端中间值 | DSP 实际值 | 验证方式 |
|---|---|---|---|---|
| 链路模块数 | nodes.length=2 | Modules.Count=2 | numInstances=2 | DynChain_GetMetrics |
| Wire 通道数(gain#1.input) | channels=20 | ResolvedPortSpec.Channels=20 | portCfg.ports[0].channels=20 | DynChain_GetParam("gain#1","channelCount") |
| 采样率(gain#1) | sampleRate=48000 | ResolvedPortSpec.SampleRate=48000 | portCfg.ports[0].sampleRate=48000 | DynChain_GetParam("gain#1","sampleRate") |
| Gain 参数(ch0) | gainDb[0]=-3.0 | ParamSetEntry{0x0101,0,-3.0} | g->params.gainDb[0]=-3.0f | DynChain_GetParam / WAV幅值验证 |
| Xisnd VehicleCfg | vehicleCfg=0 | ParamSetEntry{xisnd_vehicle,0,0} | S->VehicleCfg=0 | awe_modXisndDemomGet |
| Xisnd AlgoMem | memSizeAlgo=3000116 | initParam{words=3000116} | AlgoMem已分配 | platform_init返回值 |
| 连接关系 | gain#1.output→xisnd#1.input | Connections[0]{0,1,1,0} | connections[0]{0,1,1,0} | DynChain_Process输出抓取 |
| 拓扑顺序 | order: gain=1, xisnd=2 | 拓扑排序结果 | orderedIndices=[0,1] | DynChain_GetMetrics cpuUs顺序 |
七、前端 ↔ AWE 字段映射总表
| 前端字段路径 | AWE 对应字段 | C 结构体位置 | 说明 |
|---|---|---|---|
global.sampleRate |
_pif->ptr_Param->mFmtInput->smp_rate |
MediaFmt.smp_rate |
初始化时传入 |
global.blockSize |
_pif->ptr_Param->mFmtInput->num_smpPerCh |
MediaFmt.num_smpPerCh |
帧大小 |
ports[*].channels |
wireInfo1[9:0] / MediaFmt.num_chan |
WireInstance.wireInfo1 |
通道数 |
ports[*].blockSize |
wireInfo2[16:0] |
WireInstance.wireInfo2 |
当前帧大小 |
ports[*].dataType |
wireInfo2[22:17] / MediaFmt.fmt_stream |
WireInstance.wireInfo2 |
数据类型 |
ports[*].isComplex |
wireInfo1[27] |
WireInstance.wireInfo1 |
是否复数 |
ports[*].sampleSizeBytes |
wireInfo1[31:28] / MediaFmt.num_bytePerSmp |
WireInstance.wireInfo1 |
字节宽度 |
params.enable |
S->enable / _pif->ptr_Param->enable |
awe_modXisndDemomInstance.enable |
算法使能 |
params.vehicleCfg |
S->VehicleCfg / _pif->ptr_Param->vehicleCfg |
.VehicleCfg |
车型配置 |
params.algoChanNum |
S->AlgoChanNum / _pif->ptr_Param->AlgoChanNum |
.AlgoChanNum |
算法通道数 |
params.numOutChan |
S->NumOutChan |
.NumOutChan |
输出通道数 |
params.memSizeAlgo |
S->MemSize_Algo |
.MemSize_Algo |
算法内存(words) |
params.memSizeArch |
S->MemSize_Arch |
.MemSize_Arch |
架构内存(words) |
params.memSizeTune |
S->MemSize_Tune / _pif->ptr_Param->TunMemSize |
.MemSize_Tune |
调音内存(words) |
moduleState:'bypass' |
MODULE_BYPASS (0x01000000) |
ModuleInstanceDescriptor.packedFlags[25:24] |
Bypass 状态 |
edges[*].channels |
DynChainConnection.channels |
DynamicChain.connections[i] |
连线通道数 |
edges[*].sampleRate |
DynChainConnection.sampleRate |
DynamicChain.connections[i] |
连线采样率 |
nodes[*].enabled |
GainModuleData.params.enable |
模块 params 结构体 | 模块启停 |
八、待实现的关键组件
| 组件 | 位置 | 说明 | 优先级 |
|---|---|---|---|
XisndDemom_GetMemSize |
dsp/xisnddemom_module.c |
计算 sizeof(instance) + AlgoMem + TuningBuffer | P0 |
XisndDemom_Init |
dsp/xisnddemom_module.c |
调用 BaseClassModule_Constructor 变体 | P0 |
XisndDemom_GetDefaultPorts |
dsp/xisnddemom_module.c |
返回 in(20ch,fract32) + out(20ch,fract32) | P0 |
XisndDemom_SetParamNum |
dsp/xisnddemom_module.c |
映射数字 paramTypeId → MASK_XisndDemom_* | P0 |
MODULE_TYPE_XISNDDEMOM_V1 |
module_type_id.h |
数字类型ID 0x10070001 | P0 |
Backend: TypeNameToNumId |
Backend/ChainService.cs |
"xisnddemom_v1" → 0x10070001 | P0 |
Backend: XisndParamsToSetList |
Backend/ParamConverter.cs |
XisndModuleParams → List |
P1 |
Frontend: xisnddemom_v1 |
moduleLibrary.ts |
注册模块定义(ports/params schema) | P1 |
Frontend: TuningBuffer协议 |
XisndDemomTuningDialog.vue |
在线调音 read/write 协议封装 | P2 |
生成时间:2026-03-25