跳转至

系统数据模型与链路重构架构设计 (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, &param)

六、验证架构(端到端可行性验证)

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