Making a mono input "wide" / adding stereo info

I’ve been working on a stereo enhancer plugin recently and ran into a small issue, my current code gets mid and side data from the L/R channels and then multiplies them my some amount.

However when i input a mono signal, there’s no side data to multiply and the signal stays mono no matter what (see snippet below)

I’ve been considering implementing a haas-effect like delay but ideally i would like to avoid any interference issues in mono. I’m not sure what other methods are usually used when making a mono sound “wide” and I would love some help finding some ways to go about this! thanks :slight_smile:

code snippet:

float* leftChannel = buffer.getWritePointer(0);
    float* rightChannel = buffer.getWritePointer(1);
    
    float* dryLeft = buffer.getWritePointer(0);
    float* dryRight = buffer.getWritePointer(1);
    
    for (int i = 0; i < buffer.getNumSamples(); ++i)
    {
        
        // apply dB gain
        float inGain = Decibels::decibelsToGain(inGainDb);
        leftChannel[i] = leftChannel[i] * inGain;
        rightChannel[i] = rightChannel[i] * inGain;
        
        // apply the wideness-ness
//        float widthVal = wide * 0.5;
        float tmp = 1 / fmax(1 + wide, 2);
        float coef_M = 1 * tmp;
        float coef_S = wide * tmp;

        
        float mid = (leftChannel[i] + rightChannel[i])*coef_M;
        float sides = (rightChannel[i] - leftChannel[i])*coef_S;

        
        // apply waveshaping to mid and side
        // bentStepIG is a simple symmetric waveshaping function btw :3
        mid = bentStepIG(mid, drive);
        sides = bentStepIG(sides, drive);
        
        float outGain = Decibels::decibelsToGain(outGainDb);
        mid *= outGain;
        sides *= outGain;
        
        // set left and right outputs
        
        float wetL = mid-sides;
        float wetR = mid+sides;
        
        // stereoise the left + right outputs
        
        leftChannel[i] = (wetL * mix) + (dryLeft[i] * (1-mix));
        rightChannel[i] = (wetR * mix) + (dryRight[i] * (1-mix));
    }

What you basically want is to get a decorrelated clone of your mid signal that you can then use as an artificial side signal. If you do it that way, you maintain mono compatibility as the artificial side will cancel out when mixed back to mono. It’s the same as adding a delayed signal phase to the left, and phase reversed to the right.

You can use a single delay, but that only decorrelates a little, and at some frequencies it’ll be fully correlated. That’s how you get the comb filter effect that you probably don’t want. You can use multiple delays or a very short reverb burst instead, which should work better. If you overdo it, it’ll sound roomy though. Also highpassing the artificial side could be a good idea, to keep the low frequencies mono-ish. Note however that this will also influence spectral balance and loudness, so you may want to compensate for that as well.

2 Likes

Interesting, I never considered cloning my mids at all! Thanks for the help.

you should check out polyverse wider’s and izotope imager’s phase response in bertom eq curve analyzer to get an idea on what stereo widening plugins do to achieve their effects

I actually saw a pretty old thread on here with some images of that (I think it might have actually been you lol). As far as I know Izotope Image (mode I) uses the haas effect and has that nasty comb filtering i want to avoid, I’ve also heard things about using high pass filters on left and right channels to create some phase difference which leads to extra side info, happen to know anything about that?

I think there are different methods involved. One way to make an educated guess about them is to stack a lot of plugin instances in serial to see how the filters behave in extreme situations. i did that with wider once:

If the plugin starts to sound like a delay at some point it likely uses IIR allpass filters, I would say

1 Like

That’s a cool approach, ill give it a shot, thanks!