AAX SDK  2.4.1
Avid Audio Extensions Development Kit
Getting Started with AAX

A brief introduction to AAX.

Contents

Welcome

Welcome to AAX! This guide is designed to introduce you to the fundamental concepts of AAX. By the end of this guide you will understand:

Quick Start

Use the steps below to get up and running quickly with an example plug-in from the AAX SDK.

AAX Design Overview

Architecture Philosophy

The purpose of the AAX architecture is to provide an easy-to-use framework for the development of high-performance audio plug-ins that can run on a variety of platforms, both present and future, with a high level of code re-use.

Design Attributes

AAX incorporates a flexible, decoupled component hierarchy, including:
  1. A relocatable C-style callback that performs the plug-in's real-time audio processing algorithm
  2. A collection of supporting C++ objects that manage the plug-in's data, GUI, and any other non-real-time information
  3. A "description" method that statically describes the plug-in's layout and compatibility requirements to the host.
This flexible design facilitates optimal performance and portability; AAX is 64-bit ready and is designed to work well in both host-based (Native), accelerated (DSP), and future (e.g. embedded) environments. Importantly, plug-ins running in each of these environments can use the same code base, and porting to new platforms should not require much more than a re-compile. To satisfy these portability requirements, AAX must be decoupled into three parts, the GUI, data model, and algorithm.

Component Structure

The core component structure of an AAX plug-in involves data model, GUI, description, and algorithm modules. The design involves a mixture of C++ objects (data model and GUI modules) and C-style callbacks (algorithm and description modules.)
The figure below shows basic object ownership and composition in an AAX plug-in. The purple components are provided as part of the AAX SDK while the other components are written as needed by the plug-in developer. The classes above the dotted line are pure virtual interfaces, while the classes below the dotted line are concrete implementations.
Core AAX interface design
Figure 1: Core AAX interface design.
As you can see, the plug-in's data model and GUI are written as C++ objects inherited from SDK interfaces, while its algorithm and Description methods are implemented as simple callbacks. This basic model may be expanded by attaching additional modules, such as the Host Processor module used by advanced non-real-time plug-ins. In this section, however, we will be considering only the four core interfaces and modules.

Algorithm

The most fundamental, and most important, component of any audio plug-in is its processing algorithm. Due to the design requirements of an AAX plug-in, this component must meet several constraints in order to be compatible with accelerated platforms:
  1. It must be possible to build the algorithm as a compatible component on a variety of platforms
  2. It must be possible for the host to re-locate the algorithm in memory without affecting the algorithm's state
  3. The algorithm must be separable from the rest of its plug-in, e.g. into a different memory space
  4. The algorithm must be as efficient as possible to call.
To satisfy these constraints, AAX uses a decoupled C-style callback function. State information within the function is obtained through the context, a custom data structure that contains everything from the "outside world" that is relevant to the algorithm. The context includes information such as audio buffers and coefficient packets, which are provided according to a static set of data routing definitions that we will describe further in the next chapter. The host is responsible for ensuring that this structure is up-to-date whenever the algorithm callback is entered.
See also
Real-time algorithm callback

Data Model

The AAX_IEffectParameters interface represents the data model portion of your plug-in. The AAX host interacts with your plug-in's data model via this interface, which includes methods that store and update of your plug-in's internal data.
In your plug-in's data model implementation, you will be responsible for creating the plug-in's parameters and defining how the plug-in will respond when these parameters are changed via control updates or preset loads. In order for information to be routed from the data model to the plug-in's algorithm, the parameters that are created here must be registered with the host in the plug-in's Description callback (see below).
The data model is composed with AAX_IController, an interface that provides access to the host. This interface provides a means of querying information from the host such as stem format or sample rate, and is also responsible for communication between the data model and the (decoupled) algorithm.
You will most likely inherit your custom data model's implementation from AAX_CEffectParameters, a default implementation of the AAX_IEffectParameters interface. This class provides basic functionality such as adding custom parameters, setting control values, restoring state, generating coefficients, etc., which you can override and customize as needed.
See also
Data model interface

GUI Interface

