> ## 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.

# 使用 XDSP API 进行单流 FFT/IFFT 概述

> 使用 XDSP API 进行单流 FFT/IFFT 概述

在本主题中，我们将通过 Microsoft Game Development Kit (GDK) 附带的一个示例，逐步介绍在 XBOX Series X 设备上使用硬件进行 FFT/IFFT 的预期代码流程。

## 硬件连接与启动

必须通过 [XDspConnect](/reference/audio/xdspaudio/functions/xdspconnect) 建立与硬件加速单元的连接。调用者使用以下参数设置连接的要求：

1. *baseBuffer* - 指向用户分配的一块内存，其大小足以容纳将传给硬件的输入、输出和/或块乘子缓冲区。该内存应通过 XMemAlloc 分配，属性为：*XALLOC\_MEMTYPE\_PHYSICAL\_CACHEABLE*、*XALLOC\_PAGESIZE\_64KB*、*XALLOC\_ALIGNMENT\_64K*。这些属性应通过 `MAKE_XALLOC_ATTRIBUTES()` 设置，示例见下文。该内存在 [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) 调用成功之前不能释放。此缓冲区至少 64KB。

2. *baseBufferLength* - *baseBuffer* 的长度（字节数）。此缓冲区至少 64KB。

3. *aggregateImpulseResponseInSeconds* - 表示将通过 [XDspActivate](/reference/audio/xdspaudio/functions/xdspactivate) 与 [XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype) 同时激活的所有单独脉冲响应的总时长。如果此值为 0，则不能激活任何 [XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype) 类型的流。

4. handle - 指向 [XDspClientHandle](/reference/audio/xdspaudio/handles/xdspclienthandle) 的指针，用于接收本次调用返回的句柄。

如果调用成功，调用者即可通过 [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) 参数使用 *baseBuffer* 向硬件传入输入数据并获取流的输出数据。传给 [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) 的任何缓冲区都必须按 16 字节对齐。以下是可用于管理输入输出缓冲区的简单缓冲区管理器示例。

```cpp theme={null}
class TransformOneBufferManager 
{ 
private: 
    float const* _outputBuffer = nullptr; 
    uint32_t _maxBufferFrameCount = 0; 
    uint32_t _bufferStride = 0; 
    uint32_t _maxBufferCount = 0; 
    uint32_t _acquiredBufferCount = 0; 
    uint32_t _channelCount = 1; 
 
public: 
    void Reset(float const* outputBuffer, uint32_t maxBufferCount, uint32_t maxBufferFrameCount, uint32_t channelCount) { 
        _outputBuffer = outputBuffer; 
        _maxBufferCount = maxBufferCount; 
        _maxBufferFrameCount = maxBufferFrameCount; 
        _channelCount = channelCount; 
    } 
 
    uint32_t GetMaxBufferFrameCount() const { 
        return _maxBufferFrameCount; 
    } 
 
    bool IsFull() const { 
        return _acquiredBufferCount >= _maxBufferCount; 
    } 
 
    void Acquire() { 
        if (_acquiredBufferCount < _maxBufferCount) { 
            _acquiredBufferCount++; 
        } 
    } 
 
    void Release() { 
        if (_acquiredBufferCount > 0) { 
            _acquiredBufferCount--; 
        } 
    } 
 
    float* GetOutputBuffer(uint32_t index) { 
        return const_cast<float*>(_outputBuffer) + (((index % _maxBufferCount) * _maxBufferFrameCount * _channelCount)); 
    } 
 
}; 
```

以下是分配内存并建立连接的第一步代码示例。

```cpp theme={null}
static const ULONGLONG XMemAllocAttributes = MAKE_XALLOC_ATTRIBUTES(allocatorId,
                      0,
                      XALLOC_MEMTYPE_PHYSICAL_CACHEABLE,
                      XALLOC_PAGESIZE_64KB,
                      XALLOC_ALIGNMENT_64K,
                      FALSE);

float* baseBuffer = (float *)XMemAlloc(baseBufferLength, XMemAllocAttributes);
```

其中 XMemAllocAttributes 表示上述属性。下列代码将 *aggregateImpulseResponse* 设为 32 秒。

```cpp theme={null}
hr = XDspConnect(baseBuffer, baseBufferLength, 32 /*aggregateImpulseResponseInSeconds*/, &clientHandle);
```

## 流的激活与停用

在与设备建立连接后，调用者应发送一个流激活命令以启用卷积、FFT 或 IFFT。调用者必须在 [XDspActivationParameters](/reference/audio/xdspaudio/structs/xdspactivationparameters) 中指定 [XDspProcessType](/reference/audio/xdspaudio/enums/xdspprocesstype)、块帧数、声道数、以浮点数表示的脉冲响应长度以及指向频域脉冲响应数据的指针。只允许单声道或立体声流。对于 [XDspProcessType::ForwardFourierTransform](/reference/audio/xdspaudio/enums/xdspprocesstype) 和 [XDspProcessType::inverseFourierTransform](/reference/audio/xdspaudio/enums/xdspprocesstype)，[XDspActivationParameters::impulseResponse](/reference/audio/xdspaudio/structs/xdspactivationparameters) 应为 nullptr，且 [XDspActivationParameters::impulseResponseLengthInFloats](/reference/audio/xdspaudio/structs/xdspactivationparameters) = 0。[XDspActivationOptions](/reference/audio/xdspaudio/enums/xdspactivationoptions) 应做相应设置。

以下是激活一个正向傅里叶变换流的代码，逆向傅里叶变换的代码与此类似。

```cpp theme={null}
XDspActivationParameters params; 
XDspStatus* status; 
XDspStreamHandle* streamHandle; 
params.type = isForward ? XDspProcessType::ForwardFourierTransform : XDspProcessType::InverseFourierTransform;  // choose FFT vs IFFT
params.blockFrameCount = maxBufferFrameCount; 
params.channelCount = 1; // only mono is supported 
params.impulseResponseLengthInFloats = 0; // should be 0 for FFT/IFFT
params.impulseResponse = nullptr;  // should be nullptr for FFT/IFFT
params.options |= isPolar ? XDspActivationOptions::EnablePolarFormat : XDspActivationOptions::None;  // specifies whether the output should be in Polar format or Cartesian format and for IFFT this specifies whether the input is in Polar or Cartesian format.
 
hr = XDspActivate(clientHandle, &params, &status, &streamHandle);
```

[XDspActivate](/reference/audio/xdspaudio/functions/xdspactivate) 会返回一个 [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) 缓冲区作为其参数之一。该缓冲区可用于查询硬件为该流处理的命令的状态。收到激活结果后，调用者即可开始提交用于卷积/FFT/IFFT 的数据。这应通过 [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) API 完成。每次调用 [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) 时，调用者必须：

1. 用恰好 *blockFrameCount* 帧数据填充输入缓冲区。
2. 用合适的值以及按 16 字节对齐的输入/输出缓冲区填充 [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand)。
3. 调用 [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) 将命令提交给硬件。该函数返回一个命令序号，可用于跟踪已发送给硬件的命令数量。

```cpp theme={null}
XDspCommand command;  
command.inputBlockBuffer = inputDataBuffer; 
command.outputBlockBuffer =  bufferManager.GetOutputBuffer(_totalCommandsSubmitted + 1); 
command.blockBufferMultiplier = nullptr;
 
uint32_t sequence = 0; 
 hr = XDspSubmitCommand(streamHandle, &command, &sequence); 
 
 if (SUCCEEDED(hr)) 
 { 
     totalCommandsSubmitted++; 
 } 
```

接下来，可按如下方式检查 [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) 以获取已提交给硬件的命令的状态：

```cpp theme={null}
while (status->lastProcessedCommandSequence > totalResponsesReceived)
{
    totalResponsesReceived++;
 
    // Hardware status is only updated when there is a hardware fault and the
    // error is going to persist for the duration of the stream. But, all the
    // commands that have already been submitted will still be processed by
    // the hardware and we have to wait for these commands to be processed 
    // before calling XDspDeactivate.
    hr = status->result; 
     
    float* outBuffer = bufferManager.GetOutputBuffer(totalResponsesReceived);
    // The output will be in the outBuffer
    // Do something with the outBuffer
    bufferManager.Release();
}
```

当流中的数据包处理完毕，或硬件在 [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) 结果中返回错误时，应调用 [XDspDeactivate](/reference/audio/xdspaudio/functions/xdspdeactivate)。

```cpp theme={null}
while (totalResponsesReceived < totalCommandsSubmitted)
{
    // run the above code in the while loop to obtain all the responses
}
hr = XDspDeactivate(streamHandle);
```

如果硬件尚未完成本流所有已提交命令的处理，[XDspDeactivate](/reference/audio/xdspaudio/functions/xdspdeactivate) 会返回 *XDSP\_E\_PENDING\_RESULTS*。

## 终止

[XDspDeactivate](/reference/audio/xdspaudio/functions/xdspdeactivate) 成功后，调用者必须调用 [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) 断开与音频硬件加速单元的连接，以释放所有已分配的资源。如果并非所有流都已停用，[XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) 会返回 *XDSP\_E\_NOT\_ALL\_HANDLES\_DEACTIVATED* 错误。

```cpp theme={null}
hr = XDspDisconnect(clientHandle);
XMemFree(baseBuffer, XMemAllocAttributes);
```


## Related topics

- [XDspActivate](/zh-CN/reference/audio/xdspaudio/functions/xdspactivate.md)
- [XDspStatus](/zh-CN/reference/audio/xdspaudio/structs/xdspstatus.md)
- [XDspDeactivate](/zh-CN/reference/audio/xdspaudio/functions/xdspdeactivate.md)
- [XDspDisconnect](/zh-CN/reference/audio/xdspaudio/functions/xdspdisconnect.md)
- [XDspSubmitCommandWithEnvelope](/zh-CN/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope.md)
