Table des matières

How to Create Wwise Sound Engine Source Plug-ins

Source Plug-in Interface Implementation

Source plug-ins provide audio content to an output buffer using synthesis methods, including physical modeling, modulation synthesis, sampling synthesis, and so on. Writing source plug-ins consists of implementing the AK::IAkSourcePlugin interface as detailed in this document. Only the functions specific to AK::IAkSourcePlugin interface are covered here. Refer to How to Create Wwise Sound Engine Plug-ins for information about interface components shared with other plug-in types. Refer to the provided Sine sample plug-in for details (Samples).

AK::IAkSourcePlugin::Init()

This method prepares the source plug-in for data processing, allocates memory, and sets up initial conditions.

The plug-in is passed in a pointer to a memory allocator interface (AK::IAkPluginMemAlloc). You should perform all dynamic memory allocation through this interface using the provided memory allocation macros (refer to Allocating/De-allocating Memory in Audio Plug-ins). For the most common memory allocation needs, namely allocation at initialization and release at termination, the plug-in does not need to retain a pointer to the allocator as it will also be provided to the plug-in on termination.

The AK::IAkSourcePluginContext interface allows to retrieve information such as the number of loop iterations or other information related to the context in which the source plug-in is operated.

The plug-in also receives a pointer to its associated parameter node interface (AK::IAkPluginParam). Most plug-ins will want to keep a reference to the associated parameter node to be able to retrieve parameters at runtime. Refer to Communication Between Parameter Nodes and Plug-ins. for more details.

All of these interfaces will remain valid throughout the plug-in's lifespan so it is safe to keep an internal reference to them when necessary.

The audio format native to the platform on which the plug-in is executed is passed as an argument of the AK::IAkSourcePlugin::Init() function. It is highly recommended that plug-ins are output in the platform's native format to avoid any performance penalty incurred by audio format conversion. If for some reason the source is not suited to be output in this format, interleaved 16-bit signed samples may also be output. In multi-channel configurations, the source plug-in must also specify whether it is going to output interleaved or deinterleaved data (AkAudioFormat::uInterleaveID). The default, native setting is AK_NONINTERLEAVED. When outputting deinterleaved data, a source plug-in should use the AkAudioBuffer::GetChannel() method to access buffers of each channel. When outputting interleaved data, it should use AkAudioBuffer::GetInterleavedData(). Refer to Accessing Data Using AkAudioBuffer structure for more details.

The channel mask passed in will default to a mono (center speaker only) channel configuration. You may change this channel mask to whatever channel configuration you want your source plug-in to output. In the code example below, the channel mask was changed to stereo. The audio buffers to be received at each Execute will be able to accomodate the format specified by the plug-in upon initialization and will not change for the life span of the plug-in making it safe to store any format information required for later processing in the initialization routine.

AKRESULT CAkDCOffset::Init( AK::IAkPluginMemAlloc *     in_pAllocator,              // Memory allocator interface.
                            AK::IAkSourcePluginContext * in_pSourcePluginContext,   // Source plugin context
                            AK::IAkPluginParam *        in_pParams,                 // Effect parameters.
                            AkAudioFormat &             io_rFormat                  // Supported audio output format.
                            )   
{
    // Keep a pointer to associated parameter node.
    m_pParams = reinterpret_cast<CAkMyPluginParams*>( in_pParams );
    
    // Setup helper to handle looping and possibly changing duration of synthesis
    m_DurationHandler.Setup( m_pParams->fDuration, in_pSourceFXContext->GetNumLoops(), io_rFormat.uSampleRate ); 

    // Output format set to Mono native by default (input). Change to Stereo output.
    io_rFormat.uChannelMask = AK_SPEAKER_SETUP_STEREO;

    ...

    return AK_Success;
}

AK::IAkSourcePlugin::GetDuration()

This method is called by the sound engine after initialization time to determine the approximate duration of the source for the purpose of processing crossfades during sound transitions. The estimated duration of the source should be returned in milliseconds. If looping is applied with a finite number of iterations, the returned duration should correspond to the total duration, considering how many sound iterations will be played. When infinite looping is selected or when the duration of the source is unknown the returned duration should be zero. Note that the number of loop iterations retrieved through AK::IAkSourcePluginContext::GetNumLoops() when infinite looping is selected is always zero.

// Get the duration of the source in milliseconds.
AkTimeMs CAkDCOffset::GetDuration( ) const
{
    return m_DurationHandler.GetDuration() * 1000.f; 
}
Note.gif
Note: If RTPC parameters change the duration of the plug-in, the crossfade transition may not last the expected duration.
Note.gif
Note: The easiest way to handle most time elapsed management (including looping) for a source plug-in is to use the AkFXDurationHandler service as shown in the Sine plug-in example (Samples).

AK::IAkSourcePlugin::StopLooping()

This method is called by the sound engine when a break action is received. A break action is a smooth stop. The plug-in may implement a way to smoothly terminate playing in this function. Usually it should simply stop looping and play the release if there is one. The function must return AK_Success if it ignores the break command or if it handles it. If the plug-in does not handle it, then this function should return AK_Fail, which will cause the source to stop playing.

Stop playback after the current loop iteration AKRESULT CAkDCOffset::StopLooping() { m_DurationHandler.SetLooping( 1 ); // No longer looping. return AK_Success; }

Note.gif
Note: This function is optional. If you don't implement it, the source will ignore break commands and play normally until the end.

AK::IAkSourcePlugin::GetEnvelope()

This method is called to get an estimate of the normalized amplitude envelope value, between 0 and 1, that a source plugin will generate at the next call to Execute(). In the current version of Wwise, it is used for HDR processing, where the algorithm uses estimates of the envelope to attenuate softer sounds accordingly. This feature is optional: if you return 1 (default), the attenuation of softer sounds will be constant when your plugin is used under an HDR bus.

If you decide to implement it, ensure that it returns the normalized envelope value, that is, that you know the value of the peak of the waveform that you are about to generate, or that amplitude envelope and gain are decoupled so that you may return only the current value of the envelope.

AK::IAkSourcePlugin::Execute()

This method executes the source plug-in's audio signal processing algorithm and fills given audio output buffers (refer to Accessing Data Using AkAudioBuffer structure). Upon function entrance, the AkAudioBuffer::uValidFrames field of the output buffer will always be zero, meaning that there are no valid audio frames in the channel buffers. The AkAudioBuffer::MaxFrames() method returns the maximum number of audio sample frames that the source should fill. The plug-in determines how many output sample frames need to be produced, which may depend on parameter values such as duration. After DSP execution, the source plug-in must tell the audio pipeline how many sample frames were effectively produced by setting the AkAudioBuffer::uValidFrames field of the audio buffer accordingly.

Note.gif
Note: In general, the algorithm should always attempt to produce full buffers to avoid pipeline starvation.

The Execute() routine will be called as long as the source plug-in sets the eState field of the AkAudioBuffer structure to AK_DataReady. When AK_NoMoreData is returned, the plug-in will be terminated and will no longer be called by the audio pipeline. The current version of Wwise supports source plug-ins that output up to 6 channels (5.1 setup, see Channel Ordering).

Important Notes for Source Plugin Execution

  • Source plug-ins must always generate complete sample frames, that is, samples for each channel of the specified output format.
  • It is your responsibility to ensure that your plug-in checks parameters with RTPC support at runtime. You should implement accessor methods in the shared parameter interface for retrieving values from the parameter node and invoke them as often as required at runtime. For more information, refer to Communication Between Parameter Nodes and Plug-ins.. For most parameter values, checking once every buffer is sufficient. Look at the sample code examples to see how parameter values (for example, gains) can be ramped to avoid signal discontinuities (Samples).
  • When a plug-in is used in Wwise, parameter changes are sent down to the parameter node whether or not the parameter supports RTPCs. This allows the plugin to support runtime value changes on non-RTPC values if desired for Wwise usage. If you do not want your plugin to support this, you should make a copy of the parameter values at initialization time to ensure they remain the same throughout the plug-in's duration. Refer to CAkToneGen::Init for an example of this.
  • You should optimize processing performed in the AK::IAkSourcePlugin::Execute() method so it can be completed well within the time slice represented by a buffer. Failure to do so may result in source starvation for this and possibly other source pipelines. It can also have a significant impact on the sound engine's performance.

A DC offset plug-in Execute() function is provided below. Refer to CAkSrcSine::Execute() and the various tone generator DSP routines for more details.

// This example demonstrates a simple application which outputs a steady DC offset signal using an RTPC parameter.
void CAkDCOffset::Execute( AkAudioBuffer * io_pBuffer ) 
{   
    // Set new duration when it changes (e.g. RTPC parameter)
    m_DurationHandler.SetDuration( m_pParams->fDuration );
    
    // Determine how many sample frames to produce this execution and set uValidFrames and eState according to current state
    m_DurationHandler.ProduceBuffer( io_pBuffer ); 
    
    // Retrieve RTPC DC offset parameter
    AkReal32 fDCOffset = m_pParams->GetDCOffset( );
        
    // DC offset output DSP (supports any number of channels)
    for ( unsigned int i = 0; i < m_uNumChannels; ++i )
    {
        AkSampleType * pBufOut = io_pBuffer->GetChannel(i); // AkSampleType is platform specific (AkReal32 on software platforms and AkInt16 on the Wii and WiiU)
        AkUInt32 uFrameCount = io_pBuffer->uValidFrames;
        while ( uFrameCount-- )
        {
            *pBufOut++ = AK_FLOAT_TO_SAMPLETYPE( fDCOffset );       // DC-offset output, will convert normalized float to platform supported format.
        }
    }
}

AK::IAkSourcePlugin::Execute()

AK::IAkSourcePlugin::TimeSkip() is substituted to Execute() when a virtual voice is playing from elapsed time to allow the source plug-ins to keep updating their internal state (for example advance synthesis time) if desired. This can be used to simulate processing that would have taken place while avoiding most of the CPU hit of plug-in execution. Given the number of frames requested, adjust the number of frames that would have been produced by a call to Execute() in the io_uFrames parameter and return AK_DataReady or AK_NoMoreData, depending if there would be audio output or not at that point.

Returning AK_NotImplemented will trigger a normal execution of the voice (as if it was not virtual) thus not enabling the CPU savings of a proper from elapsed time behavior.

Note.gif
Note: Returning AK_NotImplemeted for a source plug-ins that support asynchronous processing (PlayStation3 platform only) will produce a 'resume' virtual voice behavior instead.

This example shows how to skip the processing of some frames when voice is virtual from elapsed time. AKRESULT CAkDCOffset::TimeSkip( AkUInt32 &io_uFrames ) { AkUInt16 uValidFrames = (AkUInt16)io_uFrames; AkUInt16 uMaxFrames = (AkUInt16)io_uFrames; AKRESULT eResult = m_DurationHandler.ProduceBuffer( uMaxFrames, uValidFrames ); io_uFrames = uValidFrames; return eResult; }

For more information, refer to the following sections: Wwise Sound Engine Plug-Ins Overview, Effect Plug-in Interface Implementation, Writing the Wwise Part of an Audio Plug-in