Main Content

Tips and Tricks for Plugin Authoring

To author your algorithm as an audio plugin, you must conform to the audio plugin API. When authoring audio plugins in the MATLAB® environment, keep these common pitfalls and best practices in mind.

To learn more about audio plugins in general, see Audio Plugins in MATLAB.

Avoid Disrupting the Event Queue in MATLAB

When the Audio Test Bench runs an audio plugin, it sequentially:

  1. Calls the reset method

  2. Sets tunable properties associated with parameters

  3. Calls the process method

While running, the Audio Test Bench calls in a loop the process method and then the set methods for tuned properties. The plugin API does not specify the order that the tuned properties are set.

It is possible to disrupt the normal methods timing by interrupting the event queue. Common ways to accidentally interrupt the event queue include using a plot or drawnow function.

Note

plot and drawnow are only available in the MATLAB environment. plot and drawnow cannot be included in generated plugins. See Separate Code for Features Not Supported for Plugin Generation for more information.

In the following code snippet, the gain applied to the left and right channels is not the same if the associated Gain parameter is tuned during the call to process:

...
L = plugin.Gain*in(:,1);
drawnow
R = plugin.Gain*in(:,2);
out = [L,R];
...

 See Full Code

The author interrupts the event queue in the code snippet, causing the set methods of properties associated with parameters to be called while the process method is in the middle of execution.

Depending on your processing algorithm, interrupting the event queue can lead to inconsistent and buggy behavior. Also, the set method might not be explicit, which can make the issue difficult to track down. Possible fixes for the problem of event queue disruption include saving properties to local variables, and moving the queue disruption to the beginning or end of the process method.

Save Properties to Local Variables

You can save tunable property values to local variables at the start of your processing. This technique ensures that the values used during the process method are not updated within a single call to process. Because accessing the value of a local variable is cheaper than accessing the value of a property, saving properties to local variables that are accessed multiple times is a best practice.

...
gain = plugin.Gain;
L = gain*in(:,1);
drawnow
R = gain*in(:,2);
out = [L,R];
...

 See Full Code

Move Queue Disruption to Bottom or Top of Process Method

You can move the disruption to the event queue to the bottom or top of the process method. This technique ensures that property values are not updated in the middle of the call.

...
L = plugin.Gain*in(:,1);
R = plugin.Gain*in(:,2);
out = [L,R];
drawnow
...

 See Full Code

Separate Code for Features Not Supported for Plugin Generation

The MATLAB environment offers functionality not supported for plugin generation. You can mark code to ignore during plugin generation by placing it inside a conditional statement by using coder.target (MATLAB Coder).

...
    if coder.target('MATLAB')
    ...
    end
...
If you generate the plugin using generateAudioPlugin, code inside the statement if coder.target('MATLAB') is ignored.

For example, timescope is not enabled for code generation. If you run the following plugin in MATLAB, you can use the visualize function to open a time scope that plots the input and output power per frame.

 See Full Example Code

Implement Reset Correctly

A common error in audio plugin authoring is misusing the reset method. Valid uses of the reset method include:

  • Clearing state

  • Passing down calls to reset to component objects

  • Updating properties which depend on sample rate

Invalid use of the reset method includes setting the value of any properties associated with parameters. Do not use your reset method to set properties associated with parameters to their initial conditions. Directly setting a property associated with a parameter causes the property to be out of sync with the parameter. For example, the following plugin is an example of incorrect use of the reset method.

classdef badReset < audioPlugin
    properties
        Gain = 1;
    end
    properties (Constant)
        PluginInterface = audioPluginInterface(audioPluginParameter('Gain'));
    end
    methods
        function out = process(plugin,in)
            out = in*plugin.Gain;
        end
        function reset(plugin) % <-- Incorrect use of reset method.
            plugin.Gain = 1;   % <-- Never set values of a property that is
        end                    %     associated with a plugin parameter.
    end
end

Implement Plugin Composition Correctly

If your plugin is composed of other plugins, then you must pass down the sample rate and calls to reset to the component plugins. Call setSampleRate in the reset method to pass down the sample rate to the component plugins. To tune parameters of the component plugins, create an audio plugin interface in the composite plugin for tunable parameters of the component plugins. Then pass down the values in the set methods for the associated properties. The following is an example of plugin composition that was constructed using best practices.

 Plugin Composition Using Basic Plugins

