开发者

Help with dynamic range compression function (audio)

开发者 https://www.devze.com 2023-01-01 13:34 出处:网络
I am writing a C# function for doing dynamic range compression (an audio effect that basically squashes transient peaks and amplifies everything else to produce an overall louder sound).I have written

I am writing a C# function for doing dynamic range compression (an audio effect that basically squashes transient peaks and amplifies everything else to produce an overall louder sound). I have written a function that does this (I think):

alt text http://www.freeimagehosting.net/uploads/feea390f84.jpg

public static void Compress(ref short[] input, double thresholdDb, double ratio)
{
    double maxDb = thresholdDb - (thresholdDb / ratio);
    double maxGain = Math.Pow(10, -maxDb / 20.0);

    for (int i = 0; i < input.Length; i += 2)
    {
        // convert sample values to ABS gain and store original signs
        int signL = input[i] < 0 ? -1 : 1;
        double valL = (double)input[i] / 32768.0;
        if (valL < 0.0)
        {
            valL = -valL;
        }
        int signR = input[i + 1] < 0 ? -1 : 1;
        double valR = (double)input[i + 1] / 32768.0;
        if (valR < 0.0)
        {
            valR = -valR;
        }

        // calculate mono value and compress
        double val = (valL + valR) * 0.5;
        double posDb = -Math.Log10(val) * 20.0;
        if (posDb < thresholdDb)
        {
            posDb = thresholdDb - ((thresholdDb - posDb) / ratio);
        }

        // measure L and R sample values relative to mono value
        double multL = valL / val;
        double multR = valR / val;

        // convert compressed db value to gain and amplify
        val = Math.Pow(10, -posDb / 20.0);
        val = val / maxGain;

        // re-calculate L and R gain values relative to compressed/amplified
        // mono value
        valL = val * multL;
        valR = val * multR;

        double lim = 1.5; // determined by experimentation, with the goal
            // being that the lines below should never (or rarely) be hit
        if (valL > lim)
        {
            valL = lim;
        }
        if (valR > lim)
        {
            valR = lim;
        }

        double maxval = 32000.0 / lim; 

        // convert gain values back to sample values
        input[i] = (short)(valL * maxval); 
        input[i] *= (short)signL;
        input[i + 1] = (short)(valR * maxval); 
        input[i + 1] *= (short)signR;
    }
}

and I am calling it with threshold values between 10.0 db and 30.0 db and ratios between 1.5 and 4.0. This function definitely produces a louder overall sound, but with an unacceptable level of distortion, even at low threshold values and low ratios.

Can anybody see anything wrong with t开发者_如何学JAVAhis function? Am I handling the stereo aspect correctly (the function assumes stereo input)? As I (dimly) understand things, I don't want to compress the two channels separately, so my code is attempting to compress a "virtual" mono sample value and then apply the same degree of compression to the L and R sample value separately. Not sure I'm doing it right, however.

I think part of the problem may the "hard knee" of my function, which kicks in the compression abruptly when the threshold is crossed. I think I may need to use a "soft knee" like this:

alt text http://www.freeimagehosting.net/uploads/4c1040fda8.jpg

Can anybody suggest a modification to my function to produce the soft knee curve?


The open source Skype Voice Changer project includes a port to C# of a number of nice compressors written by Scott Stillwell, all with configurable parameters:

  • Fast attack compressor
  • Fairly childish (compressor limiter)
  • Event Horizon (peak eating limiter)

The first one looks like it has the capability to do soft-knee, although the parameter to do so is not exposed.


I think your basic understanding of how to do compression is wrong (sorry ;)). It's not about "compressing" individual sample values; that will radically change the waveform and produce severe harmonic distortions. You need to assess the input signal volume over many samples (I would have to Google for the correct formula), and use this to apply a much-more-gradually-changing multiplier to the input samples to generate output.

The DSP forum at kvraudio.com/forum might point you in the right direction if you have a hard time finding the usual techniques.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号