SRS Stereo Widener Plugin

Demo:

Approach

For a project in my audio signal processing class, I was assigned to create a stereo widening plugin modeled after the Hughes SRS stereo processor. It works by splitting the stereo input signal into a mid signal and a side signal, and running the side signal through various filters to accentuate the sides of the stereo field, psychoacoustically "widening" the sound. The "side" signal is run through three filters: a high shelf, an all-pass filter, and a low frequency parametric EQ. Depending on the sound they want, the user can play around with the filter settings to alter the sides of the stereo field as well as adjust the overall mid and side levels. The result is a wider sound that can be used on reverbs, masters, or really any stereo track that a mix engineer may want to widen.

Code

The main audio processing code for the SRS-style stereo widener is shown below. I used RackAFX both to prototype my DSP and to create the final plugin GUI. It involves splitting the signal into mid-side, adjusting for gain, and processing the side signal through three different filters in parallel. The "EQ Listen mode" feature designed for prototyping allows the developer to hear only the processing that is applied to the side signal, so as to understand better how they are altering it.

bool eqListenMode = getControl(controlID::m_uEQListen) == 1; bufferType sumSignal = 0.5 * (leftChannel + rightChannel); //mid channel bufferType differenceSignal = 0.5 * (leftChannel - rightChannel); //side channel //high-pass the difference signal (LFF) differenceSignal = LFF.process(differenceSignal); //Apply mid/side gain float sumGain = getControl(controlID::m_fSumLevel); float differenceGain = getControl(controlID::m_fDiffLevel); sumSignal *= dBToRatio(sumGain); differenceSignal *= dBToRatio(differenceGain); // Difference 1 - Low Parametric EQ bufferType differenceWithLPEQ = LPEQ.process(differenceSignal); // Difference 2 - All Pass Filter float APFGainRatio = dBToRatio(getControl(controlID::m_fAPFGain)); bufferType differenceWithAPF = getControl(controlID::m_uAPFEnable) == 1 ? APFGainRatio * APF.process(differenceSignal) : differenceSignal; // Difference 3 - High shelf bufferType differenceWithHSF = HSF.process(differenceSignal); // sum the different branches to get parallel-filtered difference signal differenceSignal = (differenceWithHSF + differenceWithAPF + differenceWithLPEQ) / 3.0; // EQ-LISTEN MODE: process the sum signal through filters bufferType sumWithLPEQ = sumLPEQ.process(sumSignal); bufferType sumWithAPF = getControl(controlID::m_uAPFEnable) == 1 ? APFGainRatio * sumAPF.process(sumSignal) : sumSignal; bufferType sumWithHSF = sumHSF.process(sumSignal); bufferType eqListenSignal = (sumWithLPEQ + sumWithAPF + sumWithHSF) / 3.0; bufferType leftOut = eqListenMode ? eqListenSignal : (0.5 * (leftChannel + sumSignal + differenceSignal)); //mixed with 50% dry signal bufferType rightOut = eqListenMode ? eqListenSignal : (0.5 * (rightChannel + sumSignal - differenceSignal)); // Apply output gain float outputGain = dBToRatio(getControl(controlID::m_fOutputLvl)); leftOut *= outputGain; rightOut *= outputGain;