> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SHAPE 概述

> XBOX One SHAPE 音频硬件如何通过固定功能模块、混音缓冲区和 DMA 加速用于 XMA、SRC 和滤波的流程图。

XBOX One 音频系统的一个关键组件是可扩展硬件音频处理引擎 (Scalable Hardware Audio Processing Engine, SHAPE)，其功能包括：

* 借助固定功能的硬件块高效执行常用音频功能：XMA、采样率转换器 (SRC)、均衡与压缩、滤波音量
* 提供可编程的嵌入式音频控制处理器 (Audio Control Processor, ACP) 来控制这些块

为实现高速运行并尽量减少主内存总线的流量，提供了以下机制。

* 用于访问系统内存的直接内存访问 (DMA) 过程
* 一种特殊的内部内存，称为 *混音缓冲区 (mix buffers)*

  混音缓冲区是一块内存，用于存储一个完整音频帧的数据。SHAPE 硬件通常从一个或两个混音缓冲区读取数据，并写入一个输出混音缓冲区。也有一些常规流程的例外情况，例如 XMA 解码以及 DMA 与 SRC 块。

许多游戏不会直接实现流程图。`XAudio2` 和音频中间件会隐式地使用 SHAPE 组件。不过，构建流程图能提供最大的灵活性，并让你直接使用 XBOX One 主机的音频加速能力。

本主题提供了 SHAPE 硬件及其功能块的概述。

* [控制流](#ID4EAB)
* [DMA](#ID4EWC)
* [XMA](#ID4EYD)
* [PCM](#ID4EFE)
* [采样率转换器 (SRC) 块](#ID4EOE)
* [均衡与压缩](#ID4ECF)
* [滤波/音量块 (FLTVOL)](#ID4EBH)
* [混音缓冲区](#ID4EYH)
* [溢出、幅值与饱和](#ID4ELAAC)
* [SHAPE 队列](#ID4EDDAC)
* [编程注意事项](#ID4EMDAC)

<a id="ID4EAB" />

## 控制流

以下是音频处理的典型控制流。

1. 将压缩后的 XMA 音频数据文件加载到主系统内存。

2. XMA 解码块将部分 XMA 数据解码为脉冲编码调制 (PCM) 数据，并将输出存储到系统内存中的 XMA 解码缓冲区。

3. SRC 块从 XMA 解码缓冲区读取 PCM 采样，并进行必要的采样率转换和音高变换。这样任意采样率的音频数据都可以进入 SHAPE 加速块。SRC 块以固定的 48 KHz 速率向内部混音缓冲区输出音频数据。

4. 接下来执行任何额外的 SHAPE 处理以及对临时混音缓冲区的读写。这些处理由 ACP 控制，而 ACP 又由应用通过本文档集中描述的 ACP API 驱动。处理内容包括均衡、压缩、滤波缩放和音量缩放。

5. 最后阶段称为 *扬声器输出累积 (Speaker Output Accumulation)*，扬声器混音缓冲区在这里收集并混合来自多个声源的采样以供播放。可选的全局音频效果也可在此阶段处理。

对于需要在主应用 CPU 上执行更任意处理的情况，可以采用另一种流程。前三步与前述完全相同。

1. 将压缩后的 XMA 音频数据文件加载到主系统内存。

2. XMA 解码块将部分 XMA 数据解码为 PCM 数据，并将输出存储到系统内存中的 XMA 解码缓冲区。

3. SRC 块从 XMA 解码缓冲区读取 PCM 采样，并进行必要的采样率转换和音高变换。这样任意采样率的音频数据都可以进入 SHAPE 加速块。SRC 块以固定的 48 KHz 速率向内部混音缓冲区输出音频数据。

4. 在这一步流程发生分叉。使用 DMA 系统将一个或多个混音缓冲区传输到主系统内存。

5. 应用 CPU 对主内存中的缓冲区执行所需的任何信号处理。

6. 使用 DMA 系统将处理后的音频数据从主内存缓冲区传回临时混音缓冲区。此后流程与前述相同。

7. 执行任何额外的 SHAPE 处理以及对临时混音缓冲区的读写。这些处理由 ACP 控制，而 ACP 又由应用通过本文档集中描述的 ACP API 驱动。处理内容包括均衡、压缩、滤波缩放和音量缩放。

8. 最后阶段——扬声器输出累积——扬声器混音缓冲区在此收集并混合多个声源的采样以供播放。可选的全局音频效果也可在此阶段处理。

各个 SHAPE 块由两大软件要素——上下文和执行列表——控制。上下文存储在主内存中，用于保存状态并为特定 SHAPE 硬件块中的处理单元提供控制。它们最初由 CPU 生成，但既可由 CPU 更新，也可由 SHAPE ACP 更新。上下文会在每个音频帧根据各硬件块的需要被读入 SHAPE 子系统。若音频帧大小为 128 个采样，采样率为 48 KHz，则每个音频通道的上下文交换速率为 375 Hz。

执行列表由 SHAPE 音频处理器处理的元命令组成。由于该处理器可编程且灵活，执行列表的格式和功能也非常灵活。一些元命令带有隐式数据，另一些则带有显式参数。这些元命令还指定各 SHAPE 硬件块通过硬件分配的混音缓冲区获取输入和写入输出的位置。发送给 SHAPE 硬件进行处理的命令列表称为 *流程图*，详见 [ACP 概述](/build/console-features/audio/overviews/acp-overview)。图 1 显示了四个 SHAPE 加速块及其与其他主要组件的交互关系。

**图 1. 四个 SHAPE 加速块及其与其他主要组件的交互。**

<img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/shape_blocks.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=a56a0204158197c6ac612eb7260c0f17" alt="四个 SHAPE 加速块" width="464" height="580" data-path="images/gdk/features/console/shape_blocks.png" />

<a id="ID4EWC" />

## DMA

直接内存访问 (Direct Memory Access, DMA) 系统支持读/写功能，并可自动进行浮点到整数以及整数到浮点的转换。DMA 系统使用循环缓冲区，其中包含以 128 个采样为一块的去交错样本。

DMA 处理器允许 SHAPE 引擎将数据从混音缓冲区送到主系统内存，也可以从主系统内存取回数据到混音缓冲区，以便进行进一步处理。DMA 按块进行传输，每次传输一个音频帧的数据。为便于块交错数据，跳过值 (skip value) 用于指定在读写采样时，从基地址开始应跳过的系统内存中音频帧块的数量。因此，多通道流每个通道都需要一个 DMA 上下文。

音频数据采样始终以 32 位数值形式读写，可以是整数或浮点数，具体由 DMA 上下文中的 `FloatConvert` 标志指定。为了减少数据带宽，DMA 的方向（读或写）在 DMA 命令中指定，而不是在 DMA 上下文中指定。DMA 引擎本身不做信号处理，但可以在浮点和整数之间进行转换。这样应用 CPU 就能以浮点格式处理数据采样。

要计算 DMA 缓冲区的大小，请使用以下公式。

```cpp theme={null}
fullBufferSizeInBytes = 128 * 4 * numChannels * numFrames  
```

读写指针按以下方式递增。

```cpp theme={null}
readPointer = (readPointer + 1) % numFrames  
```

```cpp theme={null}
writePointer = (writePointer + 1) % numFrames  
```

音频缓冲区内的地址按以下方式计算。

```cpp theme={null}
readAddress = audioBuffer + (128 * 4 * ((readPointer * numChannels) + channel))  
```

```cpp theme={null}
writeAddress = audioBuffer + (128 * 4 * ((writePointer * numChannels) + channel))  
```

例如，如果 DMA 缓冲区包含三帧和四个通道，该缓冲区的组织方式如图 2 所示，每个通道块包含 128 个连续采样。

**图 2. 一个 DMA 缓冲区。**

<img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/audio_dma_buffer.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=687279036fc31562a1e09b3e656214d4" alt="音频 DMA 缓冲区" width="900" height="113" data-path="images/gdk/features/console/audio_dma_buffer.png" />

由于 SHAPE 时钟独立于 CPU 时钟，延迟可能成为一个因素。硬件到软件与软件到硬件的切换会产生 2.66 ms 的延迟。当你同步关键代码并且对某些声音使用多次切换时，请考虑基于 CPU 时钟实现延迟，以同步声音渲染。此外，还可以考虑设计对延迟具有容忍度的子混音，以帮助降低 CPU 占用。

<a id="ID4EYD" />

## XMA

XMA 格式支持单声道、立体声和交错多声道声音，具有可变比特率和压缩率。相较 XBOX 360 上的 XMA 实现，SHAPE 版本做了多项改进，包括提高时钟速率（例如音高变换能力提升了 40%）以及将并发语音数从 320 增加到 512。

XMA 解码块解码主内存中的部分 XMA 数据，并将 PCM 数据返回到主内存中的 XMA 解码缓冲区。除了为与其他 SHAPE 组件对接而做的一些与状态相关的小改进外，XMA 解码块与 XBOX 360 的 XMA 解码块完全相同。

XMA 解码器寄存器块增加了与每条语音解码输出相关的状态信息。每个 XMA 上下文有 5 位，用于指定 PCM 输出缓冲区中尚未被消费的音频数据量。这些寄存器实现在 XMA 解码块内部。这样，就可以在不读取主内存的情况下随时确定是否有足够多的采样来锁定 SRC 的上下文。

<Note>SHAPE 不支持 xWMA，但可以通过 `XAudio2` 来支持。</Note>

<a id="ID4EFE" />

## PCM

线性脉冲编码调制 (PCM) 支持最高 7.1 环绕声（48 KHz）。如果用户的音响系统是 5.1 环绕声或立体声等，ACP 会进行下混。支持 S/PDIF 输出。

同时也支持包含 16 位单声道或立体声整数、32 位单声道浮点数或 32 位单声道整数（24 位左对齐，或 32 位低 8 位被屏蔽）的线性和环形 PCM 缓冲区。

<a id="ID4EOE" />

## 采样率转换器 (SRC) 块

SRC 块基于整数执行采样率转换，并将输出数据写入混音缓冲区。SRC 通常用于模拟乐器、将输入采样转换为源采样上下若干个八度的音，以及用于多普勒和其他效果。

SRC 块可从系统内存读取 32 位浮点 PCM 数据或 16 位、24 位、32 位定点数据。对于输入数据，该块既可以取用 XMA 硬件解码器的输出，也可以取用由软件维护的数据。

由于 SRC 块与 XMA 解码器紧密集成，它可以判断 XMA 解码缓冲区中的解码 PCM 数据采样数是否足以在不读取主内存的情况下生成并完成一整帧音频采样。

SRC 可在单声道或立体声模式下运行。PCM 数据从内存读取，并转换为统一的 24 位定点格式以便进行采样率转换。输出根据模式的不同写入一个或两个混音缓冲区。

立体声数据以 16 位交错流的形式存储在内存中。每个 32 位字的低 16 位是左声道，高 16 位是右声道。在立体声模式下，PCM 数据在读取时也会被同步去交错。左声道数据（第 0、2、4、6…个采样）被处理并输出到一个混音缓冲区，右声道数据（第 1、3、5、7…个采样）写入第二个混音缓冲区。图 3 展示了采样率转换模式。

**图 3. 采样率转换模式。**

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/console/src_modes.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=ef6fd2d2a8c90d8675a6ab62ec975a0a" alt="采样率转换模式" width="469" height="363" data-path="images/gdk/features/console/src_modes.png" />

SRC 块的输入可以是不超过 384 KHz 减去一个 epsilon 的任意采样率。但输出恒为 48 KHz。这里的 epsilon 指请求的整数值与硬件实际使用的浮点值之间的微小差值。

SHAPE 每个音频帧最多可处理 512 条 SRC 通道（单声道和立体声的任意组合）。

SRC 支持线性和多相插值、单声道和立体声，以及从 1:16（向下四个八度）到 3.99:1（几乎向上两个八度）的重采样范围。

<a id="ID4ECF" />

## 均衡与压缩

压缩器通过监视输入电平并生成动态增益值来限制信号的动态范围。然后压缩器将该增益值作为信号的乘数，根据输入信号当前电平对其进行相应的缩放。换句话说，压缩器就是一个不断监测输入信号并自我调整的自动音量控制。

压缩器只对超过特定阈值的信号起作用。低于阈值的信号会原样通过。阈值由应用作为 EQComp 上下文数据的一部分进行设置。上下文中一个可编程的控制位用于执行压缩或扩展。

压缩器根据一个指定输入电平与输出电平比值的参数来改变声音数据。该比值表示当输入信号超过阈值时对其进行衰减的程度。示例如下。

* 2:1 时，输入每超过阈值 2 dB，压缩器输出比阈值高 1 dB。
* 1:1 时，压缩器实际上处于关闭状态。

该比值由应用作为上下文数据的一部分进行设置。下图（图 4）展示了压缩器对输入信号的影响。低于阈值时，输出等于输入。高于阈值时，压缩器按比值指定的量降低输出——大约为 2:1。

**图 4. 输入/输出音量曲线。**

<img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_threshold.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=98e67769541f0ef0e66e633dad1c9207" alt="输入/输出音量曲线" width="433" height="309" data-path="images/gdk/features/console/eqcomp_threshold.png" />

`attack` 和 `release` 参数指定压缩器被激活的速度，也就是需要多长时间才能按 `ratio` 指定的量对超过阈值的声音完全衰减。`Attack` 和 `release` 以毫秒为单位，对应压缩器在信号超过或低于阈值时改变输出增益的速度。这些调整可以是线性的，也可以是对数的。

下图（图 5）显示，只有当输入超过阈值持续了由 `attack` 指定的时间后，才会达到你所期望的输出电平。图中还显示了输入和输出只有在输入低于阈值持续了 `release` 指定的时间后才恢复为单位增益。

**图 5. attack/release 的时间关系。**

<img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_attack.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=b657d17c473c879eaab036ab7ec48893" alt="attack/release 时间关系" width="459" height="233" data-path="images/gdk/features/console/eqcomp_attack.png" />

然后输入会乘以当前的输出增益。这第一步乘法完成了实际的压缩——即根据输入振幅通过时变函数对信号进行缩放。

最后还有一次额外的增益缩放。*补偿增益 (Makeup gain)* 是由应用设置的一个恒定值。由于压缩尤其在低阈值下可能得到较低增益的信号，因此使用补偿增益将信号拉回到可用范围。

压缩器的另一个参数是 `RMS`。在普通模式下，输入电平是瞬时的，每个采样都要计算。在 `RMS` 模式下，会保留前 128 个采样的移动平均（即 `RMS` 值的近似），并使用该移动平均作为输入电平，而不是当前输入采样的绝对电平。

EQ/压缩-扩展块 (EQ/Compressor-Expander) 在串联结构中包含两个独立的处理单元：一个三段可编程均衡器和一个动态范围压缩器（图 6）。该块用于控制声音的频率和动态范围，从一个或两个混音缓冲区获取输入。

* 一个输入是要处理的音频信号。
* 第二个输入称为 *sidechain*，是用于确定音频压缩的可选控制信号。

均衡器具有完全可编程的系数。软件可以完全控制均衡器的传递函数。

**图 6. 均衡器由三个串联的双二阶滤波器实现，分别记为 A、B、C。**

<img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_biquad.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=fe84848c225aa369e39865efa0d1b4a5" alt="三个双二阶滤波器" width="735" height="59" data-path="images/gdk/features/console/eqcomp_biquad.png" />

每个双二阶滤波器实现以下方程，其中 *x* 是输入、*y* 是输出，*a1*、*a2*、*b0*、*b1* 和 *b2* 是系数。这些系数可以在 [SHAPE\_EQCOMP\_CONTEXT](/reference/audio/shapeeqcompcontext/structs/shape_eqcomp_context) 结构中访问。

```
y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2] - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]  
```

通常对声音进行均衡时，会削减或提升某个频段。频率被削减（衰减）时不会出现问题：输出信号的电平会等于或低于输入。但频率被提升时，通常会引起信号峰-峰动态范围的相应增大。例如将低频提升 6 dB，会使输出增益高于输入。如果输入信号本身已是满刻度 (-0 dBFS)，输出必然会超过最大值。

正因如此，三级级联的双二阶 EQ 部分在动态范围内在二进制小数点左侧多保留 8 位精度，使 s.23 输入格式在 EQ 处理时变为 s8.23。这一额外范围允许每个 EQ 级在多数情况下提供增益提升而不会使音频输出饱和。但在最大输入条件下，如果三级共施加 +18 dB 的最大增益，第三级仍可能饱和，此时硬件会检测到峰值溢出事件。

每个双二阶滤波器的滤波系数 (b0、b1、b2、a1、a2) 是 24 位整数。这允许的系数范围高达 +/- 7.998，足以为所允许的滤波器类型提供合适的系数，同时支持 20 Hz 到 18 KHz 的频率范围以及 -18 dB 到 18 dB 的增益范围。

压缩-扩展器可以运行在以下三种 sidechain 模式之一。

* 正常模式下（图 7），只有一路来自混音缓冲区的音频输入。输入经过均衡器，随后由压缩器处理并输出到一个混音缓冲区。

  **图 7. 正常模式。**

  <img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_normal.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=be6088e111b99afb2055ec8f6bb00c37" alt="正常模式" width="735" height="144" data-path="images/gdk/features/console/eqcomp_normal.png" />

* 内部模式下（图 8），输入信号直接送入压缩器的音频输入端，并被分出一路送入均衡器。从均衡器出来后，再送入压缩器的 sidechain 输入。

  **图 8. 内部模式。**

  <img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_internal.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=ed49c3cd88dc5fee2317e873e819fe1d" alt="内部模式" width="735" height="137" data-path="images/gdk/features/console/eqcomp_internal.png" />

* 外部模式下（图 9），使用来自两个独立混音缓冲区的两路独立输入信号。输入信号先经均衡器，再进入压缩块的音频输入端。sidechain 输入信号则直接送入压缩器的 sidechain 输入端。

  **图 9. 外部模式。**

  <img src="https://mintcdn.com/microsoft-4404708b/CwRBzaXvHw9zaPoe/images/gdk/features/console/eqcomp_external.png?fit=max&auto=format&n=CwRBzaXvHw9zaPoe&q=85&s=cf67a63a45209acd9f40730df9fc9ba2" alt="外部模式" width="735" height="146" data-path="images/gdk/features/console/eqcomp_external.png" />

SHAPE 每个音频帧最多可处理 512 个 EQCOMP 上下文。

<a id="ID4EBH" />

## 滤波/音量块 (FLTVOL)

FLTVOL 用于：

* 模拟声音绕过或穿过物体时的遮挡效果

* 将声音能量分配到多个扬声器，以模拟声音抵达的方向。

FLTVOL 从一个混音缓冲区读取单声道输入数据，对其进行滤波和音量缩放，然后将输出写入一个混音缓冲区。通常会用多个滤波/音量控件生成"一进多出"的声像效果，例如环绕声声像。声像器常用于对一个声音执行 *n* 声道声像以及一个或多个混响效果。借助状态变量滤波器 (State Variable filter)，游戏可以轻松制作增强的距离效果以及 I3DL2 风格的遮挡与阻挡效果。

由于其固有特性，状态变量滤波器可能产生使中间值超出 1.0 或 -1.0 的谐振。为减轻中间值饱和带来的问题，FLTVOL 会对输入信号应用可编程的余量缩放 (headroom scaling)，并在输出时进行补偿。该缩放通过对数据进行一定位数的算术右移实现。上下文中的 headroom 字段指定了 FLTVOL 处理之前对输入信号进行右移的位数（0、1、2 或 3）。例如，若指定 headroom 值为 *3*，状态变量滤波器就可以在内部谐振和溢出方面额外保留 3 位（18 dB）的余量。

将输入数据右移必然会降低低端的精度。输入音频信号中低位的 headroom 位会永久丢失。因此，仅当状态变量滤波器内部发生饱和时，才应将 headroom 位设为非零。为此可检查 FLTVOL 上下文中 Internal Overflow 位的值。如果原始源数据是 16 位（例如源自 XMA），通常可以将 headroom 位设为最大值 *3* 且几乎察觉不到音质变化，因为低位本来就填零。

灵活的状态变量滤波器实现同样允许对语音施加谐振滤波，以创作有趣的音效和变化。为确保入口与出口设定点之间的平滑过渡，滤波器参数与音量属性在每个采样上都逐步调整。FLTVOL 块以 Chamberlin 滤波器实现，提供高通、低通和带通三种模式，并带有可变的 Q 值（带宽）控制。Chamberlin 滤波器的参数按以下方式计算：

```
f := 2*Sin( ( PI * FC )/FS)  

q := 1/Q, where Q ranges from .5 to 5  
```

`f` 和 `q` 参数在 SHAPE 块之外由软件计算。当一个系数被更新时，会在一个音频帧的过程中平滑过渡到新值。控制位用于确定 FLTVOL 块最终输出使用哪种输出（带阻、高通、带通或低通）。

每个通道或流的上下文数据存储在系统内存中。虽然性能上限是同时 2560 条 48 KHz 流（意味着 SHAPE 每个音频帧最多可处理 2560 个 FLTVOL 上下文），但内存中可寻址的上下文数量更大，以便简化流复用场景。

FLTVOL 的滤波行为被设计得与 `XAudio2` 完全一致。

<a id="ID4EYH" />

## 混音缓冲区

混音缓冲区主要有三种用途。

* 混音缓冲区作为系统（或某个玩家）每个扬声器输出的最终混音目的地。每种声音被处理后，其后续输出会混入这些缓冲区。

* 混音缓冲区在音频数据缓冲在硬件块之间传递时充当临时存储位置。

* 结合 DMA 引擎，混音缓冲区可以作为将数据从 SHAPE 硬件和音频子系统向上传递到主系统的机制，同时也可将主系统内存中的数据传回 SHAPE 硬件和音频子系统。

SHAPE 系统广泛使用混音缓冲区。最多可同时存在 8192 个虚拟混音缓冲区（ID 从 0 到 8191），渲染到 128 个物理通道上。混音可通过硬件累加器完成，无需与内存之间的 DMA。混音缓冲区支持计量和削波检测。

<a id="ID4ELAAC" />

## 溢出、幅值与饱和

在游戏音频引擎中，管理动态余量可能非常困难。因此每个 SHAPE 块都会维护与信号余量和增益相关的状态。除 SRC 之外，每个块都会维护一个标志，指示处理过程中是否发生了内部饱和。

除内部饱和标志外，每个硬件块还维护两个 4 位数值，用于表示该硬件块输出的幅值。幅值的计算方式是先确定音频帧内的峰值输出，然后统计峰值绝对值的前导零个数。其中一个 4 位数存储持续的峰值幅值，另一个则由硬件在每个音频帧上重置。

除了监视每个硬件块的峰值幅值外，还会维护混音缓冲区的峰值幅值。当硬件块的输出被加入混音缓冲区时，会以相同方式计算峰值幅值——统计前导零个数。每个硬件块的状态额外维护一对 4 位的峰值幅值。这些值表示硬件块输出累加到混音缓冲区之后，混音缓冲区的峰值幅值。其中一个 4 位数表示持续的、进行中的峰值幅值，另一个则每个音频帧都会更新。虽然它们表示的是混音缓冲区的状态，但这些峰值幅值仍保存在 SHAPE 块的上下文中。

峰值幅值按下表编码（在 ShapeHardwareContexts.h 文件中定义）。

| 二进制值            | 描述                                                                                                                                |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| B'0000          | 饱和：某次计算的输出值溢出了带符号 24 位范围，并被替换为最近的极值。具体来说，大于 8,388,607 (0x7F\_FFFF) 的值被替换为 8,388,607；小于 -8,388,608 (0x80\_0000) 的值被替换为 -8,388,608。 |
| B'0001          | 峰值绝对值大于 MAX >> 1 (0x3F\_FFFF)。                                                                                                    |
| B'0010          | 峰值绝对值大于 MAX >> 2 (0x1F\_FFFF)。                                                                                                    |
| B'0011          | 峰值绝对值大于 MAX >> 3。                                                                                                                 |
| B'0100          | 峰值绝对值大于 MAX >> 4。                                                                                                                 |
| B'0101          | 峰值绝对值大于 MAX >> 5。                                                                                                                 |
| B'0110 到 B'1101 | 与前述情形类似，峰值绝对值分别大于 MAX >> 6 到 13。                                                                                                  |
| B'1110          | 峰值绝对值大于 MAX >> 14。                                                                                                                |
| B'1111          | 峰值绝对值小于或等于 MAX >> 14。                                                                                                             |

为帮助管理动态余量、避免溢出和饱和，除了细粒度的增益设置之外，还可在硬件块输出累加到输出混音缓冲区之前对其执行 0 到 7 位的右移。这在每个 SHAPE 硬件块的上下文中指定。

<a id="ID4EDDAC" />

## SHAPE 队列

SHAPE 音频处理器维护两个队列，以帮助减少系统硬件与应用硬件之间的互锁。

1. 命令与控制队列 (Command and Control Queue, CCQ) 用于向 SHAPE 音频处理器发送一系列命令，让其在有空时执行——通常是在完成当前音频帧的处理之后。SHAPE 执行列表在每个输出音频帧读取一次；而 CCQ 只读取并执行一次。

   CCQ 提供了一种机制，使 CPU 能够在音频帧边界更新任何块的上下文数据，且无需与 SHAPE 硬件进行硬件互锁。示例包括为 XMA 块提供更多比特流数据、更新各个块的上下文参数以及指向新的执行列表。这通常在音频处理流程图发生变化时进行。

2. 状态与报告队列 (Status and Reporting Queue, SRQ) 由 SHAPE 音频处理器用于向 CPU 报告各种不需要实时响应的事件。示例包括比特流缓冲区消费情况的更新、错误、警告和标志、调试数据、性能数据以及状态信息。

<a id="ID4EMDAC" />

## 编程注意事项

可以通过 `IACPHAL` 接口直接对 SHAPE 硬件编程。但为 SHAPE 硬件准备音频数据的过程比较复杂。为此提供了许多工具方法、结构和枚举。这些工具提供了控制 SHAPE 硬件所需的绝大多数（或全部）方法。Microsoft Game Development Kit (GDK) 提供了这些工具的源代码，以便在处理某些特殊数据时进行必要的修改。

在你的项目中唯一需要显式包含的头文件是 *acphal.h*，它引用了所有的工具头文件。所有工具函数都引用 `NO_SHAPE_CONTEXT_VALIDATION` 宏。如果定义了该宏，所有的校验都会被省略。这对最终零售构建很有用。

有关与 `XAudio2` 实例共享 SHAPE 和 XMA 资源的更多细节，请参见 [XAudio2Create](https://learn.microsoft.com/windows/desktop/api/xaudio2/nf-xaudio2-xaudio2create) 函数的备注部分。

有关所有工具方法的更多详细信息，请参见 [ACP 概述](/build/console-features/audio/overviews/acp-overview)。

<a id="ID4EDEAC" />

### 持久与非持久流程图

你可以将流程图作为持久或非持久提交处理。持久流程图会被处理并保持驻留，以便在读写指针推进以及其他上下文信息更新后于下一帧重复其处理。以下任一场景应使用持久流程图。

* 高度复杂的流程图，需要大量 CPU 处理才能每帧重新组装
* 高度静态的流程图，其语音数量与配置在较长时间内保持不变

以下任一场景应使用非持久流程图。

* 语音拓扑经常变化。
* 你的游戏音频软件引擎的处理节拍与 SHAPE 硬件解耦；即不是 2.667 ms 的整数倍。
* 你希望以快于实时的方式运行音频处理，即一提交流程图就尽快消费。

<a id="ID4E6EAC" />

### 问题调试

开发流程图时，注册以接收消息有助于调试可能发生的问题。具体做法是使用 [Connect](/reference/audio/acphal/interfaces/IAcpHal/methods/iacphal_connect) 的 `NumMessages` 参数。该消息系统会针对各种问题提供丰富反馈，包括非法流程图 (`ACP_FLOWGRAPH_TERMINATED_REASON_INVALID_GRAPH`)、被阻塞的命令，以及由于尝试执行超过硬件 2.667 ms 帧尺寸的处理而导致的超帧。发布游戏之前，请通过观察每个已提交流程图的 `ACP_MESSAGE_TYPE_FLOWGRAPH_COMPLETED` 消息，验证流程图处理始终成功。

* [丢失的消息](#ID4ENFAC)
* [超帧](#ID4E1FAC)
* [被阻塞的命令](#ID4EBGAC)
* [同步问题](#ID4ENIAC)

<a id="ID4ENFAC" />

#### 丢失的消息

如果你使用 ACP 消息来驱动引擎状态，并且需要处理大量消息，那么在开发期间应检查 [ACP\_MESSAGE](/reference/audio/acphal/structs/acp_message) 的 `droppedMessageCount` 字段。该字段的非零值表示消息队列已满，不得不丢弃消息。此时请考虑加快对队列的处理并加大队列容量。

<a id="ID4E1FAC" />

#### 超帧

流程图可能因多种原因而无法在音频帧结束（2.667 ms）前完成，例如处理的流程图过多或流程图结构不佳。对于持久流程图，这类未完成的流程图会产生 `ACP_FLOWGRAPH_TERMINATED_TIME_EXCEEDED` 消息，且这些持久流程图将处于未完成状态。相比之下，非持久流程图不受此限制，会一直运行直至完成，甚至跨越多帧。

<a id="ID4EBGAC" />

#### 被阻塞的命令

SRC 和 DMA 命令可能在多种场景中被报告为阻塞 (`ACP_MESSAGE_TYPE_SRC_BLOCKED`、`ACP_MESSAGE_TYPE_DMA_BLOCKED`)，如下表所示。

\| 命令类型| 阻塞场景|
\| --- | --- | --- | --- |
\| XMA SRC| 关联的 XMA 上下文存在解析错误或没有源数据。错误为 `SHAPE_XMA_ERROR_STATUS_READ_BUFFER_INVALID_VALIDBUFFER_CURRBUF_IS_0` | `SHAPE_XMA_ERROR_STATUS_FRAME_CROSSES_BOUNDARY_INTO_INVALID_READ_BUFFER_VALIDBUFFER_CURRBUF_IS_0` | `SHAPE_XMA_ERROR_STATUS_FRAME_CROSSES_BOTH_READ_BUFFER_BOUNDARIES`。XMA SRC 不会因为解码数据不足而阻塞。|
\| PCM SRC| 关联的 PCM 上下文为 `SHAPE_PCM_MODE_CIRCULAR`，且没有源数据。|
\| 读取（从混音缓冲区做 DMA）| DMA 缓冲区已满。|
\| 写入（向混音缓冲区做 DMA）| DMA 缓冲区为空。|

一旦被判定为阻塞——这可能发生在命令被加入 SHAPE 队列之前，也可能发生在运行时——它们会被从图中移除，可能导致音频丢失。在开发期间，应把被阻塞的命令作为改进流程图处理的首要着眼点。发布前，还要确保你的游戏完全避免命令被阻塞。

你可以通过确保音频缓冲区足够大，并按缓冲区大小以规律间隔流入数据，来避免 DMA 命令被阻塞。例如，如果你一次流送四个硬件帧，请确保缓冲区中始终有若干倍（至少双缓冲即八帧），以便硬件可以在游戏读指针之前提前写入。也可以通过在提交下一个流程图之前排空缓冲区：先消费、更新 DMA 读指针，再提交流程图。

SRC 命令有三种模式。

* `SHAPE_SRC_COMMAND_TYPE_START` 用于流程图中几乎所有 SRC 命令。按正常处理，并预期当前流程图之后还会有更多音频数据。
* `SHAPE_SRC_COMMAND_TYPE_STOP_IMMEDIATE` 用于立即停止对源 XMA 或 PCM 数据的处理。SRC 会在这一帧输出全零缓冲区。
* `SHAPE_SRC_COMMAND_TYPE_STOP_END` 用于指示一条语音的最后一个数据包。

如果一条语音的最后一个数据包未以 `STOP_END` 或 `STOP_IMMEDIATE` 提交，SRC 命令将不会完成，从而使活动流程图停滞。持久流程图会在音频帧末尾终止，而非持久流程图将永远无法完成。

<a id="ID4ENIAC" />

#### 同步问题

如果你不遵守 SHAPE 硬件对上下文和命令的消费规范，可能出现同步问题。虽然你的游戏拥有对 ACP 分配内存的完全访问权，但要注意在上下文结构正在被使用时不要修改它。你可以使用 [SubmitCommand](/reference/audio/acphal/interfaces/IAcpHal/methods/iacphal_submitcommand) 提交命令，让其在特定帧、下一帧开始时或尽快执行。

<Note>"尽快"这一情形相对于任何游戏 CPU 处理来说仍然是异步的。有些命令可能不会立即完成，例如某个上下文正处于不可中断的操作中。请等待 `ACP_MESSAGE_TYPE_COMMAND_COMPLETED` 以确认命令确实已处理完毕。</Note>

有关更多信息，请参见 [深入 SHAPE：构建音频流程图的最佳实践](/build/console-features/audio/overviews/best-practices-audio-flowgraph-construction)。


## Related topics

- [概述](/zh-CN/build/console-features/audio/overviews/index.md)
- [SHAPE_PCM_FORMAT](/zh-CN/reference/audio/shapepcmcontext/enums/shape_pcm_format.md)
- [SHAPE_PCM_MODE](/zh-CN/reference/audio/shapepcmcontext/enums/shape_pcm_mode.md)
- [SHAPE_PCM_CONTEXT](/zh-CN/reference/audio/shapepcmcontext/structs/shape_pcm_context.md)
- [SHAPE_FLOWGRAPH_FILTVOL_COMMAND](/zh-CN/reference/audio/shapeflowgraph/structs/shape_flowgraph_filtvol_command.md)