The AAX_IEffectGUI interface contains the plug-in's GUI methods. This interface is decoupled from the plug-in's data model, allowing AAX to support distributed architectures that incorporate remote GUIs.
The GUI is also composed with AAX_IController, from which references to the plug-in's other components, such as its data model interface, may be retrieved.
The AAX SDK includes a set of GUI extensions to help you get started implementing your plug-ins' GUIs. These extensions include both native drawing formats and suggested integrations with third-party graphics frameworks. Although the SDK does not include any actual graphics frameworks, these extensions provide examples of how you can incorporate your chosen GUI framework with AAX.
See also
GUI interface

Describe

A plug-in's Describe callback ties all the plug-in's components together and registers the plug-in with the host. In this callback, your plug-in defines a set of algorithm callbacks, data connections, and static plug-in properties
In order to route data to the plug-in's algorithm, Describe includes a description of the algorithm's context structure. This description involves a set of port definitions, which can be "hard-wired" to receive data from the host (such as audio buffers,) from the plug-in's data model (such as packets of coefficients,) or even from past calls to the algorithm (private, persistent algorithm state.)
Once these connections are made, Describe passes the host a populated description interface and returns. In the next section we will demonstrate how all these interfaces interact with one another by examining a sample plug-in.
See also
Description callback

Controller

There is one additional core component to any AAX plug-in, the Controller. The plug-in does not implement this component - rather, the Controller is an interface that provides access to various facilities provided by the host, as well as to the plug-in's other modules.

DemoGain Example

The AAX SDK includes a basic example plug-in named DemoGain. In this section we will examine this plug-in to show how the various core modules in an AAX plug-in interact with one another. We will focus in particular on how the DemoGain's "gain" parameter is defined, routed to the algorithm, and used to apply a gain effect to audio samples. In this way we will "visit" each of the core components in DemoGain.
For a description of all the example plug-ins included in the SDK, see the Example Plug-Ins page.
Note
To run DemoGain or other example plug-ins in Pro Tools 10 you must change the AAX_ePlugInCategory_Example category to AAX_ePlugInCategory_Dynamics in the plug-in's Describe function. The "example" category is not supported in Pro Tools 10.

AAX Plug-In Parameters

A good starting point for understanding a plug-in is to understand its parameters. In DemoGain, as in most AAX plug-ins, the plug-in's parameters define its data model state and the plug-in's parameters provide the fundamental connection between user interactions and audio processing.
The sections below will guide you through each of the main steps of parameter creation and connection, making use of the default implementation in AAX_CEffectParameters:
  1. Data Model: Create Your Parameter
    1. Create a new parameter object
    2. Register the parameter with the Packet Dispatcher
    3. Create an update callback that converts the raw parameter value into a packet of processed coefficients
  2. Algorithm: Add coefficients to the algorithm's context structure
    1. Create a new field for incoming coefficient packets
    2. Generate a field ID to reference the new member
    3. Add the new coefficient to the plug-in's algorithm code
  3. Describe: Connect the parameter throughout the plug-in
    1. Add a new Data Input Port to the algorithm via the component descriptor interface
    2. Add a connection between the parameter's packet ID and its coefficients' field ID
  4. GUI: Add a control
    1. Create a GUI widget that can update the parameter's state
    2. Add logic to notify the data model when the GUI is edited
    3. Add logic to update the GUI when the data model state changes

Data Model: Create Your Parameter

