> ## 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 進行含包絡的單一串流摺積

> 使用 XDSP API 進行含包絡的單一串流摺積概觀

在本主題中，我們將使用 Microsoft Game Development Kit (GDK) 隨附的其中一個範例，逐步說明如何在 XBOX Series X 裝置上使用硬體摺積殘響的預期程式碼流程。

## 硬體連線和啟動

必須透過 [XDspConnect](/zh-TW/reference/audio/xdspaudio/functions/xdspconnect) 或 [XDspConnectWithMaximumStreamLimit](/zh-TW/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit) 與硬體加速單元建立連線。呼叫端會使用下列參數設定連線的需求：

1. *baseBuffer* - 指向使用者配置記憶體的指標，此記憶體必須足以容納將傳遞給硬體的輸入、輸出、包絡緩衝區和/或區塊乘數緩衝區。此記憶體應透過 XMemAlloc 以下列屬性配置：*XALLOC\_MEMTYPE\_PHYSICAL\_CACHEABLE*、*XALLOC\_PAGESIZE\_64KB*、*XALLOC\_ALIGNMENT\_64K*。這些屬性應使用 `MAKE_XALLOC_ATTRIBUTES()` 設定，如下列範例所示。在 [XDspDisconnect](/zh-TW/reference/audio/xdspaudio/functions/xdspdisconnect) 呼叫成功之前，不應釋放此記憶體。此緩衝區的大小應至少為 64KB。

2. *baseBufferLength* - *baseBuffer* 的長度 (以位元組為單位)。此緩衝區應至少為 64KB，且不超過 500 MB。

3. *aggregateImpulseResponseInSeconds* - 代表將使用 [XDspActivate](/zh-TW/reference/audio/xdspaudio/functions/xdspactivate) 搭配 [XDspProcessType::Convolution](/zh-TW/reference/audio/xdspaudio/enums/xdspprocesstype) 同時啟用的所有個別脈衝回應的總持續時間。如果此值為 0，則無法啟用任何使用 [XDspProcessType::Convolution](/zh-TW/reference/audio/xdspaudio/enums/xdspprocesstype) 的串流。

4. handle - 指向 [XDspClientHandle](/zh-TW/reference/audio/xdspaudio/handles/xdspclienthandle) 的指標，用來保存此呼叫所傳回的控制代碼。

如果呼叫成功，呼叫端就可以使用 *baseBuffer*，透過 [XDspCommand](/zh-TW/reference/audio/xdspaudio/structs/xdspcommand) 參數傳遞輸入資料並擷取串流的輸出資料。在 [XDspCommand](/zh-TW/reference/audio/xdspaudio/structs/xdspcommand) 中傳遞的任何緩衝區都應以 16 位元組對齊。下列簡單緩衝區管理員範例可用於管理輸入和輸出緩衝區。

```cpp theme={null}
class ConvolveOneBufferManager
{
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));
    }

};
```

與輸入和輸出緩衝區類似，每個串流的包絡緩衝區都應該是傳遞給 **XDspConnect** 或 **XDspConnectWithMaximumStreamLimit** 之 baseBuffer 的一部分。每個包絡緩衝區的長度可依下列方式計算。

```cpp theme={null}
#define ROUND_UP_TO_16BYTE_ALIGNED(x) (((x) + (15)) & ~(0xF))
uint32_t blockSize = 1024; // can be 512 or 1024
uint32_t irLengthInComplexes = impulseResponseLengthInFloats/2;
uint32_t numFilterBlocks = irLengthInComplexes/blockSize;

// In case of a stereo impulse response filter, the envelope buffer length will be 2 * envelopeLengthInBytes assuming impulseResponseLengthInFloats is the impule response length of a single channel.
uint32_t envelopeLengthInBytes = ROUND_UP_TO_16BYTE_ALIGNED(numFilterBlocks * sizeof(float));
uint32_t envelopeGainCount = envelopeLengthInBytes/sizeof(float);
```

以下是配置記憶體並建立連線之第一個步驟的程式碼範例。請注意，如果可作用中的串流數目低於 256，可以使用 **XDspConnectWithMaximumStreamLimit** 取代 **XDspConnect**，以減少內部記憶體使用量。

