跳转到主要内容
本主题介绍用于指挥音频控制处理器 (Audio Control Processor, ACP) 的流程图,并给出相关示例。

ACP 概述

本节介绍 ACP 的编程接口。 acphal 库(acphal.lib)为可扩展硬件音频处理引擎 (Scalable Hardware Audio Processing Engine, SHAPE) 定义了一整套 API。Microsoft Game Development Kit (GDK) 中还包含一系列音频工具和源代码,可帮助你为 SHAPE 准备音频数据。这些工具头文件定义了以下内容:
  • 每种受支持数据格式对应的上下文结构
  • 一整套可用于读写这些上下文的函数
在你的应用中至少定义一个流程图,用于将音频数据 通过 SHAPE 块进行路由。ACP 会管理 SHAPE 块并确保其运行高效。 有关该 API 集合的详细信息(包括在工具头文件中声明的结构和枚举),请参见 AcpHal 参考。 有关 SHAPE 架构的详细信息,请参见 SHAPE 概述。 在游戏项目的源文件中,务必包含 ShapeContext.h 文件,而不是各个单独的上下文头文件。 本主题内容:

流程图

要使用 SHAPE,游戏通常会创建一个 SHAPE 流程图。有关替代方式的说明,请参见 XMA 工具 一节。SHAPE 流程图是一组命令(每个 SHAPE 块对应一条)以及配套的上下文数据,用于描述各个块的执行顺序及其操作的数据。ACP 使用流程图中的数据来正确调度 SHAPE 块内的各项操作。 游戏负责构建流程图并将其提交给 ACP。要构建流程图,请使用 ShapeFlowGraph(流程图工具方法)。 在下面的图中,绿色方块表示 SHAPE 组件,青色方块是源数据,标有标签的黄色圆圈是硬件混音缓冲区。

3D 声音

图 1. 两条语音,每条在两个输出之间做声像,并向一个共同输出发送。 两条语音的流程图 在代码中,此流程图可表示如下。

软件音频引擎的前端

图 2. 一个软件引擎的基本前端。使用该模型的所有语音都有可能采用相同的结构。 软件音频引擎的基本前端 在代码中,此流程图可表示如下。

渲染音频

要渲染音频
  1. 创建一个类似上例的流程图,用于定义要处理的音频图。
  2. 创建上下文结构,用于定义流程图如何以及处理什么内容。
  3. 使用 SubmitCommand 方法将流程图提交给 ACP。根据传给 SubmitCommand 的参数,流程图可以只被处理一次然后丢弃,也可以每个音频帧都被处理。
  4. 游戏负责在每个音频帧内使用 ACP_COMMAND_UPDATE_*_CONTEXT 命令更新上下文数据,或通过与流程图处理正确同步来手动更新上下文。 在更新上下文时,请注意 SubmitCommand 的标志参数会决定更新是尽快发生还是在下一个音频帧发生。
有关手动同步上下文更新的方法的详细信息,请参考 ACP_COMMAND_LOAD_SHAPE_FLOWGRAPH 命令。一般来说,如果每个音频帧只更新少量上下文,可以使用 ACP_COMMAND_TYPE_UPDATE_*_CONTEXT 命令。用这些命令更新大量上下文并不高效,因为除了处理命令本身之外,还必须将大量的上下文数据复制并传输到 ACP。如果你想更新大量上下文,游戏应修改上下文数据,然后向 ACP 提交非持久流程图,或者充分利用 ACP_COMMAND_TYPE_START_FLOWGRAPH 命令以及 ACP_COMMAND_LOAD_SHAPE_FLOWGRAPH 命令中的 waitForStart 参数,在上下文更新完成前暂缓处理。当流程图完成处理后,上下文即可被再次更新。 更新上下文的第三种替代方案是使用双缓冲。游戏可以在流程图处理期间更新第二份上下文副本,然后在音频帧开始时交换上下文。

流程图解析

ACP 会在音频帧末尾终止持久流程图的解析。ACP 的音频帧限制为 2.667 ms。如果游戏的流程图处理需要 75% 的音频帧时间,但游戏直到已经过了 30% 的音频帧才允许处理开始,那么未被解析的部分将被清除。流程图本身不会被改动。发生这种情况时,如果游戏已注册接收消息,ACP 会发送 ACP_MESSAGE_TYPE_FLOWGRAPH_TERMINATED 消息。要确定哪些命令未被插入到内部 SHAPE 队列中,游戏可以检查流程图命令的 queued 标志。 非持久流程图通常不论提交时间点如何都会被处理到完成。一个例外情况是,由于源数据不可用或 disabled 标志使用不当而使一个或多个命令被阻塞。游戏可以在 ACP 音频帧的任意时刻提交一个非持久流程图,并可确信(少数边界情况除外)它会完成处理。好处是游戏不必与音频时钟完全同步,只要仍在 ACP 音频帧的 2.667 ms 间隔内为流程图服务即可。风险在于,如果游戏没有一致地提交流程图,或流程图需要超过 2.667 ms,就可能导致音频卡顿。 以下是流程图生命周期的总结。
  • 持久流程图会一直在 ACP 上处于活动状态,直到被新的流程图替换或被空流程图替换(后者实际上会将其移除)。
  • 除非客户端指示 ACP 等待开始命令,否则 ACP 会在音频帧开始时开始处理持久流程图。
  • 即便未处理完毕,ACP 也会在音频帧结束前不久停止对持久流程图的处理。
  • 非持久流程图仅在其完成之前处于活动状态,完成后即被移除。
  • 非持久流程图可以在多个音频帧之间保持活动。只有在其完成后才会被移除。
命令处理与流程图解析并不绑定。ACP 会持续扫描新命令,并在解析流程图或执行其他工作的同时尽快处理它们。

更新流程图

更新流程图的选项与前述更新上下文的选项类似。基本原则相同:不要在流程图正在被处理时更新它。 你可以使用三种策略更新流程图。
  1. 使用非持久流程图,按需重新构建它们,并在音频帧开始时提交。游戏可以按需多次复用同一个流程图。如果流程图没有变化,游戏就不需要重建。你也可以通过在旧流程图正在处理时构建新的流程图来对这种方式做双缓冲。在重建时,流程图不仅可以从头构建,其各部分也可以被存储,然后通过适当设置混音缓冲区 ID 按需链接起来。
  2. 使用持久流程图,仅在需要变化时才重建并替换它们。此处双缓冲同样适用。可以通过不使用 ACP_COMMAND_LOAD_SHAPE_FLOWGRAPH 的 waitForStart 参数,让 ACP 自由运行。这样 ACP 会在音频帧开始并处理完标记给该帧的命令后,立即开始处理流程图。或者,也可以设置 waitForStart 参数,直到发送 ACP_COMMAND_TYPE_START_FLOWGRAPH 命令后流程图才被处理。这并不直接影响流程图更新,但可以让同步略微更精确。
  3. 使用流程图命令上的 disabled 标志来禁用流程图的部分内容。例如,可以构建一个主流程图,但每次运行时仅启用所需的部分。这必须与选项 1 或 2 配合使用,以便让新的流程图到达 ACP。请注意,ACP 目前不会遍历流程图去查找孤立的块。游戏必须禁用流程图中的完整路径(而不仅仅是首个节点)。若不这样做,SHAPE 命令会暂时使硬件停顿。这些停顿有可能只是轻微的,例如一个无法处理的单一块被以较小的代价移除;也可能严重到让整条语音停顿。
对于语音数量较大或每个音频帧更新次数较多的游戏,请使用双缓冲技术。 如果游戏使用 ACP_MESSAGE_TYPE_FLOWGRAPH_COMPLETED 来管理更新,从 ACP 将消息加入消息队列,到游戏调用 PopMessage 之间,ACP 可能会长时间处于空闲状态。不要用这种方式来管理更新——请让 ACP 保持活动。

多个流程图

如果多个流程图的总需求不超过硬件能力,游戏可以在每个音频帧内处理多个流程图。这样做的好处是游戏可以同时运行多个音频引擎(中间件或自定义),或将解析拆分为更可控的块。不利之处是 SHAPE 硬件在此模式下运行效率不如单流程图。要达到 SHAPE 的最大吞吐量,每个 SHAPE 块都必须 100% 保持繁忙,而在处理多个流程图时这是不可能的。 每个 ACP 客户端只能加载一个流程图。如果客户端提交了新的流程图,它会替换掉现有的。需要支持多个流程图的游戏,要么为每种流程图类型分别拥有独立的客户端(每个客户端有自己的命令与消息队列),要么在提交新流程图前等待现有流程图完成。 支持多个流程图的设计初衷是供多个客户端使用——而不是单个客户端。这样,中间件引擎可以提交它自己的流程图,游戏也可以为额外的自定义处理提交单独的流程图。为游戏所需的每个 ACP 客户端,都需要创建一个 IACPHAL 接口实例。 由于所有 ACP 与 SHAPE 资源在所有客户端之间共享(尤其是上下文数组),客户端之间必须协调这些资源的分配与共享。

DMA 工具

