Wwise SDK 2019.2.14
First, we need to create a new plug-in project with wp.py tools. See Creating a new Plug-in for more details about
We will target the Authoring platform on Windows, so let's call
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.
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
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.
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.
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 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.cpp in the SoundEnginePlugin folder to reflect our property changes.
LowpassFXParams.h, update the parameter IDs and the name of the parameter in the
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:
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
With this data, all we have to do is to increase
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.
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
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.