Version

menu_open
Wwise SDK 2023.1.8
Example: Developing a Lowpass Filter Plug-in

This topic provides an example of Community plug-in development from start to finish. In this case, we will develop a lowpass filter plug-in.

Creating a New Project

First, we need to create a new plug-in project with wp.py tools. See Creating Audio Plug-ins for more details about new arguments.

We will target the Authoring platform on Windows, so let's call premake:

Solutions have been created for building the SoundEngine and the Authoring (WwisePlugin) parts.

We can now build our plug-in and confirm that it loads in Wwise.

Implementing the Filtering Process

Now, we want to add some processing to make our plug-in a little bit more useful. We will implement a simple first order lowpass filter, using this equation:

y[n] = x[n] + (y[n-1] - x[n]) * coeff

where coeff is a floating-point value between 0 and 1.

First, let's create a couple of variables in SoundEnginePlugin/LowpassFX.h to hold our filter's data.

The variable m_coeff is our filter coefficient as a floating-point value, it will be used for all sound channels. The vector m_previousOutput will hold the last output value of all channels, mandatory to compute the next values of the filter.

To implement the filtering effect, all we have to do is to initialize the coefficient variable, adjust the size of the vector according to the number of channels and then process every sample with the previous formula.

In SoundEnginePlugin/LowpassFX.cpp:

Using an RTPC Parameter to Control the Filter's Frequency

At this point, our filter is pretty boring because there is no way to interact with it. The next step is to bind an RTPC parameter to the filter's frequency so that we can change its value in real time. There are four changes to make to allow our plug-in to use an RTPC parameter.

First, we must add its definition in WwisePlugin/Lowpass.xml. There is already a parameter skeleton, named "PlaceHolder", in the plug-in template. We will use it to define a "Frequency" parameter. In WwisePlugin/Lowpass.xml, replace the placeholder property with this:

Second, we need to update LowpassFXParams.h and LowpassFXParams.cpp in the SoundEnginePlugin folder to reflect our property changes.

In LowpassFXParams.h, update the parameter IDs and the name of the parameter in the LowpassRTPCParams structure.

Update LowpassFXParams.cpp as well:

Third, in the WwisePlugin folder, we need to update the Lowpass::GetBankParameters function to write the "Frequency" parameter in the bank:

Finally, in our processing loop, we want to use the current frequency to compute the filter's coefficient with this formula:

coeff = exp(-2 * pi * f / sr)

We need to retrieve the current sampling rate,

include some math symbols:

and compute the filter coefficient:

Interpolating Parameter Values

It is often not enough to update a processing parameter once per buffer size (the number of samples in an audio buffer channel, usually between 64 and 2048). Especially if this parameter affects the frequency or the gain of the processing; updating the value too slowly can produce zipper noise or clicks in the output sound.

A simple solution to this problem is to linearly interpolate the value over the whole buffer. Here is how we can do this for our frequency parameter.

Just before computing a new frame of audio samples, i.e., at the top of the Execute function in LowpassFX.cpp, we will check if the frequency parameter has changed. To do so, we just ask the AkFXParameterChangeHandler object in the LowpassFXParams class. If the frequency has changed, we compute the variables of the ramp:

Note: The member variable uValidFrames of the AkAudioBuffer object represents the number of valid samples per channel contained in the buffer.

With this data, all we have to do is to increase coeffBegin by coeffStep for each sample of the frame. We need to do this for each channel of the in/out buffer.

Now that we have a basic functional plug-in implementing a simple lowpass filter with real-time control over the cutoff frequency, let's talk about design concerns.

Encapsulating the Processing

At this point, all our signal processing logic is written inside the plug-in main class. This is not a good design pattern for many reasons:

  • It's bloating our main plug-in class, and it will quickly become worse as we add new processing to build a complex effect.
  • It will be hard to reuse our filter if we need it for another plug-in, and, especially with this kind of basic processing unit, it's going to happen!
  • It does not respect the single responsibility principle.

Let's refactor our code to encapsulate the filter processing in its own class. Create a file FirstOrderLowpass.h in the SoundEnginePlugin folder with this defintion:

And add the implementation in a file called FirstOrderLowpass.cpp:

Then, all we have to do in our main plug-in class is to create a vector of FirstOrderLowpass objects (one per audio channel), call their Setup function and start using them.

AkSampleType * GetChannel(AkUInt32 in_uIndex)
Definition: AkCommonDefs.h:576
uint16_t AkUInt16
Unsigned 16-bit integer.
Audiokinetic namespace.
Interface used to write data during sound bank generation.
AkForceInline AkUInt32 NumChannels() const
Get the number of channels.
Definition: AkCommonDefs.h:492
AKRESULT
Standard function call result.
Definition: AkTypes.h:131
AKSOUNDENGINE_API AKRESULT Init(const AkCommSettings &in_settings)
bool WriteReal32(float in_value)
Writes a 32-bit, single-precision floating point value.
float AkReal32
32-bit floating point
AkUInt16 uValidFrames
Number of valid sample frames in the audio buffer.
Definition: AkCommonDefs.h:657
#define READBANKDATA(_Type, _Ptr, _Size)
Read and return bank data of a given type, incrementing running pointer and decrementing block size f...
uint32_t AkUInt32
Unsigned 32-bit integer.
AkInt16 AkPluginParamID
Source or effect plug-in parameter ID.
Definition: AkTypes.h:66
AkForceInline AkUInt32 GetNumChannels() const
Definition: AkCommonDefs.h:75
Defines the parameters of an audio buffer format.
Definition: AkCommonDefs.h:63
AkUInt32 uSampleRate
Number of samples per second.
Definition: AkCommonDefs.h:64
#define AK_RESTRICT
Refers to the __restrict compilation flag available on some platforms.
Definition: AkTypes.h:45

Was this page helpful?

Need Support?

Questions? Problems? Need more info? Contact us, and we can help!

Visit our Support page

Tell us about your project. We're here to help.

Register your project and we'll help you get started with no strings attached!

Get started with Wwise