How to link parameters.
On this page
Basics of Linked Parameters
A "linked" parameter can be defined as any parameter whose state is somehow dependent on another parameter. Within this general definition, there are various different kinds of parameter linking:
- Linking behavior can operate one-way or parameters can be reciprocally linked
- Linking between parameters can be one-to-one or one-to-many
Basic considerations for parameter linking
Although the concept of parameter linking is simple, implementing linked parameter behavior that is intuitive and consistent can require careful design.
- Parameter interdependencies and constraints can become complex, especially when handling multiple sets of linked parameters.
- Linked parameters that update other parameters during playback can result in subtle timing inconsistencies
- Automated parameters may contain arbitrary and conflicting automation data
- A user may attempt to edit multiple linked parameters simultaneously during playback, e.g. using multiple encoders on a control surface
- A plug-in may contain dependency cycles between interdependent parameters. These cycles can cause undesired behavior that is difficult to debug, especially if it only occurs in certain circumstances such as when loading particular presets.
In all of these cases, a plug-in should provide consistent linked parameter behavior: every automated playback pass should be identical, parameters should never "fight" one another or trigger rapid and unexpected changes in other parameters, parameters should not become "stuck" in a particular state, etc.
Here comes trouble
Defining proper linked parameter behavior
A good way to approach parameter linking is to start with an understanding of exactly what behavior you desire.
Here are some behaviors that you probably don't want in your plug-in:
-
BAD: Parameters are only linked when edited from the plug-in GUI
Users may attempt to edit linked parameters from attached control surfaces or using the host's automation features. The parameters should behave the same way regardless of which method is used to edit them.
-
BAD: Parameters try to match all automation data
Automation data can be written arbitrarily: Pro Tools doesn't have any restrictions that a user with a pencil tool must draw inside the lines, or a user may attempt to edit multiple parameters on an attached control surface simultaneously. Any parameter that attempts to match both its own automation data and the automation data of another parameter, or any parameter that attempts to set another automatable parameter's state based on its own automation data, will lead to "fighting" during playback of non-conformant automation.
-
BAD: Automation data is only written to one lane at a time
One approach to parameter linking may be to only write automation data to a single parameter at a time. This could be the parameter that is currently being touched and edited, or it could be a dedicated "master" parameter within the linked group. While this approach can be used to solve some types of conflicts, it can still lead to unnecessarily complex or inconsistent behavior in certain situations: for example, arbitrary automation data can still be written to multiple parameters' automation lanes, or a user can choose to record automation for only one parameter in a set but can skip the "master" parameter. Furthermore, it is difficult if not impossible to properly handle parameters that can be dynamically linked or un-linked using this approach.
With those potential problems in mind, here is a description of how parameter linking should behave.
Correct behavior for linked parameters
Notes
- In this proposal (and throughout the rest of this page) the term "linker" will refer to a parameter that initiates a change and the term "linked" will refer to a dependent parameter that receives the change.
- The following discussion will focus on automatable parameters that are reciprocally linked. This case tends to be the most complex, with the greatest need for consistency of implementation.
During user-generated real-time edits (from the plug-in GUI or a control surface) both the linker and the linked parameters should be updated. Without this requirement, there would be no parameter linking. In order for this requirement to be enforced consistently the following behaviors must be maintained:
-
The linked parameter should not jump to a new value if the user attempts to edit both parameters simultaneously using a control surface.
-
To ensure proper automation playback, automation should be written to both the linker and the linked parameters.
When playing back automation, parameters should operate independently and should not attempt to force dependent parameters to a new state. This prevents fighting in the presence of incompatible automation and ensures deterministic automation playback with every playback pass. As above, there are some more subtle behaviors that must be maintained for this to work properly:
-
If the user begins a real-time edit during automation playback then the parameter linking behavior should resume as described above
-
If the plug-in's algorithm cannot support certain parameter configurations then its automatable parameters should be decoupled from the algorithm using a set of coefficients that is aware of the algorithm's constraints. In this way every combination of parameter states can map to a particular coefficient state, maintaining determinism, and incompatible parameter combinations can simply resolve to the "closest" match in the possible coefficient space during playback of edited parameter automation data.
-
Another, simpler approach for plug-ins that do not support arbitrary parameter configurations is to ensure that the problematic parameters are not automatable. Handling non-automatable parameter linking is much easier in general, so consider this approach if automation is not a requirement for some of your plug-in's parameters.
When handling preset changes and plug-in initialization, a similar approach should be taken as with plug-in automation playback. In these cases it is very unlikely that the plug-in's parameters will be left in an incompatible state and attempts at linking may result in unwanted update cycles between inter-dependent parameters or unnecessary coefficient churn. This latter concern can be a real problem for AAX DSP plug-ins that initialize internal algorithmic state based on initial coefficient data.
Compatibility caveat
This behavior was not possible under the RTAS/TDM format, and many RTAS and TDM plug-ins reverted to workarounds such as writing automation to only one parameter at a time and linking the parameters during playback. Therefore, plug-ins that previously supported linked automatable parameters under the RTAS/TDM format may not be able to both implement this recommended parameter linking behavior and maintain compatibility with automation in saved sessions.
Most of Avid's plug-ins that were available in the RTAS and TDM formats fall into this category and should not be used as examples of proper parameter linking behavior. Instead, use the SDK's DemoGain_LinkedParameters example plug-in as an example of proper linked parameter operation.
Linked Parameter Operation
As described above, the key rule for linked parameters is to link during real-time user edits only, and should operate the parameters independently (without linked behavior) during automation playback and preset restore. This rule will simplify many issues: it will prevent conflicts with automation data, avoid potentially strange behaviors when restoring presets, and more.
Here is how the system works WITH linked parameters, using code snippets from the DemoGain_LinkedParameters example plug-in:
User Editing
- User clicks on a parameter in the GUI or grabs a parameter on the controls surface. A TOUCH token should be sent at this point.
- The touched parameter status comes back to the plug-in. If the parameters are linked the other linked parameter should have a TOUCH token sent. This really should only be done for linked continuous parameters. This is done by overriding the AAX_CEffectParameters::UpdateParameterTouch() method.
- The user changes the parameter from the GUI or controls surface. A SET token should be sent at this point.
{
if ( inTouchState )
{
AAX_CParamID linkedControl = this->GetLinkedControl ( inParameterID );
if ( linkedControl )
{
this->TouchParameter ( linkedControl );
mLinkTouchMap.insert ( std::pair<std::string,std::string>( inParameterID, linkedControl ) );
}
}
[...]
}
const char * AAX_CParamID
Parameter identifier.
Definition: AAX.h:352
int32_t AAX_Result
Definition: AAX.h:337
uint8_t AAX_CBoolean
Cross-compiler boolean type used by AAX interfaces.
Definition: AAX.h:329
- The SET token goes into the system and comes back to the plugin via AAX_CEffectParameters::UpdateParameterNormalizedValue().
- If the parameter is linked then the other linked parameter should have its value set for its linked behaviour. The system knows this is a linked parameter so when the value comes back to the plug-in via UpdateParameterNormalizedValue() it will know not to perform linked behaviors on that value change. To determine if a parameter should set a linked parameter you check it with the AAX_CEffectParameters::IsParameterTouched() method.
- The plug-in updates its internal state and sends an UPDATE tokens for both parameters.
{
bool touched = this->IsParameterTouched ( inParameterID );
[...]
{
if ( type == eType_Pan )
this->SetParameterNormalizedValue( linkedControl, (1.0 - inValue) );
else if ( type == eType_Gain )
this->SetParameterNormalizedValue( linkedControl, inValue );
}
[...]
}
AAX_EUpdateSource
Source for values passed into UpdateParameterNormalizedValue().
Definition: AAX_Enums.h:1028
@ AAX_eUpdateSource_Unspecified
Parameter updates of unknown / unspecified origin, currently including all updates from control surfa...
Definition: AAX_Enums.h:1029
AAX_Result UpdateParameterNormalizedValue(AAX_CParamID iParameterID, double iValue, AAX_EUpdateSource iSource) AAX_OVERRIDE
Updates a single parameter's state to its current value.
- Repeat steps 2-4 while changing the parameter.
- The user lets go of the GUI or controls surface. A TOUCH token with the released state should be sent.
- The touched parameter status comes back to the plug-in. If the parameters were linked the other linked parameter should have a TOUCH token with the release status sent. This again is done by overriding the AAX_CEffectParameters::UpdateParameterTouch() method.
{
if ( inTouchState )
{
[...]
}
else
{
[...]
this->ReleaseParameter ( iter->second.c_str () );
[...]
}
}
@ AAX_SUCCESS
Definition: AAX_Errors.h:39
Automation Playback
- The SET token comes from the automation system and enters the plugin via UpdateParameterNormalizedValue().
- The plug-in will know this is not from the user editing therefore it will NOT set the other linked parameter. Remember ONLY LINK USER EDITING. That way there's no conflicts if the user edited the automation or if the order in which automation arrives at the plug-in changes.
- The plug-in updates its internal state and sends an UPDATE token.
- Repeat steps 1-2 while playing back automation.
Chunk Restoring
- Plug-in loads the chuck.
- The plug-in sets every parameters value.
- The SET tokens comes back to the plugin via UpdateParameterNormalizedValue().
- The plug-in will know this is not from the user editing therefore it will NOT set the other linked parameter. Remember ONLY LINK USER EDITING. Hopefully the result of this is that the contents of the chunk will be restored to its exact state.
- The plug-in updates its internal state and sends out UPDATE tokens.
Changing Tapers
One common use of linked parameters is to change the taper associated with a parameter. For changing tapers there are basically only a two rules you need to follow:
- When you're loading a new chunk you need to set the taper values first. If a parameter is what updates the taper then set that value first. That way when the value of a parameter is set from a chunk it wont change because of a taper change.
- Update the taper from the UpdateParameterNormalizedValue() method. If the new taper needs to change the value of the parameter you only do so if the user is editing the linked parameter. This still follows the ONLY LINK USER EDITING rule.
{
AAX_CParamID linkedControl = this->GetLinkedControl ( inParameterID );
if ( linkedControl )
{
if ( this->IsParameterLinkReady ( inParameterID, inSource ) )
this->SetParameterNormalizedValue( linkedControl, inValue );
}
return result;
}