HEOLINY JUNG
AUDIO ENGINEER - PROGRAMMER - PRODUCER

May 6, 2021

The Oli EQ 3


Oli EQ 3 Screenshot 1


Developing a parametric EQ audio plug-in for Wwise

The Oli EQ 3 is a simple yet effective 1-band parametric EQ plug-in for use in the game audio middleware Wwise, completed for a Game Development class at Indiana University.

The majority of the programming was done in C++ with Visual Studio, and the main DSP algorithm was derived from an implementation provided in this paper.


Adaptation and Interpretation

When I first decided to start this project, my aim was to develop an audio plug-in that I could implement and instatiate inside Wwise. I decided to move towards a parametric EQ, as it was on the simpler side but required a bit more attention than the average digital signal processing algorithm.

My first experiments were centered around the Wwise SDK Developing a Lowpass Filter tutorial. This guide was incredibly helpful, but also presented some challenges mostly due to the documentation layout.

Basic EQ forms

The tutorial leads you through three main processing steps:

    1. Construct a new plugin
    python "%WWISEROOT%/Scripts/Build/Plugins/wp.py" new --effect -a author_name -n PluginName 
        -t "Full Plugin Name" -d "Plugin Description"
    2. Build the plugin
    python "%WWISEROOT%/Scripts/Build/Plugins/wp.py" build -c Release -x x64 -t vc160 Authoring
    3. Package and bundle
    python "%WWISEROOT%/Scripts/Build/Plugins/wp.py" package ... --version=YEAR.X.X.XXXX
    python "%WWISEROOT%/Scripts/Build/Plugins/wp.py" generate-bundle --version=YEAR.X.X.XXXX

Step 1 constructs various Visual Studio solutions and C++ scripts that you can modify to complete your implementation. Step 2 builds the project for a version of your choice - Debug, Release, or Profile - as indicated by the "-c" parameter. Step 3 packages all the files into deliverables and generates a .json file that can be read by the Wwise Launcher for easy installation into your local Wwise libraries.

Following the documentation, I kept ending up with the same error. I could get it installed into Wwise but the functionality was not making the journey. The only custom parameter in the plug-in should be called "Frequency". Instead it would only show "Placeholder", which was the parameter that the Wwise SDK implements by default on new plug-in builds.

Oli EQ 3 Placeholder Error

Modifying "Placeholder" did not effect the source sound at all, and the range on the parameter didn't match the range defined in my code. Clearly, my changes were not making it over to the installed version of my plug-in.


I uninstalled the plug-in using the Wwise Launcher, built for Debug instead of Release, and reinstalled.

Still Placeholder.

I did the same thing but tried re-building for Release.

Still Placeholder.

I tried building the Visual Studio solutions, even though the documentation didn't say to do so.

Again, still Placeholder.

Nothing seemed to be working, so I dug a bit deeper into my Wwise libraries. I found some odd behaviours, such as when I uninstalled my EQ plug-in from the Launcher, parts of it would stick around and could even be recalled from within a Wwise project. I was running a minor update behind the most recent version of Wwise, so to bypass this perpetuation problem and maybe fix some bugs, I installed a new version.

I also decided to re-order the process and not run any build scripts until my implementation was complete. I wrote all the algorithms and made all the changes that I needed to, built each Visual Studio solution, and then ran the build script and the package and bundle scripts.

The Cutoff Works!

Finally, success! Not only did the UI match the parameters in code, but the sound was being correctly manipulated by the DSP.

I then wanted to test how the implementation would behave if I made modifications and reinstalled the plug-in. What I found is that I could change aspects of the plug-in, reinstall using the Wwise Launcher, and have those changes appear in the Wwise Editor (unlike before with Placeholder).


Parametric Parameters
What My EQ Should Do

Taking what I learned from the Lowpass Filter implementation, I began working on my parametric EQ. Here, you can see a single-band parametric EQ in action. There are three parameters to control: Gain, Frequency, and Q.

