> ## 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) に含まれているサンプルの 1 つを使用して、XBOX Series X デバイスでハードウェア コンボリューション リバーブを使用する方法についての想定されるコード フローを説明します。

## ハードウェアの接続と起動

[XDspConnect](/reference/audio/xdspaudio/functions/xdspconnect) または [XDspConnectWithMaximumStreamLimit](/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit) を介して、ハードウェア アクセラレーション ユニットとの接続を確立する必要があります。呼び出し元は、次のパラメーターを使用して接続の要件を設定します。

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 で 500 MB 以下である必要があります。

3. *aggregateImpulseResponseInSeconds* - [XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype) で [XDspActivate](/reference/audio/xdspaudio/functions/xdspactivate) を使用して同時にアクティブ化されるすべての個別のインパルス応答の全体的な持続時間を表します。この値が 0 の場合、[XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype) を持つストリームをアクティブ化することはできません。

4. handle - この呼び出しによって返されるハンドルを保持する [XDspClientHandle](/reference/audio/xdspaudio/handles/xdspclienthandle) へのポインター。

呼び出しが成功すると、*baseBuffer* を呼び出し元が使用して、[XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) パラメーターを介してストリームの入力データを渡し、出力データを取得できます。[XDspCommand](/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 未満の場合、内部メモリ使用量を減らすために、**XDspConnect** の代わりに **XDspConnectWithMaximumStreamLimit** を使用できます。

```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)、ブロック フレーム カウント、チャネル カウント、float 単位のインパルス応答の長さ、および周波数領域のインパルス応答データへのポインターを指定する必要があります。モノラルまたはステレオのストリームのみが許可されます。[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) は適切に設定してください。

コンボリューション用に 1 つのストリームをアクティブ化するコードを以下に示します。

```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](/reference/audio/xdspaudio/functions/xdspactivate) は、パラメーターの 1 つとして [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) バッファーを返します。この [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) バッファーを使用して、このストリームに対してハードウェアによって処理されたコマンドの状態を確認してください。アクティブ化の結果を受け取った後、呼び出し元は Convolution/FFT/IFFT 用にデータを送信できます。これは、[XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) または [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) API を使用して行う必要があります。各呼び出しで、呼び出し元は以下を行う必要があります。

1. 入力バッファーに正確に *blockFrameCount* のデータを入れます。
2. [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) に適切な値と、16 バイトにアラインされた入力および出力バッファーを入れます。
3. [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) を呼び出して、ハードウェアにコマンドを送信します。この関数は、ハードウェアに送信されたコマンドの数を追跡するために使用できるコマンド シーケンス番号を返します。
4. エンベロープを適用するには、インパルス応答フィルターの各ブロックのゲイン値でエンベロープ バッファーを埋め、[XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) を呼び出し、エンベロープ処理が不要になるか、エンベロープを変更する必要があるまで、同じエンベロープ バッファーでこの呼び出しを続けます。
5. エンベロープ処理が不要になったら、*envelopeBuffer* を nullptr にして [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) または [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) を呼び出します。*envelopeBuffer* を nullptr にした **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 を使用したエンベロープ処理の概要](/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** を呼び出してエンベロープを適用する例を示しています。これは、追加の *envelopeBuffer* パラメーターを渡すことを除いて、**XDspSubmitCommand** を使用してコマンドを送信するスニペットと同じです。エンベロープが不要になるまでこれを繰り返す必要があります。その時点で、**XDspSubmitCommand** または *envelopeBuffer* を nullptr にした **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](/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);
```

## リファレンス API ドキュメント

* [XDspAudio (API 内容)](/reference/audio/xdspaudio/xdspaudio_members)
  * 関数
    * [XDspConnect](/reference/audio/xdspaudio/functions/xdspconnect)
    * [XDspConnectWithMaximumStreamLimit](/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit)
    * [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect)
    * [XDspActivate](/reference/audio/xdspaudio/functions/xdspactivate)
    * [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand)
    * [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope)
    * [XDspDeactivate](/reference/audio/xdspaudio/functions/xdspdeactivate)
  * 構造体
    * [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand)
    * [XDspActivationParameters](/reference/audio/xdspaudio/structs/xdspactivationparameters)
    * [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus)

## 関連項目

[XDSP の概要](/build/console-features/audio/overviews/xdsp-overview)
[XDSP API を使用したエンベロープ処理の概要](/build/console-features/audio/overviews/xdsp-overview-enveloping)


## Related topics

- [XDSP エンベロープ処理の概要](/ja-jp/build/console-features/audio/overviews/xdsp-overview-enveloping.md)
- [XDSP API を使用したシングル ストリーム コンボリューションの概要](/ja-jp/build/console-features/audio/overviews/xdsp-overview-single-stream-convolution.md)
- [概要](/ja-jp/build/console-features/audio/overviews/index.md)
- [XDspConnect](/ja-jp/reference/audio/xdspaudio/functions/xdspconnect.md)
- [XDspSubmitCommand](/ja-jp/reference/audio/xdspaudio/functions/xdspsubmitcommand.md)