DemoGain's data model inherits its functionality from AAX_CEffectParameters (the default implementation of AAX_IEffectParameters). The DemoGain_Parameters.h and DemoGain_Parameters.cpp source files comprise the source code for DemoGain's data model.
During plug-in initialization, DemoGain_Parameters::EffectInit() creates the plug-in's custom parameters. A look inside of the .cpp file shows how this is done via the creation of a new AAX_CParameter for the gainParameter: the AAX_CParameter constructor is parametrized with a set of arguments that define the gain parameter's behavior, such as its default value (1.0f), name ("Gain"), taper behavior (linear), etc.
After creating the parameter objects, a series of packets are registered with the host via the inherited Packet Dispatcher, mPacketDispatcher, which is a helper object used by AAX_CEffectParameters to assist with the plug-in's packet management tasks.
mPacketDispatcher.RegisterPacket(
DemoGain_GainID,
eAlgPortID_CoefsGainIn,
this,
&DemoGain_Parameters::UpdatePacket_Gain);
Listing 1: Registration of DemoGain's "gain" packet handler
The last parameter in AAX_CPacketDispatcher::RegisterPacket() takes a reference to a packet handler callback. This method will be called by the Packet Dispatcher whenever new parameter values need to be converted into coefficients that can be used by the algorithm. In DemoGain, a reference to DemoGain_Parameters::UpdatePacket_Gain() is used for the gain parameter's coefficient packet.
As a developer, you will choose how this portion of your data model gets handled; you can choose to use the default handler method, which simply forwards the raw parameter values to the algorithm, or you can define a custom handler. You can also choose to bypass the Packet Dispatcher completely (see the DemoDist_GenCoef plug-in for an example of this.)
Although the handling of DemoGain's gain parameter is trivial, for the sake of demonstration this plug-in uses both Packet Dispatcher approaches in the registration of its bypass and gain parameters.

Algorithm: Add coefficients to the algorithm's context structure

Your plug-in's context is nothing more than a data structure of pointers that is registered with the host during Describe. These pointers function as ports where host-managed data may be delivered or retrieved.

Context definition

Looking under the "Component context definitions" section within DemoGain_Alg.h, you will find DemoGain's SDemoGain_Alg_Context context. This is DemoGain's context structure, and its sole purpose is to parametrize DemoGain's algorithm with a set of ports. Note the definition of a port for the gain coefficients that are created by DemoGain_Parameters::UpdatePacket_Gain() and another port for the bypass value that is forwarded via the default packet update handler.
int32_t * mCtrlBypassP;
SDemoGain_CoefsGain * mCoefsGainP;
Listing 2: Gain and bypass ports in the DemoGain algorithm's context structure
Once ports have been defined for the algorithm's coefficients and other algorithmic data, unique identifiers for each port in the context must be generated. This is accomplished through use of the AAX_FIELD_INDEX macro. The basic idea behind this macro is to generate IDs that the host can use to directly address the ports within their context:
, eAlgPortID_CoefsGainIn = AAX_FIELD_INDEX ( SDemoGain_Alg_Context , mCoef ),
, eAlgFieldID_AudioIn = AAX_FIELD_INDEX ( SDemoGain_Alg_Context , mInputPP ) )
#define AAX_FIELD_INDEX(aContextType, aMember)
Compute the index used to address a context field.
Definition: AAX.h:323
Listing 3: Some port ID definitions for the DemoGain algorithm's context structure
Now the context's definition is complete. So far these are just fields in a struct; the host doesn't yet know how to route packets from the data model to these ports. That information will come later, in the plug-in's description. Once this context is described to the host, the host will know to populate it and pass it to the processing function located in DemoGain_AlgProc.cpp.

Algorithm processing callback

void
DemoGain_AlgorithmProcessFunction (
SDemoGain_Alg_Context * const inInstancesBegin [],
const void * inInstancesEnd )
#define AAX_CALLBACK
Definition: AAX.h:285
Listing 4: The DemoGain algorithm's callback prototype
This is where the plug-in's audio buffer processing of the plug-in occurs. Note that this function is passed a pointer to an array of SDemoGain_Alg_Contexts. Each of these represents the state of a particular instance of DemoGain, and contains pointers to the applicable coefficient and audio buffer data.
Using the SDemoGain_Alg_Context array, instance-specific information and audio buffers are easily retrieved for processing. DemoGain accomplishes this by first iterating over each plug-in instance, then over each channel, and finally over each sample, at which point the gain coefficient is applied to each sample in the input audio buffer and the sample data is copied to the output audio buffer:
// --------- Iterate over plug -in instances ---------//
for (SDemoGain_Alg_Context * const * walk = inInstancesBegin ; walk <
inInstancesEnd ; ++ walk )
{
.
.
.
// --------- Run processing loop over each input channel ---------//
//
for (int ch = 0; ch < kNumChannelsIn ; ch ++)
{
// --------- Run processing loop over each sample ---------//
//
for (int t = 0; t < kAudioWindowSize ; t++)
{
.
.
.
pdO [t] = gain * pdI [t];
.
.
.
} // Go to the next sample
} // Go to next channel
} // End instance-iteration loop
Listing 5: Iterative loops in the DemoGain algorithm

