> ## 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 장치에서 하드웨어 Convolution reverb를 사용하는 예상 코드 흐름을 안내합니다.

## 하드웨어 연결 및 시작

[XDspConnect](/reference/audio/xdspaudio/functions/xdspconnect) 또는 [XDspConnectWithMaximumStreamLimit](/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit)를 통해 하드웨어 가속 유닛과 연결을 설정해야 합니다. 호출자는 다음 매개변수를 사용하여 연결 요구사항을 설정합니다:

1. *baseBuffer* - 하드웨어에 전달될 입력, 출력, 엔벨로프 버퍼 및/또는 블록 승수 버퍼를 담을 수 있는 사용자 할당 메모리를 가리키는 포인터입니다. 이 메모리는 *XALLOC\_MEMTYPE\_PHYSICAL\_CACHEABLE*, *XALLOC\_PAGESIZE\_64KB*, *XALLOC\_ALIGNMENT\_64K* 속성으로 XMemAlloc를 통해 할당되어야 합니다. 이러한 속성은 아래 예시와 같이 `MAKE_XALLOC_ATTRIBUTES()`를 사용하여 설정해야 합니다. 이 메모리는 [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) 호출이 성공할 때까지 해제되어서는 안 됩니다. 이 버퍼는 최소 64KB 크기여야 합니다.

2. *baseBufferLength* - *baseBuffer* 의 바이트 단위 길이. 이 버퍼는 최소 64KB이고 500MB 이하여야 합니다.

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)는 적절히 설정되어야 합니다.

다음은 컨볼루션을 위한 하나의 스트림을 활성화하는 코드입니다.

```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)는 매개변수 중 하나로 [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) 버퍼를 반환합니다. 이 [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) 버퍼는 이 스트림에 대해 하드웨어에서 처리된 명령의 상태를 찾는 데 사용해야 합니다. 활성화 결과가 수신된 후, 호출자는 컨볼루션/FFT/IFFT를 위한 데이터 제출을 시작할 수 있습니다. 이는 [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) 또는 [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) API를 사용하여 수행해야 합니다. 각 호출에서 호출자는 다음을 수행해야 합니다:

1. 정확히 *blockFrameCount* 만큼의 데이터로 입력 버퍼를 채웁니다.
2. 적절한 값과 16바이트 정렬된 입력 및 출력 버퍼로 [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand)를 채웁니다.
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** 를 사용하여 명령을 제출하는 방법을 보여주는 스니펫과 동일합니다. 이는 엔벨로프가 더 이상 필요하지 않을 때까지 반복되어야 하며, 이 시점에서 *envelopeBuffer* 에 nullptr을 사용하여 **XDspSubmitCommand** 또는 **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 contents)](/reference/audio/xdspaudio/xdspaudio_members)
  * Functions
    * [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)
  * Structures
    * [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 인벨로핑 개요](/ko/build/console-features/audio/overviews/xdsp-overview-enveloping.md)
- [XDSP API를 사용한 싱글 스트림 컨볼루션 개요](/ko/build/console-features/audio/overviews/xdsp-overview-single-stream-convolution.md)
- [XDspConnect](/ko/reference/audio/xdspaudio/functions/xdspconnect.md)
- [XDspSubmitCommand](/ko/reference/audio/xdspaudio/functions/xdspsubmitcommand.md)
- [XDspConnectWithMaximumStreamLimit](/ko/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit.md)
