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

# Single-stream Opus audio decoding with the XAPU API

> Overview of single stream Opus audio decoding by using the XAPU API

In this topic, we will walk through the expected code flow about how to use Opus hardware accelerated decoding on XBOX Series X devices by using one of the samples included with the Microsoft Game Development Kit (GDK). This sample is called `DecodeOne.cpp` and covers a single-stream decoding scenario with one client.

## Audio hardware connection and startup

Decoding starts by establishing a connection with the hardware acceleration unit via [XApuConnect](/reference/audio/xapu/functions/xapuconnect). The caller sets the requirements for the connection by using [XApuConnectInputParameters](/reference/audio/xapu/structs/xapuconnectinputparameters) to set up the following:

1. The processing type (in this case, [XApuProcessType::DecodeOpus](/reference/audio/xapu/enums/xapuprocesstype)).
2. The number of streams (in this case, 1).
3. The total memory that's required to pass and retrieve the input and the output data, in this case, 1024 for `MaxInputBufferLength` (must be equal to or greater than the largest Opus packet in the stream). The number must also be a multiple of 16 and 7680. This is 20 ms of data at 48 K, which is 960 × 2 × sizeof(float) = 7680 for `MaxOutputBufferLength`. The output also needs to be a multiple of 16.

If this call succeeds, the [XApuConnectOutputParameters::baseData](/reference/audio/xapu/structs/xapuconnectoutputparameters) parameter points to a memory location that's allocated by the operating system in such a way that the hardware acceleration unit can directly access without additional marshaling or copying. This memory should be used by the caller to pass the input data and retrieve the output data. The following example of a simple memory manager can be used to set [XApuConnectInputParameters](/reference/audio/xapu/structs/xapuconnectinputparameters) and partition [XApuConnectOutputParameters::baseData](/reference/audio/xapu/structs/xapuconnectoutputparameters) into two pointers: one for the input data and the other for the output data.

```cpp theme={null}
class DecodeOneSimpleMemoryManager
{
public:
    static const uint32_t MaxStreamCount = 1;
    const uint32_t MaxInputBufferLength = 1024; // Must be equal to or greater than the largest Opus packet in the stream. The number must also be a multiple of 16.
    const uint32_t MaxOutputBufferLength = 7680; // This is 20 ms of data at 48 K, which is 960 x 2 x sizeof(float) = 7680 for MaxOutputBufferLength. The output also needs to be multiple of 16.

private:
    XApuConnectInputParameters _inParam = {
        XApuProcessType::DecodeOpus,
        MaxStreamCount,
        1,
        MaxStreamCount * (MaxInputBufferLength + MaxOutputBufferLength),
        0,
        XApuConnectOptions::None };

    XApuConnectOutputParameters _outParam = {};

public:
    XApuConnectInputParameters * const GetConnectParametersRef() {
        return &_inputParam;
    }

    XApuConnectOutputParameters * const GetConnectionDataRef() {
        return &_outParam;
    }

    uint8_t* GetRawInputBuffer(uint32_t& maxByteCount) {
        maxByteCount = MaxInputBufferLength;
        return reinterpret_cast<uint8_t*>(_outParam.baseData);
    }

    uint8_t* GetRawOutputBuffer(uint32_t& maxByteCount) {
        maxByteCount = MaxOutputBufferLength;
        return reinterpret_cast<uint8_t*>(_outParam.baseData) + MaxInputBufferLength;
    }
};
```

Following is the code example for the first step to establish a connection.

```cpp theme={null}
XApuConnectInputParameters * const connectParameters = memoryManager.GetConnectParametersRef();
XApuConnectOutputParameters * const connectionData = memoryManager.GetConnectionDataRef();
hr = XApuConnect(connectParameters, connectionData, &xapuHandle);
```

## Activation and deactivation of streams

After the connection is established with the device, the caller should send a stream activation command to engage a single decoding engine. The caller must specify the stream index and the channel count. Only mono or stereo streams are allowed.

The caller should call [XApuDequeueResult](/reference/audio/xapu/functions/xapudequeueresult) to get the activation result. Because [XApuConnectInputParameters::maxQueuedCommandsPerStream](/reference/audio/xapu/structs/xapuconnectinputparameters) is set to 1 in this sample, the caller has to wait for each result before submitting the next command. This includes the `Activate`, `Process`, and `Deactivate` commands (for details, see [XApuCommandType](/reference/audio/xapu/enums/xapucommandtype)).

Following is the code to activate one stream.

```cpp theme={null}
XApuDecodeConvertActivateCommand command = {};
command.id.type = XApuCommandType::Activate;
command.id.streamIndex = 0;
command.id.sequence = 0;
command.channelCount = channelCount;

hr = XApuEnqueueCommand(xapuHandle, &command.id, nullptr);
XApuResult result = {};

do {
    hr = XApuDequeueResult(handle, &result);
} while (hr == XAPU_E_PENDING_RESULTS);
```

After the activation result is received, the caller can start submitting Opus packets for decoding. This should be done by using the [XApuCommandType::Process](/reference/audio/xapu/enums/xapucommandtype) command. For each `Process` command, the caller must do the following:

1. Fill the input buffer with exactly one Opus packet.
2. Specify the length of the packet.
3. Provide the location to the output buffer that will be filled with decoded data by the audio hardware acceleration unit.
4. Specify the maximum length of the output buffer.

Also, the caller must specify the `streamIndex` and `frameCount` values. If all the decoded data is required to be copied to the output buffer, the `frameCount` value can be set to any value that's equal to or greater than the number of frames that are encoded in the packet (for example, for a 20 ms packet, `frameCount` can be set between 960 and 0xFFFFFFFF).

```cpp theme={null}
XApuDecodeConvertCommand command = {};
command.id.streamIndex = 0;
command.id.type = XApuCommandType::Process;
command.id.sequence = packetIndex;
command.frameCount = 0xFFFFFFFF;
command.inputData = memoryManager.GetRawInputBuffer(inputMaxByteCount);
command.inputDataLength = packetLength;
command.outputData = memoryManager.GetRawOutputBuffer(outputMaxByteCount);
command.maxOutputDataLength = outputMaxByteCount;

hr = XApuEnqueueCommand(xapuHandle, &command.id, nullptr);
```

Next, [XApuDequeueResult](/reference/audio/xapu/functions/xapudequeueresult) should be called to get the decoded output of the packet. After [XApuResult](/reference/audio/xapu/structs/xapuresult) is successfully retrieved, the output data will be stored in `result.outputData`. After it's processed, the next packet should be submitted for decoding with a new call to [XApuEnqueueCommand](/reference/audio/xapu/functions/xapuenqueuecommand) with the [XApuCommandType::Process](/reference/audio/xapu/enums/xapucommandtype) command.

```cpp theme={null}
XApuResult result = {};

do {
    hr = XApuDequeueResult(handle, &result);
    if (SUCCEEDED(hr))
    {
         if (result.id.type == XApuCommandType::Process)
         {
              fwrite(result.outputData, 1, result.outputDataLength, fileOutput);
         }
    }
    
} while (hr == XAPU_E_PENDING_RESULTS);
```

After all the packets in the stream are decoded, an [XApuCommandType::Deactivate](/reference/audio/xapu/enums/xapucommandtype) command should be submitted.

```cpp theme={null}
XApuCommandId id;
id.type = XApuCommandType::Deactivate;
id.streamIndex = 0;
id.sequence = 0;

hr = XApuEnqueueCommand(xapuHandle, &id, nullptr);

do {
    hr = XApuDequeueResult(handle, &result);
} while (hr == XAPU_E_PENDING_RESULTS);
```

## Termination

After the result for the deactivation is received, the caller must call [XApuDisconnect](/reference/audio/xapu/functions/xapudisconnect) to disconnect from the audio hardware acceleration unit to free all the allocated resources.

```cpp theme={null}
hr = XApuDisconnect(xapuHandle);
```


## Related topics

- [XAPU overview](/build/console-features/audio/overviews/xapu-overview.md)
- [Overview of XBOX Series X|S audio hardware](/build/console-features/audio/overviews/scarlett-audio.md)
- [XAPU Errors](/reference/audio/xapu/enums/xapuerrors.md)
- [Overviews](/build/console-features/audio/overviews/index.md)
- [XApuConnectInputParameters](/reference/audio/xapu/structs/xapuconnectinputparameters.md)