Describe: Connect the parameter throughout the plug-in

As mentioned before, Describe provides a static description of all communication pathways between a plug-in's algorithm, host, and data model. In addition, various effect properties are defined that help the host determine how to handle the plug-in.
Communication paths between the various plug-in components are described as connections between source and destination ports. In order for these communication paths to be created, the algorithm must first define some destination ports by actually registering its previously defined context fields as communication destinations. DemoGain does this for its gain parameter through the following call to AAX_IComponentDescriptor::AddDataInPort() in DemoGain_Describe.cpp's DescribeAlgorithmComponent() method:
compDesc = outDescriptor->NewComponentDescriptor ();
err = compDesc->AddDataInPort (
eAlgPortID_CoefsGainIn ,
sizeof (SDemoGain_CoefsGain) );
Description interface for an AAX plug-in component.
Definition: AAX_IComponentDescriptor.h:47
virtual AAX_Result AddDataInPort(AAX_CFieldIndex inFieldIndex, uint32_t inPacketSize, AAX_EDataInPortType inPortType=AAX_eDataInPortType_Buffered)=0
Adds a custom data port to the algorithm context.
Listing 6: Adding a data port to DemoGain's algorithm component descriptor
This registration process is required for both custom coefficients (Gain) and for data that all plug-ins need such as audio input and output fields.
That completes the connection, and now the plug-in is fully wired to receive parameter updates, convert raw parameter values to algorithmic coefficients, pack these coefficients into a packet, post the packet to the host for routing, receive the updated packet in the list of context structures that the host provides when calling the algorithm callback, and apply the updated coefficient data in the appropriate context structure to the plug-in's audio data. Whew!

GUI: Add a control

Although the specific steps for adding a GUI control to edit a plug-in parameter will vary depending on the GUI framework you choose, there are a few basic design principles that should always be followed.
The basic DemoGain plug-in does not include any GUI implementation. For practical GUI implementation examples, open the DemoGain_GUIExtensions sample plug-ins. DemoGain_Cocoa provides an example of a custom plug-in GUI using the native OS X SDK, while DemoGain_Win32 uses native Windows APIs. The other examples in this folder require common third-party libraries.

Control edits vs. parameter updates

The most important principle for AAX GUI design is that an edit in a plug-in's GUI should never directly set the state of the associated parameter or parameters. This is because there may be other controller clients of the plug-in's data model which will need to be notified of the edit, or which may need to override edits from the GUI.
For detailed information about the sequence of events and GUI responsibilities during a parameter update, see Parameter updates

Notifying the host of GUI events

Some GUI events must be handled by the host rather than by the plug-in. For example, in Pro Tools a user should be able to display a pop-up menu for controlling automation information by command-option-control-clicking (Mac) or alt-ctl-right clicking (Windows) a control. These events, and other direct communication between the GUI and the "container" in which the host creates the plug-in view, is accomplished via the AAX_IViewContainer interface. Be sure to always call the handler methods in this interface before handling a mouse event within the plug-in GUI, in order to maintain the expected host behavior.

Next Steps

Now that you have a basic understanding of AAX, head back to the front page and continue reading through the suggested documentation. Or dig in to the sample plug-ins to see how specific features are supported in AAX.
Warning
Your AAX plug-ins will not be compatible with shipping versions of Pro Tools until they are digitally signed using tools provided by PACE Anti-Piracy, Inc. As an AAX developer you can receive these tools free of charge. Read the Digital signature section of the Pro Tools Guide to learn about the digital signing requirements for compatibility with Pro Tools.
Collaboration diagram for Getting Started with AAX: