AAX SDK  2.4.1
Avid Audio Extensions Development Kit
Real-time algorithm callback

A plug-in's audio processing core.

On this page

Algorithm definition

Algorithm processing in AAX plug-ins is handled via a C-style algorithm processing callback (see code below.) Each Effect variation in a plug-in must register an algorithm entrypoint in the plug-in description, and the host will call this entrypoint to render a buffer of audio samples.
void AAX_CALLBACK MyPlugIn_AlgorithmProcessFunction(
SMyPlugIn_Alg_Context * const inInstancesBegin [],
const void * inInstancesEnd)
{
//
// Processing code...
//
}
#define AAX_CALLBACK
Definition: AAX.h:285

Algorithm memory management

This callback pattern is designed such that plug-in algorithms may be easily loaded in remote memory spaces on a variety of devices and quickly re-compiled for different operating environments without significant changes to the code, and this design goal informs the algorithm's memory management techniques.
When the AAX host calls a plug-in's algorithm callback, it provides a block of memory describing the state of the plug-in. This block of memory, known as the algorithm's context, can be thought of as the algorithm's interface to the outside world: when another part of the plug-in interacts with the algorithm, it does so by posting information to the algorithm's context.
//==============================================================================
// Component context definitions
//==============================================================================
// Context structure
struct SDemoDist_Alg_Context
{
int32_t * mCtrlBypassP; // Coefficient message destination
float * mCtrlMixP; // Coefficient message destination
float * mCtrlInpGP; // Coefficient message destination
float * mCtrlOutGP; // Coefficient message destination
SDemoDist_DistCoefs * mCoefsDistP; // Coefficient message destination
SDemoDist_FiltCoefs * mCoefsFiltP; // Coefficient message destination
CSimpleBiquad * mBiquads; // Private data
float* * mInputPP; // Audio signal input
float* * mOutputPP; // Audio signal output
float* * mMeterTapsPP; // Meter signal output
};
It is important to note that, in most circumstances, algorithm callbacks do not own their own memory. The algorithm and its memory is managed entirely by the host or shell environment, and relies on the host-provided context structure for all state information.
If persistent memory is required, algorithms can register for block(s) of persistent state date via the AAX_IComponentDescriptor::AddPrivateData() API (as in SDemoDist_Alg_Context::mBiquads above.) A plug-in may store state data in the resulting "private data" context fields and this data will be restored by the host when the algorithm is next called. See the Persistent algorithm memory section below for more information.

Communicating with the algorithm

Plug-ins communicate with their algorithms via a buffered, host-managed message system. The host guarantees that messages posted to this system will be delivered to the applicable context field and that the algorithm's context is up to date every time the component is entered.
This system utilizes a static data routing scheme that is defined in the plug-in's describe method. Once the routing scheme has been defined, the plug-in may post packets of data to its algorithm using AAX_IController::PostPacket().
In order to reference the fields in its algorithm's context, the plug-in's host-side code uses unique identifiers generated with the AAX_FIELD_INDEX macro:
enum EDemoDist_Alg_PortID
{
eAlgPortID_BypassIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCtrlBypassP)
,eAlgPortID_MixIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCtrlMixP)
,eAlgPortID_InpGIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCtrlInpGP)
,eAlgPortID_OutGIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCtrlOutGP)
,eAlgPortID_CoefsDistIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCoefsDistP)
,eAlgPortID_CoefsFilterIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mCoefsFiltP)
,eAlgFieldID_Biquads = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mBiquads)
,eDemoDist_AlgFieldID_AudioIn = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mInputPP)
,eDemoDist_AlgFieldID_AudioOut = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mOutputPP)
,eAlgFieldID_MeterTaps = AAX_FIELD_INDEX (SDemoDist_Alg_Context, mMeterTapsPP)
};
#define AAX_FIELD_INDEX(aContextType, aMember)
Compute the index used to address a context field.
Definition: AAX.h:323
See Description callback for more information about registering context fields and defining a plug-in's message routing scheme.

Algorithm initialization

The following events occur before the AAX host begins calling a plug-in's algorithm:

Private data initialization

To initialize an algorithm's private data blocks, AAX_IEffectParameters::ResetFieldData() is called on the host for each block in the algorithm. The host uses this method to acquire a default initialized memory block for each private data port, which is then copied into the algorithm's memory pool and provided to its context.
The default implementation of this method in AAX_CEffectParameters will initialize the data to zero.
See also
Persistent algorithm memory

Optional initialization callback

If any additional initialization or de-initialization steps are required for proper operation of the algorithm, an optional initialization routine may be registered and associated with the algorithm's processing callback. This initialization routine will be called in the same device / memory space as the algorithm's processing context. The initialization callback is provided with the algorithm's default context and is called both before every new instance of the Effect begins its algorithm render callbacks and before every instance is destroyed.
This initialization routine is provided in Describe as an argument to the platform's AddProcessProc registration method:
Host Compatibility Notes:
As of Pro Tools 10.2.1 an algorithm's initialization calllback routine will have up to 5 seconds to execute.
See also
AAX_CInstanceInitProc

Algorithm processing

Once the algorithm has been initialized and processing begins, the algorithm function is called regularly by the host audio engine. The algorithm may read the context data provided by the host and is responsible for writing data to all of the samples in its output buffers each time it is executed.
Note
The data in an algorithm's output buffers is not initialized before the algorithm is called, thus the algorithm must always write data into all output samples. This is to ensure equivalent behavior between all platforms, some of which do not have the resource budget to pre-initialize output data buffers.

Persistent algorithm memory

An AAX plug-in algorithm may contain one or more private data ports in its context. These are the only context fields in which an algorithm may store persistant state data.

Private memory characteristics

Each private data port is a pointer to a preallocated block of memory. The size of each port is defined during Describe when the port is registered. On DSP systems, the plug-in may request that the data block be placed in the chip's external memory.
Once private data is allocated by the plug-in host or DSP shell, it will not be relocated or re-allocated until the algorithm is destroyed (see Optional initialization callback)

Private data port registration

Private data ports are registered during Describe via AAX_IComponentDescriptor::AddPrivateData(). This method defines the size of the data block that will be allocated as well as an initialization callback with format AAX_CInitPrivateDataProc.

Private data initialization

AAX_IEffectParameters::ResetFieldData() is called on the host for both Native and DSP plug-ins. For DSP plug-ins, the initialized data block is copied to the DSP by the AAX host following the initialization callback. The initialization callbacks for a plug-in's private data blocks are called after all host modules have been initialized and before the algorithm's optional initialization callback.
See also
Algorithm initialization

Private data communication

It is possible to transfer data to and from the algorithm's private data blocks using the AAX_IPrivateDataAccess interface, which is available in a TimerWakeup context through the AAX_IEffectDirectData interface. For more information about this API, see auxinterface_directdata_privatedataaccess.

Example algorithm callback

As a final example, the code below describes a simple audio processing component. The component's context contains one message pointer to receive incoming "gain" parameter values, as well as one audio data input, "pdI", and one audio data output, "pdO". Additionally there is a message pointer to receive "bypass" on/off values. The host calls the component each time a new input sample buffer must be processed, and each time the component is called the host ensures that all context fields are up-to-date.
void AAX_CALLBACK MyPlugIn_AlgorithmProcessFunction(
SMyPlugIn_Alg_Context * const inInstancesBegin [],
const void * inInstancesEnd)
{
// Get a pointer to the beginning of the memory block table
SMyPlugIn_Alg_Context* AAX_RESTRICT instance = inInstancesBegin [0];
//--------- Iterate over plug-in instances ---------//
for (SMyPlugIn_Alg_Context * const * walk = inInstancesBegin; walk < inInstancesEnd; ++walk)
{
instance = *walk;
//--------- Retrieve instance-specific information ---------//
//
const SMyPlugIn_CoefsGain* const AAX_RESTRICT coefsGainP = instance->mCoefsGainP; // Input (const)
const int32_t bypass = *instance->mCtrlBypassP;
const float gain = coefsGainP->mGain;
//--------- Run processing loop over each input channel ---------//
//
for (int ch = 0; ch < kNumChannelsIn; ch++) // Iterate over all input channels
{
//--------- Run processing loop over each sample ---------//
//
for (int t = 0; t < kAudioWindowSize; t++)
{
float* const AAX_RESTRICT pdI = instance->mInputPP [ch];
float* const AAX_RESTRICT pdO = instance->mOutputPP [ch];
if ( pdI && pdO )
{
pdO [t] = gain * pdI [t];
if (bypass) { pdO [t] = pdI [t]; }
}
} // Go to the next sample
} // Go to next channel
} // End instance-iteration loop
}

Port Types and Behavior

In this section, we will examine the various kinds of ports that can be used by the algorithm component in an AAX plug-in:
  1. Standard message input
  2. Internal state storage
  3. Metering output
  4. Environment variable retrieval
  5. Other functionality enhancement

Standard message input

Most ports will function as pointers to incoming data. This data can have any type. For example, an algorithm's context may include a port of type float* to receive incoming float data and another port of type SMyCustomStructure* to receive incoming SMyCustomStructure data.
Like all registered context fields, input ports are managed by the hosting environment such that they always point to the most recently received data at the time that the algorithm callback is entered. The algorithm may not store or alter data in a standard message input port: this data is available as read-only input. If data is stored in the space allocated for the port's data then the result will be undefined behavior.
To define a standard message input port, a plug-in should call AAX_IComponentDescriptor::AddDataInPort().

Internal state storage

Most plug-ins require local data to be accessible to their algorithms. These may be static data, such as lookup tables, or dynamic data, such as coefficient smoothing history or delay lines. In the DemoDist sample plug-in, SDemoDist_Alg_Context::mBiquads is an example of this type of port: it is not modified by any other component and DemoDist_AlgorithmProcessFunction() relies on the mBiquads data persisting between processing calls.
A component that has registered a private data field is given access to a block of private data. Although the memory in this block will be allocated by the host, its data is fully owned by the component. Because this data is considered private to its parent component, other components cannot overwrite or target this data. Plug-ins that need to transmit data directly between their algorithms' private data ports and their other modules may use the AAX_IEffectDirectData interface, which provides an API for reading from or writing to this data from outside of the algorithm callback.
The plug-in's data model includes an initialization function that is called by the AAX host at the time of plug-in instantiation and "reset" events. This initialization method is called on the host for both Native and DSP plug-ins. Since this method is part of the plug-in's data model, it has direct access to plug-in state information.

Metering output

Plug-in metering ports are populated with an array of float values, or 'taps'. One tap is provided per plug-in meter. The algorithm writes per-buffer peak values to this port and the AAX host applies standardized ballistics to these values. Both raw and processed meter values are available to the plug-in's GUI.

Environment variable retrieval

Another use of ports is to receive data from the AAX host describing the execution environment. For example, an algorithm may include a port to receive the number of samples in its processing window or the sample rate. These services are provided automatically by the host once the component registers ports for them.

Other functionality enhancement

An algorithm component may use ports to gain additional functionality that is provided by the host. For example, an algorithm that will be compiled for accelerated environments may take advantage of the TI chip's Direct Memory Access functionality by registering a DMA port. The host will then allow this port to access memory directly using AAX's DMA APIs.

Additional Information

For information about optional features for the algorithm processing callback, see the following Additional AAX features documentation:
Collaboration diagram for Real-time algorithm callback: