How to: Create an XAPO

The XAPO API provides the IXAPO interface and the CXAPOBase class for building new XAPO types. The IXAPO interface contains all of the methods that need to be implemented to create a new XAPO. The CXAPOBase class provides a basic implementation of the IXAPO interface. CXAPOBase implements all of the IXAPO interface methods except the IXAPO::Process method, which is unique to each XAPO.

To create a new static XAPO

  1. Derive a new XAPO class from the CXAPOBase base class.

    Note

    XAPOs implement the IUnknown interface. The IXAPO and IXAPOParameters interfaces include the three IUnknown methods: QueryInterface, AddRef, and Release. CXAPOBase provides implementations of all three of the IUnknown methods. A new instance of CXAPOBase will have a reference count of 1. It will be destroyed when its reference count becomes 0. To allow XAudio2 to destroy an instance of an XAPO when it is no longer needed, call IUnknown::Release on the XAPO after it is added to an XAudio2 effect chain. See How to: Use an XAPO in XAudio2 for more information about using an XAPO with XAudio2.

     

  2. Override the CXAPOBase class implementation of the IXAPO::LockForProcess method.

    Overriding LockForProcess allows information about the format of audio data to be stored for use in IXAPO::Process.

  3. Implement the IXAPO::Process method.

    XAudio2 calls the IXAPO::Process method whenever an XAPO needs to process audio data. Process contains the bulk of the code for an XAPO.

Implementing the Process Method

The IXAPO::Process method accepts stream buffers for input and output audio data. A typical XAPO will expect one input stream buffer and one output stream buffer. You should base the processing of data from the input stream buffer on the format specified in the LockForProcess function and any flags passed to the Process function with the input stream buffer. Copy the processed input stream buffer data to the output stream buffer. Set the output stream buffer's BufferFlags parameter as either XAPO_BUFFER_VALID or XAPO_BUFFER_SILENT.

The following example demonstrates a LockForProcess and Process implementation that simply copies data from an input buffer to an output buffer. However, there is no processing if the input buffer is marked with XAPO_BUFFER_SILENT.

STDMETHOD(LockForProcess) (UINT32 InputLockedParameterCount,
          const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS* pInputLockedParameters,
          UINT32 OutputLockedParameterCount,
          const XAPO_LOCKFORPROCESS_BUFFER_PARAMETERS* pOutputLockedParameters)
{
    assert(!IsLocked());
    assert(InputLockedParameterCount == 1);
    assert(OutputLockedParameterCount == 1);
    assert(pInputLockedParameters != NULL);
    assert(pOutputLockedParameters != NULL);
    assert(pInputLockedParameters[0].pFormat != NULL);
    assert(pOutputLockedParameters[0].pFormat != NULL);


    m_uChannels = pInputLockedParameters[0].pFormat->nChannels;
    m_uBytesPerSample = (pInputLockedParameters[0].pFormat->wBitsPerSample >> 3);

    return CXAPOBase::LockForProcess(
        InputLockedParameterCount,
        pInputLockedParameters,
        OutputLockedParameterCount,
        pOutputLockedParameters);
}
STDMETHOD_(void, Process)(UINT32 InputProcessParameterCount,
           const XAPO_PROCESS_BUFFER_PARAMETERS* pInputProcessParameters,
           UINT32 OutputProcessParameterCount,
           XAPO_PROCESS_BUFFER_PARAMETERS* pOutputProcessParameters,
           BOOL IsEnabled)
{
    assert(IsLocked());
    assert(InputProcessParameterCount == 1);
    assert(OutputProcessParameterCount == 1);
    assert(NULL != pInputProcessParameters);
    assert(NULL != pOutputProcessParameters);


    XAPO_BUFFER_FLAGS inFlags = pInputProcessParameters[0].BufferFlags;
    XAPO_BUFFER_FLAGS outFlags = pOutputProcessParameters[0].BufferFlags;

    // assert buffer flags are legitimate
    assert(inFlags == XAPO_BUFFER_VALID || inFlags == XAPO_BUFFER_SILENT);
    assert(outFlags == XAPO_BUFFER_VALID || outFlags == XAPO_BUFFER_SILENT);

    // check input APO_BUFFER_FLAGS
    switch (inFlags)
    {
    case XAPO_BUFFER_VALID:
        {
            void* pvSrc = pInputProcessParameters[0].pBuffer;
            assert(pvSrc != NULL);

            void* pvDst = pOutputProcessParameters[0].pBuffer;
            assert(pvDst != NULL);

            memcpy(pvDst,pvSrc,pInputProcessParameters[0].ValidFrameCount * m_uChannels * m_uBytesPerSample);
            break;
        }

    case XAPO_BUFFER_SILENT:
        {
            // All that needs to be done for this case is setting the
            // output buffer flag to XAPO_BUFFER_SILENT which is done below.
            break;
        }

    }

    // set destination valid frame count, and buffer flags
    pOutputProcessParameters[0].ValidFrameCount = pInputProcessParameters[0].ValidFrameCount; // set destination frame count same as source
    pOutputProcessParameters[0].BufferFlags     = pInputProcessParameters[0].BufferFlags;     // set destination buffer flags same as source

}

When writing a Process method, it is important to note XAudio2 audio data is interleaved. This means data from each channel is adjacent for a particular sample number. For example, if there is a 4-channel wave playing into an XAudio2 source voice, the audio data is a sample of channel 0, a sample of channel 1, a sample of channel 2, a sample of channel 3, and then the next sample of channels 0, 1, 2, 3, and so on.

Audio Effects

XAPO Overview