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

# Convolución de secuencia única con envolvente mediante la API XDSP

> Información general de una convolución de secuencia única con envolvente mediante la API XDSP

En este tema, se explica el flujo de código previsto sobre cómo usar la reverberación por convolución de hardware en dispositivos XBOX Series X mediante uno de los ejemplos incluidos con el Microsoft Game Development Kit (GDK).

## Conexión con el hardware e inicio

Se debe establecer una conexión con la unidad de aceleración de hardware mediante [XDspConnect](/reference/audio/xdspaudio/functions/xdspconnect) o [XDspConnectWithMaximumStreamLimit](/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit). El llamador establece los requisitos de la conexión mediante los parámetros siguientes:

1. *baseBuffer*: un puntero a la memoria asignada por el usuario suficiente para contener los búferes de entrada, salida, envolvente o multiplicadores de bloque que se pasarán al hardware. Esta memoria se debe asignar mediante XMemAlloc con estos atributos: *XALLOC\_MEMTYPE\_PHYSICAL\_CACHEABLE*, *XALLOC\_PAGESIZE\_64KB*, *XALLOC\_ALIGNMENT\_64K*. Estos atributos se deben establecer mediante `MAKE_XALLOC_ATTRIBUTES()`, como se muestra en el ejemplo siguiente. Esta memoria no se debe liberar hasta que la llamada a [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) se realice correctamente. Este búfer debe tener un tamaño mínimo de 64 KB.

2. *baseBufferLength*: la longitud del *baseBuffer* en bytes. Este búfer debe tener al menos 64 KB y no más de 500 MB.

3. *aggregateImpulseResponseInSeconds*: representa la duración total de todas las respuestas al impulso individuales que se activarán simultáneamente mediante [XDspActivate](/reference/audio/xdspaudio/functions/xdspactivate) con [XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype). Si este valor es 0, no se puede activar ninguna secuencia con [XDspProcessType::Convolution](/reference/audio/xdspaudio/enums/xdspprocesstype).

4. handle: un puntero al [XDspClientHandle](/reference/audio/xdspaudio/handles/xdspclienthandle) que contendrá el identificador devuelto por esta llamada.

Si la llamada se realiza correctamente, el llamador puede usar el *baseBuffer* para pasar los datos de entrada y recuperar los datos de salida de las secuencias mediante los parámetros de [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand). Cualquier búfer que se pase en [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) debe estar alineado a 16 bytes. El siguiente ejemplo de un administrador de búferes sencillo se puede usar para administrar los búferes de entrada y salida.

```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));
    }

};
```

Los búferes de envolvente de cada secuencia deben formar parte del baseBuffer que se pasa a **XDspConnect** o **XDspConnectWithMaximumStreamLimit**, de forma similar a los búferes de entrada y salida. La longitud de cada búfer de envolvente se puede calcular de la siguiente manera.

```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);
```

A continuación se muestra el ejemplo de código del primer paso para asignar memoria y establecer una conexión. Tenga en cuenta que se puede usar **XDspConnectWithMaximumStreamLimit** en lugar de **XDspConnect** para reducir el uso de memoria interna si el número de secuencias que pueden estar activas es inferior a 256.

```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);
```

Donde XMemAllocAttributes representa los atributos mencionados anteriormente. El código siguiente establece *aggregateImpulseResponse* en 32 segundos.

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

## Activación y desactivación de secuencias

Una vez establecida la conexión con el dispositivo, el llamador debe enviar un comando de activación de secuencia para iniciar una convolución, FFT o IFFT. El llamador debe especificar el [XDspProcessType](/reference/audio/xdspaudio/enums/xdspprocesstype), el número de tramas por bloque, el número de canales, la longitud de la respuesta al impulso en floats y un puntero a los datos de la respuesta al impulso en el dominio de la frecuencia en [XDspActivationParameters](/reference/audio/xdspaudio/structs/xdspactivationparameters). Solo se permiten secuencias mono o estéreo. Para [XDspProcessType::ForwardFourierTransform](/reference/audio/xdspaudio/enums/xdspprocesstype) y [XDspProcessType::inverseFourierTransform](/reference/audio/xdspaudio/enums/xdspprocesstype), [XDspActivationParameters::impulseResponse](/reference/audio/xdspaudio/structs/xdspactivationparameters) debe ser nullptr y [XDspActivationParameters::impulseResponseLengthInFloats](/reference/audio/xdspaudio/structs/xdspactivationparameters) = 0. [XDspActivationOptions](/reference/audio/xdspaudio/enums/xdspactivationoptions) se debe establecer adecuadamente.

A continuación se muestra el código para activar una secuencia para convolución.