Plugin composition using System objects has these key differences from plugin composition using basic plugins.

  • Immediately call setup on your component System object™ after it is constructed. Construction and setup of the component object occurs inside the constructor of the composite plugin.

  • If your component System object requires sample rate information, then it has a sample rate property. Set the sample rate property in the reset method.

 Plugin Composition Using System Objects

Address "A set method for a non-Dependent property should not access another property" Warning in Plugin

It is recommended that you suppress the warning when authoring audio plugins.

The following code snippet follows the plugin authoring best practice for processing changes in parameter property Cutoff.

classdef highpassFilter < audioPlugin
...
    properties (Constant)
        PluginInterface = audioPluginInterface( ...
            audioPluginParameter('Cutoff', ...
            'Label','Hz',...
            'Mapping',{'log',20,2000}));
    end
    methods
        function y = process(plugin,x)
            [y,plugin.State] = filter(plugin.B,plugin.A,x,plugin.State);
        end

        function set.Cutoff(plugin,val)
            plugin.Cutoff = val;
            [plugin.B,plugin.A] = highpassCoeffs(plugin,val,getSampleRate(plugin)); % <<<< warning occurs here
        end
    end
...
end

 See Full Code Example

The highpassCoeffs function might be expensive, and should be called only when necessary. You do not want to call highpassCoeffs in the process method, which runs in the real-time audio processing loop. The logical place to call highpassCoeffs is in set.Cutoff. However, mlint shows a warning for this practice. The warning is intended to help you avoid initialization order issues when saving and loading classes. See Avoid Property Initialization Order Dependency for more details. The solution recommended by the warning is to create a dependent property with a get method and compute the value there. However, following the recommendation complicates the design and pushes the computation back into the real-time processing method, which you are trying to avoid.

You might also incur the warning when correctly implementing plugin composition. For an example of a correct implementation of composition, see Implement Plugin Composition Correctly.

Use System Object That Does Not Support Variable-Size Signals

The audio plugin API requires audio plugins to support variable-size inputs and outputs. For a partial list of System objects that support variable-size signals, see Variable-Size Signal Support DSP System Objects. You might encounter issues if you attempt to use objects that do not support variable-size signals in your plugin.

For example, dsp.AnalyticSignal does not support variable-size signals. The BrokenAnalyticSignalTransformer plugin uses a dsp.AnalyticSignal object incorrectly and fails the validateAudioPlugin test bench:

validateAudioPlugin BrokenAnalyticSignalTransformer
Checking plug-in class 'BrokenAnalyticSignalTransformer'... passed.
Generating testbench file 'testbench_BrokenAnalyticSignalTransformer.m'... done.
Running testbench... 
Error using dsp.AnalyticSignal/parenReference
Changing the size on input 1 is not allowed without first calling the release() method.

Error in BrokenAnalyticSignalTransformer/process (line 13)
                analyticSignal = plugin.Transformer(in);

Error in testbench_BrokenAnalyticSignalTransformer (line 61)
        o1 = process(plugin, in(:,1));

Error in validateAudioPlugin

 See BrokenAnalyticSignalTransformer Code

If you want to use the functionality of a System object that does not support variable-size signals, you can buffer the input and output of the System object, or always call the object with one sample.

Always Call the Object with One Sample

You can create a loop around your call to an object. The loop iterates for the number of samples in your variable frame size. The call to the object inside the loop is always a single sample.

 See Full Code Example

Note

Depending on your implementation and the particular object, calling an object sample by sample in a loop might result in significant computational cost.

Buffer Input and Output of Object

You can buffer the input to your object to a consistent frame size, and then buffer the output of your object back to the original frame size. The dsp.AsyncBuffer System object is well-suited for this task.

 See Full Code Example

Note

Use of the asynchronous buffering object forces a minimum latency of your specified frame size.

Using Enumeration Parameter Mapping

It is often useful to associate a property with a set of strings or character vectors. However, restrictions on plugin generation require cached values, such as property values, to have a static size. To work around this issue, you can use a separate enumeration class that maps the strings to the enumerations, as described in the audioPluginParameter documentation.

Alternatively, if you want to avoid writing an enumeration class and keep all your code in one file, you can use a dependent property to map your parameter names to a set of values. In this scenario, you map your enumeration value to a value that you can cache.

 See Full Code Example

Related Topics