Dynamic range compression (RMS Limit)

Dynamic range compression (RMS Limit)

    • Overview
    • Limiting
    • Algorithm
    • Program

Overview

  • 背景 : 人类的听感动态范围[能承受的最大响度和能感受的最安静声音响度的范围可达100万:1(即106倍)] 达120dB。扩声系统声音重放的动态范围由于受电子设备的限制,远比人耳的动态范围小很多。最低声音的响度受系统中不相关噪声的限制,使小的声音信号淹没在噪声中而无法听到;最大声音的响度受信号削波失真,而且信号削波后产生大量谐波,损坏扬声器单元(尤其是高音扬声器)。为此,在控制信号动态范围中,音量动态平衡器得到了广泛的应用。简单地说,它是一种限制音频信号动态范围的电子装置,把安静的小信号变得更响,把高幅度的尖峰信号变得更小些,并且不产生信号削波,保护扬声器和功放免受冲击和损坏
  • 动态范围压缩特点:(1) 限制了音乐信号中极大的峰值信号,保护扬声器系统和功放系统免受损坏;(2) 可获得更大的声音增益,因为压低了信号峰值,可使节目信号中的其他幅度较小的信号得到充分的提升;(3) 使音频中的小信号不会落到调音台的噪声中去,提高了节目信号的信噪比;(4) 如果这些装置中的四个基本参数(门槛电平、压缩比、动作时间和释放时间) 调整得不适当时,会出现“泵浦声”、“呼吸声”或根本没法对信号的动态范围进行控制。

Limiting

  • 不同的参数压缩的理论效果图如下
    Dynamic range compression (RMS Limit)_第1张图片
  • 实际调试的效果图如下
    Dynamic range compression (RMS Limit)_第2张图片
    Dynamic range compression (RMS Limit)_第3张图片