Gain is the height/depth of the peak of the EQ (i.e. how amplified or reduced frequencies inside the band will be).

Frequency is the frequency where the peak is centered.

Q, also known as bandwidth or "quality", is the width of the frequency band that will be affected by the gain. A higher Q means a narrower band.

Using these parameters and the equations described in the paper here, I developed an implementation for the EQ within the Wwise framework.

    void OliEQ3FX::Execute(AkAudioBuffer* io_pBuffer)
    {
        const AkUInt32 uNumChannels = io_pBuffer->NumChannels();
    
        AkUInt16 uFramesProcessed;
        for (AkUInt32 i = 0; i < uNumChannels; ++i)
        {
            AkReal32* AK_RESTRICT pBuf = (AkReal32* AK_RESTRICT)io_pBuffer->GetChannel(i);
    
            uFramesProcessed = 0;
            while (uFramesProcessed < io_pBuffer->uValidFrames)
            {
                // Execute DSP in-place here
                // y[n] =  m_previousOutput[i] (will update after execution)
                // x[n] = pBuf[uFramesProcessed] (will update after execution: becomes the processed frame)
                // y[n-1] =  m_previousOutput[i] (before execution)
                m_xn = pBuf[uFramesProcessed];
                m_ynm1 = m_previousOutput[i];
                m_yn = m_previousOutput[i] = pBuf[uFramesProcessed] = (m_b0 * m_xn + m_b1 * 
                    m_xnm1 + m_b2 * m_xnm2 - m_a1 * m_ynm1 - m_a2 * m_ynm2) / m_a0;
    
                // move values up
                m_xnm2 = m_xnm1;
                m_xnm1 = m_xn;
                m_ynm2 = m_ynm1;
                m_ynm1 = m_yn;
    
                // Recalculate coefficients
                // TODO: Optimize this/encapsulation in another class
    
                //EQ intermediate params
                m_A = pow(10.0f, m_pParams->RTPC.fGain / 40.0f);
                m_w = (2.0f * M_PI * m_pParams->RTPC.fFrequency) / m_sampleRate;
                m_sn = sin(m_w);
                m_cs = cos(m_w);
                m_alpha = m_sn / (2.0f * m_pParams->RTPC.fQ);
    
                //EQ coefficients
                m_b0 = 1.0f + m_alpha * m_A;
                m_b1 = -2.0f * m_cs;
                m_b2 = 1.0f - m_alpha * m_A;
                m_a0 = 1.0f + m_alpha / m_A;
                m_a1 = -2.0f * m_cs;
                m_a2 = 1 - m_alpha / m_A;
    
                ++uFramesProcessed;
            }
        }
    }

Here, pBuf represents the array of values that represents the audio sample stream. One thing that I found interesting and took some time to figure out is that almost all of the processing is done in place on the sample buffer. Before m_yn = m_previousOutput[i] = pBuf[uFramesProcessed] = ..., pBuf[uFramesProcessed] represents the input sample, but after execution is assigned the output sample. This is because the samples we need to process are already in pBuf, and it is completely acceptable and even necessary to overwrite those samples in-place, as pBuf is also our output.

Execute is a method all Wwise audio plug-ins have that houses all the plug-in's DSP. Other files that must be modified for the plug-in to function are pretty straight-forward. Those changes mostly involve switching out parameter names and defining parameter ranges.


Change That Sound!

The fully-functional plug-in can be found on GitHub here. Consult the README for instructions on how to install the OliEQ3 on your own Wwise installation.



In the future, I would like to optimize my implementation a bit, as the code could definitely be encapsulated and interpolated at certain points like the method for recalculating the coefficients. The Wwise SDK gives a few more examples of how you can modify the base code to have it operate more smoothly as well, which I would be interested to dive into. I also would love to develop a multi-band EQ or a compressor as a next step in my plug-in creation quest.


Copyright Heoliny Jung 2021