```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) devuelve un búfer [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) como uno de sus parámetros. Este búfer [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) se debe usar para conocer el estado de los comandos procesados por el hardware para esta secuencia. Una vez recibido el resultado de la activación, el llamador puede empezar a enviar datos para la convolución, la FFT o la IFFT. Esto se debe hacer mediante la API [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) o [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope). En cada llamada, el llamador debe hacer lo siguiente:

1. Rellenar el búfer de entrada con exactamente *blockFrameCount* de datos.
2. Rellenar el [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand) con los valores adecuados y con búferes de entrada y salida alineados a 16 bytes.
3. Llamar a [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) para enviar el comando al hardware. Esta función devuelve un número de secuencia de comando que se puede usar para hacer un seguimiento del número de comandos enviados al hardware.
4. Para aplicar una envolvente, rellene el búfer de envolvente con valores de ganancia para cada bloque del filtro de respuesta al impulso, llame a [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) y continúe realizando esta llamada con el mismo búfer de envolvente hasta que la envolvente ya no sea necesaria o deba cambiarse.
5. Cuando la envolvente ya no sea necesaria, llame a [XDspSubmitCommand](/reference/audio/xdspaudio/functions/xdspsubmitcommand) o a [XDspSubmitCommandWithEnvelope](/reference/audio/xdspaudio/functions/xdspsubmitcommandwithenvelope) con un nullptr para *envelopeBuffer*. Tenga en cuenta que **XDspSubmitCommandWithEnvelope** con un nullptr para *envelopeBuffer* equivale a **XDspSubmitCommand**.
6. Al cambiar la envolvente, el *envelopeBuffer* actual solo se puede actualizar para la siguiente envolvente si el hardware procesó todos los comandos que usaban esa envolvente y se han recogido los resultados. De lo contrario, use un búfer de envolvente diferente.

```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++;
}
```

El siguiente fragmento de código muestra un ejemplo de cómo rellenar el búfer de envolvente con valores de ganancia para cada bloque del filtro de respuesta al impulso. Aquí se muestra cómo usar solo la primera mitad del filtro de respuesta al impulso, con los valores de ganancia de la primera mitad de los bloques de respuesta al impulso comenzando en 1.0f y disminuyendo linealmente, y la segunda mitad rellenada con un valor de ganancia de 0.0f. Para un filtro de respuesta al impulso estéreo, el filtro completo del canal izquierdo va seguido del filtro del canal derecho; el búfer de envolvente para el filtro de respuesta al impulso estéreo sigue el mismo patrón, con los valores de ganancia de todo el canal izquierdo seguidos de los valores de ganancia de todo el canal derecho. Consulte [Información general sobre la envolvente mediante la API XDSP](/build/console-features/audio/overviews/xdsp-overview-enveloping) para obtener información sobre cómo calcular el tamaño del búfer de envolvente.

```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));
    }
}
```

El siguiente fragmento de código muestra cómo aplicar la envolvente mediante una llamada a **XDspSubmitCommandWithEnvelope** con un *envelopeBuffer* válido. Es igual que el fragmento que muestra cómo enviar un comando mediante **XDspSubmitCommand**, salvo que se pasa el parámetro adicional *envelopeBuffer*. Esto se debe repetir hasta que la envolvente ya no sea necesaria, momento en el que se debe llamar a **XDspSubmitCommand** o a **XDspSubmitCommandWithEnvelope** con un nullptr para *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 = XDspSubmitCommandWithEnvelope(streamHandle, &command, envelopeBuffer, &sequence);

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

A continuación, se puede comprobar [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus) para conocer el estado de los comandos enviados al hardware de la siguiente manera:

```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();
}
```

Cuando se procesan los paquetes de la secuencia o el hardware devuelve un error en el resultado de [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus), se debe llamar a [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) devuelve *XDSP\_E\_PENDING\_RESULTS* si el hardware no ha terminado de procesar todos los comandos enviados para esta secuencia.

## Finalización

Después de que [XDspDeactivate](/reference/audio/xdspaudio/functions/xdspdeactivate) se realice correctamente, el llamador debe llamar a [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) para desconectarse de la unidad de aceleración de hardware de audio y liberar todos los recursos asignados. Si no se han desactivado todas las secuencias, [XDspDisconnect](/reference/audio/xdspaudio/functions/xdspdisconnect) devolverá el error *XDSP\_E\_NOT\_ALL\_HANDLES\_DEACTIVATED*.

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

## Documentación de referencia de la API

* [XDspAudio (contenido de la API)](/reference/audio/xdspaudio/xdspaudio_members)
  * Funciones
    * [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)
  * Estructuras
    * [XDspCommand](/reference/audio/xdspaudio/structs/xdspcommand)
    * [XDspActivationParameters](/reference/audio/xdspaudio/structs/xdspactivationparameters)
    * [XDspStatus](/reference/audio/xdspaudio/structs/xdspstatus)

## Consulte también

[Información general de XDSP](/build/console-features/audio/overviews/xdsp-overview)
[Información general sobre la envolvente mediante la API XDSP](/build/console-features/audio/overviews/xdsp-overview-enveloping)


## Related topics

- [Información general sobre la envolvente de XDSP](/es/build/console-features/audio/overviews/xdsp-overview-enveloping.md)
- [Información general de una convolución de secuencia única mediante la API XDSP](/es/build/console-features/audio/overviews/xdsp-overview-single-stream-convolution.md)
- [Información general de una FFT/IFFT de secuencia única mediante la API XDSP](/es/build/console-features/audio/overviews/xdsp-overview-single-stream-fft-ifft.md)
- [XDspConnect](/es/reference/audio/xdspaudio/functions/xdspconnect.md)
- [XDspConnectWithMaximumStreamLimit](/es/reference/audio/xdspaudio/functions/xdspconnectwithmaximumstreamlimit.md)