Algorithm

  • 一阶滞后滤波算法公式:S(n) = αS(n-1) + (1 - α)G;

  • 压缩时间根据α的大小变化,α最小可以设置成1,这样压缩时间为0;

  • 原理图示如下:

  • Dynamic range compression (RMS Limit)_第4张图片Dynamic range compression (RMS Limit)_第5张图片

  • Root Mean Square R M S = 1 n x 1 2 + x 2 2 + . . . x n 2 2 RMS=\sqrt[2]{{\frac1n}{x_1^2+x_2^2+...x_n^2}} RMS=2n1x12+x22+...xn2
    This caculation is used to get a mean value over a buffer of 512 frames. A frame is
    defined as one float per channel of sound. That can later be used to get a smoother
    compression.

  • Gain X d B = 20 ⋅ log ⁡ 10 ( R M S ) X_{dB} = 20 · \log_{10}(RMS) XdB=20log10(RMS)
    Using the RMS result, it can be converted to the logarithmic plane,it have soft knee.
    X s c = { X d B X d B < ( T − W 2 ) X d B + ( 1 R − 1 ) ( X d B − T + W 2 ) 2 2 W ( T − W 2 ) ≤ X d B < ( T + W 2 ) T + X d B − T R X d B > ( T + W 2 ) X_{sc}=\left\{ \begin{array}{rcl} X_{dB} & & {X_{dB} < (T - \frac W2)}\\ X_{dB} +\frac{(\frac1R-1)(X_{dB}-T+\frac W2)^2}{2W} & & {(T - \frac W2) \leq X_{dB} < (T + \frac W2)}\\ T +\frac{X_{dB}-T}{R} & & {X_{dB} >(T + \frac W2)} \end{array} \right. Xsc=XdBXdB+2W(R11)(XdBT+2W)2T+RXdBTXdB<(T2W)(T2W)XdB<(T+2W)XdB>(T+2W)
    Where T T T = Threshold, R R R = Ratio and W W W = KneeWidth. This is the main part of
    the equations, where the gain is computed according to the set values of T T T, R R R and
    W.
    g c = X s c + X d B gc = X_{sc} + X_{dB} gc=Xsc+XdBThe computed gain and the original signal is added to each other.

  • Smoothing Gain
    g s = { α A ⋅ g s [ n − 1 ] + ( 1 − α A ) ⋅ g c g c [ n ] < g s [ n − 1 ] α R ⋅ g s [ n − 1 ] + ( 1 − α R ) ⋅ g c g c [ n ] ≥ g s [ n − 1 ] g_s=\left\{ \begin{array}{rcl} α_A · g_s[n − 1] + (1 − α_A) · g_c & & {g_c[n] < g_s[n − 1]} \\ α_R · g_s[n − 1] + (1 − α_R) · g_c & & {g_c[n] \geq g_s[n − 1] } \end{array} \right. gs={αAgs[n1]+(1αA)gcαRgs[n1]+(1αR)gcgc[n]<gs[n1]gc[n]gs[n1]
    α A = e x p ( − l o g ( 9 ) F s ⋅ T A ) α_A = exp(\frac{−log(9)}{F_s · T_A }) αA=exp(FsTAlog(9))
    α R = e x p ( − l o g ( 9 ) F s ⋅ T R ) α_R = exp(\frac{−log(9)}{F_s · T_R }) αR=exp(FsTRlog(9))
    Where T A T_A TA = AttackTime Period, T R T_R TR = ReleaseTime Period and F s F_s Fs = Sample
    Frequency. The smoothing function is there to give a natural transition from no compression to the calculated compression. Mathworks (2017)

Program

  • 程序中算法使用不含soft knee,其变形公式如下
    X s c = { X d B X d B < ( T − W 2 ) T + X d B − T R X d B ≥ ( T + W 2 ) X_{sc}=\left\{ \begin{array}{rcl} X_{dB} & & {X_{dB} < (T - \frac W2)}\\ T +\frac{X_{dB}-T}{R} & & {X_{dB} \geq(T + \frac W2)} \end{array} \right. Xsc={XdBT+RXdBTXdB<(T2W)XdB(T+2W)
#if 0
void vRMSLimExec(float* input, LIM_Type* pLim)
{
    int i;
    volatile float aA = 0.0;
    for(i = 0; i < MG_DEF_BLK_SZ; i++)
    {
        if(pLim->Pro.fGc <= pLim->Pro.fGs)
        {
            aA = pLim->Set.aA;    /* compress time*/
            pLim->Pro.Cnt = 0;
        }
        else
        {
            aA = 1.0;    /*hold time*/
            pLim->Pro.Cnt++;
            if(pLim->Pro.Cnt >= pLim->Pro.HdCnt)
            {
                pLim->Pro.Cnt = pLim->Pro.HdCnt;
                aA = pLim->Set.aR;    /*release time*/
            }
        }
        pLim->Pro.fGs = aA * pLim->Pro.fGs + (1 - aA) * pLim->Pro.fGc;    /* one order lag algrithm*/
        input[i] = pLim->Pro.fGs * input[i];
        input[i] = pLim->Pro.fGm * input[i];
    }
    *pLim->reduc = (1.0 / pLim->Pro.fGs);
    *pLim->reduc = ((*pLim->reduc) > 1.0) ? (*pLim->reduc) : 1.0;
}
#endif
#if 1
void vRMSLimExec(float* input, LIM_Type* pLim)
{
    fGc = pLim->Pro.fGc;
    fGm = pLim->Pro.fGm;
    aA = pLim->Set.aA;
    aR = pLim->Set.aR;
    HoldTime = pLim->Pro.HdCnt;
    HoldCnt = &pLim->Pro.Cnt;
    RmsLimExec(input, &pLim->Pro.fGs);
    *pLim->reduc = (1.0 / pLim->Pro.fGs);
    *pLim->reduc = ((*pLim->reduc) > 1.0) ? (*pLim->reduc) : 1.0;
}
#endif

/**
  * @brief  receive RMS Limiter param
  * @param
  * @retval None
  */
void cmp_RMSLim_param(LIM_Type* pLim, int ChType)
{
    ENV_RMS_LIMITER_T* pCfgLim = (ENV_RMS_LIMITER_T*)(pLim->CfgLim);

    SetMaxMinValue_Float(&pCfgLim->fRatio, 1.2, 24.9);
    SetMaxMinValue_Float(&pCfgLim->fGain, -12, 12);
    SetMaxMinValue_Int(&pCfgLim->nAttackTime, 1, 10000);
    SetMaxMinValue_Int(&pCfgLim->nReleaseTime, 1, 10000);
    SetMaxMinValue_Int(&pCfgLim->nHoldTime, 1, 10000);
    if(ChType == OUTPUT_CHANNEL)
    {
        pLim->HwGain = fOutGainHW;
        SetMaxMinValue_Float(&pCfgLim->fThreshold, 12, 42);
    }
    if(ChType == INPUT_CHANNEL)
    {
        pLim->HwGain = fInGainHW;
        SetMaxMinValue_Float(&pCfgLim->fThreshold, -24, 24);
    }
    pLim->Set.fVth = pCfgLim->fThreshold + pLim->HwGain;
    pLim->Set.aA = (Factor / (pCfgLim->nAttackTime * pLim->Set.Fs));
    pLim->Set.aA = exp(pLim->Set.aA);
    pLim->Set.aR = (Factor / (pCfgLim->nReleaseTime * pLim->Set.Fs));
    pLim->Set.aR = exp(pLim->Set.aR);
    pLim->Set.fW = 2;
    pLim->Set.fRatio = pCfgLim->fRatio;
    pLim->Set.fM = pCfgLim->fGain;
    pLim->Pro.HdCnt = pCfgLim->nHoldTime * pLim->Set.Fs;
}

/**
  * @brief  get RMS limit phase status
  * @param  length
  * @retval None
  */
void gPhaseStatus(LIM_Type* pLim, float* pRMS)
{
    pLim->Pro.fInRMSdBu = 20 * log10(*pRMS) + dBuVrefCoef;
    pLim->Pro.fInRMSdBu = (pLim->Pro.fInRMSdBu > (-140)) ? pLim->Pro.fInRMSdBu : (-140);
    //pLim->Pro.fSCdBu = pLim->Pro.fInRMSdBu;
    #if 1
    /*no soft knee*/
    if(pLim->Pro.fInRMSdBu <= (pLim->Set.fVth))
    {
        pLim->Pro.fSCdBu = pLim->Pro.fInRMSdBu;
    }
    else
    {
        pLim->Pro.fSCdBu = ((pLim->Pro.fInRMSdBu - pLim->Set.fVth) / pLim->Set.fRatio);
        pLim->Pro.fSCdBu = pLim->Set.fVth + pLim->Pro.fSCdBu;
    }
    #endif

    #if 0
    /*have soft knee*/
    if(pLim->Pro.fInRMSdBu < (pLim->Set.fVth - (pLim->Set.fW / 2)))
    {
        pLim->Pro.fSCdBu = pLim->Pro.fInRMSdBu;
    }
    if((pLim->Pro.fInRMSdBu >= (pLim->Set.fVth - (pLim->Set.fW / 2))) &&
        (pLim->Pro.fInRMSdBu <= (pLim->Set.fVth + (pLim->Set.fW / 2))))
    {
        pLim->Pro.fSCdBu = (pLim->Pro.fInRMSdBu - pLim->Set.fVth + pLim->Set.fW / 2);
        pLim->Pro.fSCdBu = pLim->Pro.fSCdBu * pLim->Pro.fSCdBu * ((1 / pLim->Set.fRatio) - 1) / (2 * pLim->Set.fW) + pLim->Pro.fInRMSdBu;
    }
    if(pLim->Pro.fInRMSdBu > (pLim->Set.fVth + (pLim->Set.fW / 2)))
    {
        pLim->Pro.fSCdBu = pLim->Set.fVth + ((pLim->Pro.fInRMSdBu - pLim->Set.fVth) / pLim->Set.fRatio);
    }
    #endif
    pLim->Pro.fGc = pLim->Pro.fSCdBu - pLim->Pro.fInRMSdBu;
    pLim->Pro.fGc = gain_computer(pLim->Pro.fGc, 20);    /* dBu convert to physical unit*/
    pLim->Pro.fGm = gain_computer(pLim->Set.fM, 20);
}

你可能感兴趣的:(audio,power,limit)