```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](/zh-TW/reference/audio/xdspaudio/structs/xdspactivationparameters) 中指定 [XDspProcessType](/zh-TW/reference/audio/xdspaudio/enums/xdspprocesstype)、區塊框架計數、聲道計數、以浮點數為單位的脈衝回應長度，以及指向頻域中脈衝回應資料的指標。只允許單聲道或立體聲串流。對於 [XDspProcessType::ForwardFourierTransform](/zh-TW/reference/audio/xdspaudio/enums/xdspprocesstype) 和 [XDspProcessType::inverseFourierTransform](/zh-TW/reference/audio/xdspaudio/enums/xdspprocesstype)，[XDspActivationParameters::impulseResponse](/zh-TW/reference/audio/xdspaudio/structs/xdspactivationparameters) 應為 nullptr，且 [XDspActivationParameters::impulseResponseLengthInFloats](/zh-TW/reference/audio/xdspaudio/structs/xdspactivationparameters) = 0。應適當地設定 [XDspActivationOptions](/zh-TW/reference/audio/xdspaudio/enums/xdspactivationoptions)。

以下是啟用一個摺積串流的程式碼。

```cpp theme={null}
XDspActivationParameters params;
XDspStatus* status;
XDspStreamHandle* streamHandle;
params.type = XDspProcessType::Convolution;
params.blockFrameCount = maxBufferFrameCount;
params.channelCount = channelCount; // mono or stereo
params.impulseResponseLengthInFloats = filterFrameCount;
params.impulseResponse = filterBuffer;

if (channelCount == 2 && deinterleaved)
{
    params.options |= XDspActivationOptions::Deinterleaved;
}
if (stereoImpulseResponse)
{
    params.options |= XDspActivationOptions::StereoImpulseResponse;
}
hr = XDspActivate(clientHandle, &params, &status, &streamHandle); 
```

[XDspActivate](/zh-TW/reference/audio/xdspaudio/functions/xdspactivate) 會以其中一個參數傳回 [XDspStatus](/zh-TW/reference/audio/xdspaudio/structs/xdspstatus) 緩衝區。此 [XDspStatus](/zh-TW/reference/audio/xdspaudio/structs/xdspstatus) 緩衝區應用來尋找硬體為此串流所處理之命令的狀態。收到啟用結果之後，呼叫端就可以開始提交資料以進行摺積/FFT/IFFT。這應使用 [XDspSubmitCommand](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommand) 或 [XDspSubmitCommandWithEnvelope](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) API 來完成。每次呼叫時，呼叫端都必須執行下列動作：

1. 以剛好 *blockFrameCount* 的資料填入輸入緩衝區。
2. 以適當的值以及 16 位元組對齊的輸入和輸出緩衝區填入 [XDspCommand](/zh-TW/reference/audio/xdspaudio/structs/xdspcommand)。
3. 呼叫 [XDspSubmitCommand](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommand) 將命令提交至硬體。此函式會傳回命令序號，可用來追蹤傳送至硬體的命令數目。
4. 若要套用包絡，請以脈衝回應篩選器每個區塊的增益值填入包絡緩衝區，並呼叫 [XDspSubmitCommandWithEnvelope](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope)，並持續以相同的包絡緩衝區進行此呼叫，直到不再需要包絡或需要變更包絡為止。
5. 不再需要包絡時，請以 nullptr 作為 *envelopeBuffer* 呼叫 [XDspSubmitCommand](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommand) 或 [XDspSubmitCommandWithEnvelope](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope)。請注意，以 nullptr 作為 *envelopeBuffer* 的 **XDspSubmitCommandWithEnvelope** 等同於 **XDspSubmitCommand**。
6. 切換包絡時，只有在硬體已處理所有使用目前 *envelopeBuffer* 的命令且結果已被取回時，才能為下一個包絡更新該緩衝區。否則，請使用不同的包絡緩衝區。

```cpp theme={null}
XDspCommand command;

command.beginScale = 1.0f;
command.endScale = 1.0f;
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++;
}
```

下列程式碼片段示範以脈衝回應篩選器每個區塊的增益值填入包絡緩衝區的範例。此處示範如何只使用脈衝回應篩選器的前半部，前半部脈衝回應區塊的增益值從 1.0f 開始線性遞減，而後半部則以 0.0f 的增益值填入。對於立體聲脈衝回應篩選器，左聲道的整個篩選器後面接著右聲道的篩選器；立體聲脈衝回應篩選器的包絡緩衝區也遵循相同的模式，整個左聲道的增益值後面接著整個右聲道的增益值。如需計算包絡緩衝區大小的相關資訊，請參閱[使用 XDSP API 的包絡概觀](/zh-TW/build/console-features/audio/overviews/xdsp-overview-enveloping)。

```cpp theme={null}
// Please see the code snippet at the beginning of this example for information on the envelope buffer size and envelope gain counts.
void CreateEnvelopeParams(
    float* envelopeBuffer, 
    uint32_t envelopeGainCount, 
    uint32_t numFilterBlocks,
    bool stereoImpulseResponse)
{
    const uint32_t envelopeRatio = 0.5f; // Use only half of the impulse response filter
    uint32_t numGains = (uint32_t)(envelopeRatio * numFilterBlocks);
    float gainDecrement = 1.0f/numGains;

    envelopeBuffer[0] = 1.0f;
        
    for (uint32_t i = 1; i < envelopeGainCount; i++)
    {
        if (i < numGains)
        {
            envelopeBuffer[i] = envelopeBuffer[i-1] - gainDecrement;
        }
        else
        {
            envelopeBuffer[i] = 0.0f;
        }
    }

    if (stereoImpulseResponse)
    {
        // Populate gain values for the right channel envelope
        memcpy((void*)&envelopeBuffer[envelopeGainCount], (void*)&envelopeBuffer[0], envelopeGainCount * sizeof(float));
    }
}
```

下列程式碼片段示範以有效的 *envelopeBuffer* 呼叫 **XDspSubmitCommandWithEnvelope** 來套用包絡。這與示範如何使用 **XDspSubmitCommand** 提交命令的程式碼片段相同，只是多傳入了 *envelopeBuffer* 參數。應重複執行此動作，直到不再需要包絡為止，屆時應呼叫 **XDspSubmitCommand**，或以 nullptr 作為 *envelopeBuffer* 呼叫 **XDspSubmitCommandWithEnvelope**。

```cpp theme={null}
XDspCommand command;

command.beginScale = 1.0f;
command.endScale = 1.0f;
command.inputBlockBuffer = inputDataBuffer;
command.outputBlockBuffer =  bufferManager.GetOutputBuffer(_totalCommandsSubmitted + 1);
command.blockBufferMultiplier = nullptr;

uint32_t sequence = 0;
hr = XDspSubmitCommandWithEnvelope(streamHandle, &command, envelopeBuffer, &sequence);

if (SUCCEEDED(hr))
{
    totalCommandsSubmitted++;
}
```

接下來，可以依下列方式檢查 [XDspStatus](/zh-TW/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](/zh-TW/reference/audio/xdspaudio/structs/xdspstatus) 結果中傳回錯誤時，應呼叫 [XDspDeactivate](/zh-TW/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](/zh-TW/reference/audio/xdspaudio/functions/xdspdeactivate) 會傳回 *XDSP\_E\_PENDING\_RESULTS*。

## 終止

[XDspDeactivate](/zh-TW/reference/audio/xdspaudio/functions/xdspdeactivate) 成功之後，呼叫端必須呼叫 [XDspDisconnect](/zh-TW/reference/audio/xdspaudio/functions/xdspdisconnect) 以中斷與音訊硬體加速單元的連線，以釋放所有已配置的資源。如果並非所有串流都已停用，[XDspDisconnect](/zh-TW/reference/audio/xdspaudio/functions/xdspdisconnect) 將傳回 *XDSP\_E\_NOT\_ALL\_HANDLES\_DEACTIVATED* 錯誤。

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

## 參考 API 文件

* [XDspAudio (API 內容)](/zh-TW/reference/audio/xdspaudio/xdspaudio_members)
  * 函式
    * [XDspConnect](/zh-TW/reference/audio/xdspaudio/functions/xdspconnect)
    * [XDspConnectWithMaximumStreamLimit](/zh-TW/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit)
    * [XDspDisconnect](/zh-TW/reference/audio/xdspaudio/functions/xdspdisconnect)
    * [XDspActivate](/zh-TW/reference/audio/xdspaudio/functions/xdspactivate)
    * [XDspSubmitCommand](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommand)
    * [XDspSubmitCommandWithEnvelope](/zh-TW/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope)
    * [XDspDeactivate](/zh-TW/reference/audio/xdspaudio/functions/xdspdeactivate)
  * 結構
    * [XDspCommand](/zh-TW/reference/audio/xdspaudio/structs/xdspcommand)
    * [XDspActivationParameters](/zh-TW/reference/audio/xdspaudio/structs/xdspactivationparameters)
    * [XDspStatus](/zh-TW/reference/audio/xdspaudio/structs/xdspstatus)

## 另請參閱

[XDSP 概觀](/zh-TW/build/console-features/audio/overviews/xdsp-overview)
[使用 XDSP API 的包絡概觀](/zh-TW/build/console-features/audio/overviews/xdsp-overview-enveloping)


## Related topics

- [Play Anywhere(主機 + PC)總覽](/zh-TW/paths/play-anywhere/overview.md)
- [認證付費服務方案](/zh-TW/publishing/game-publishing/concepts/certification/certification-paid-services.md)
- [XR-037 內容套件的相依性](/zh-TW/publishing/certification/xr/xr-037.md)
- [XBOX 主機遊戲的認證測試 XBOX 需求](/zh-TW/publishing/certification/console-cert-tested-requirements.md)
- [使用 PlayFab 加入後端服務](/zh-TW/home/build-first-title/add-backend-services.md)