DMA(直接内存访问)工具包含在 ShapeDMAContext.h 文件中。以下工具作用于 SHAPE_DMA_CONTEXT 结构。有关更多信息,请参见 ShapeDmaContext (DMA 工具方法)。

EQ 压缩器工具

EQCOMP 工具包含在 ShapeEqCompContext.h 文件中。这些工具作用于 SHAPE_EQCOMP_CONTEXT 结构。 有关更多信息,请参见 ShapeEqCompContext (EQCOMP 工具方法)。

滤波音量工具

滤波音量工具包含在 ShapeFiltVolContext.h 文件中。这些工具作用于 SHAPE_FILTVOL_CONTEXT 结构。 有关更多信息,请参见 ShapeFiltVolContext (FLTVOL 工具方法)。

PCM 工具

PCM(脉冲编码调制,Pulse Code Modulation)工具包含在 ShapePCMContext.h 文件中。这些工具作用于 SHAPE_PCM_CONTEXT 结构。 有关更多信息,请参见 ShapePcmContext (PCM 工具方法)。

SRC 工具

SRC(采样率转换器,Sample Rate Convertor)工具包含在 ShapeSRCContext.h 文件中。这些工具作用于 SHAPE_SRC_CONTEXT 结构。 有关更多信息,请参见 ShapeSrcContext (SRC 工具方法)。

XMA 工具

XMA 工具包含在 ShapeXMAContext.h 文件中。这些工具作用于 SHAPE_XMA_CONTEXT 结构。 有关更多信息,请参见 ShapeXmaContext (XMA 工具方法)。 硬件模拟环境下的 XMA 解码能力有限。详情请参见 SHAPE_XMA_CONTEXT 结构主题。 游戏可以在不使用流程图的情况下使用 XMA 数据,但数据仍必须通过 ACP 使用 ACP_COMMAND_TYPE 命令传递,如下表所示。 使用这些命令,游戏可以按以下操作顺序,几乎与 XBOX 360 XMA HAL 相同的方式使用 XBOX One ACP HAL。
  1. 用相关数据(缓冲区、偏移量等)填充一个或多个上下文。
  2. 启用这些上下文。
  3. 更新上下文。如果上下文处于禁用状态,游戏可以直接修改其内容。如果上下文已启用,游戏可以先将其禁用,或者使用 ACP_COMMAND_TYPE_UPDATE_XMA_CONTEXT 命令——后者可能更高效,因为由 ACP 来处理禁用、等待和更新。
一般来说,游戏不应仅使用 SHAPE 的 XMA 组件。创建一个只需少量维护的”前端”流程图几乎是轻而易举的事,它能免费为游戏带来高质量的 SRC 和其他功能。流程图易于创建与管理,还能将 SRC 从主 CPU 卸载并提升质量。

目标值

工具函数中可以设置的目标值代表参数在音频帧末尾时的最终值。例如,SHAPE_FILTVOL_CONTEXT 结构包含 gain 和 gainTarget 值。在处理该帧的过程中,gain 通过线性插值按下式计算:
其中 i 从 0 到 127。 在帧末尾,gain 会等于 gainTarget。 可以为 ACP 设置的目标参数如下表所示。

线程安全

IACPHAL 接口方法 和 ACPHAL 方法 是线程安全的。如果调用了 ApuCreateHeap,其分配的堆将被所有线程共同使用。 以下工具函数 不是 线程安全的。不过它们的源代码是随附提供的。必要时你可以自行将其改造为线程安全。常见做法是使用 关键段对象 (Critical Section Objects)。

对 PCM 与 XMA 数据使用 SRC 的指南

下表列出了将 SRC 块用于 PCM 和 XMA 数据的指南。
上述任一场景完成时,SRC 命令都为 SHAPE_SRC_COMMAND_TYPE_STOP_IMMEDIATE。
请以处理 SHAPE 流程图相同的频率检查 XMA 输入缓冲区的有效位。如果你把此检查作为流送逻辑(其执行间隔要长得多)的一部分来做,就可能在你告知 SRC 在末尾停止之前,XMA 输出缓冲区已经清空。这会导致流程图停滞。

游戏的暂停与恢复

游戏必须能够暂停和恢复,例如当用户将其切换到 Constrained 模式时。在使用 IAcpHal 直接为 SHAPE 硬件编码时,若要暂停和恢复,只需让游戏停止提交命令即可。已经提交的命令将正常完成,并可能填满消息队列。 如果游戏使用持久流程图,则应加载一个空流程图以停止处理。这与使用 XAudio2 编码时的暂停与恢复流程不同。有关更多信息,请参见 XAudio2 概述。

参考 API 文档

最后修改于 2026年10月5日