ITU-R BS.1770 Loudness Measurement IIR filter coefficients

Hi,
so I am working on some LUFS meter and have had trouble finding fast answer to my question - what are the coefficients for the pre shelving filter and the RLB filter for other sample rates than 48 kHz, as defined in ITU-R BS.1770-5. If you have a same question, well I have an answer for you. Here are the IIR filter coefficients that I used.

struct BiquadCoefs
{
    float b0;
    float b1;
    float b2;
    float a1;
    float a2;
};

static const BiquadCoefs pre44{ 1.530841230050348f, -2.650979995154729f, 1.169079079921587f, -1.663655113256020f, 0.712595428073225f };
static const BiquadCoefs pre48{ 1.535124859586970f, -2.691696189406380f, 1.198392810852850f, -1.690659293182410f, 0.732480774215850f };
static const BiquadCoefs pre88{ 1.557515375579654f, -2.905627079926345f, 1.361333977472212f, -1.830919987962332f, 0.844142261087853f };
static const BiquadCoefs pre96{ 1.559714228975796f, -2.926741578251082f, 1.378261202315819f, -1.844609469890109f, 0.855843322930641f };
static const BiquadCoefs pre176{1.571115317741846f, -3.036544502404652f, 1.468871611989762f, -1.915329316833052f, 0.918771744160008f };
static const BiquadCoefs pre192{1.572227215091279f, -3.047283051561551f, 1.477971340979609f, -1.922202230607489f, 0.925117735116826f };

static const BiquadCoefs rlb44{ 0.999560064542514f, -1.999120129085029f, 0.999560064542514f, -1.989169673629796f, 0.989199035787039f };
static const BiquadCoefs rlb48{ 1.0f,               -2.0f,               1.0f,               -1.990047454833980f, 0.990072250366210f };
static const BiquadCoefs rlb88{ 1.002271963357382f, -2.004543926714764f, 1.002271963357382f, -1.994577515450344f, 0.994584875878055f };
static const BiquadCoefs rlb96{ 1.002492788986343f, -2.004985577972686f, 1.002492788986343f, -1.995017544724716f, 0.995023759040923f };
static const BiquadCoefs rlb176{1.003632047104213f, -2.007264094208426f, 1.003632047104213f, -1.997286922423814f, 0.997288765026068f };
static const BiquadCoefs rlb192{1.003742675371436f, -2.007485350742872f, 1.003742675371436f, -1.997507222840700f, 0.997508778355510f };

How I got the values: stepped through MATLAB function integratedLoudness().

Use with your own risk. But I tested and compared it with HOFA 4U Meter and the results were pretty close.

1 Like

I found somewhere following calculations:


    constexpr double b0_hp_48k =  1.0;
    constexpr double b1_hp_48k = -2.0;
    constexpr double b2_hp_48k =  1.0;
    constexpr double a0_hp_48k =  1.0;
    constexpr double a1_hp_48k = -1.99004745483398;
    constexpr double a2_hp_48k =  0.99007225036621;
    
    constexpr double b0_hs_48k =  1.53512485958697;
    constexpr double b1_hs_48k = -2.69169618940638;
    constexpr double b2_hs_48k =  1.19839281085285;
    constexpr double a0_hs_48k =  1.0;
    constexpr double a1_hs_48k = -1.69065929318241;
    constexpr double a2_hs_48k =  0.73248077421585;

    if(sampleRate == 48000)
    {
        for(int ch=0; ch<numOfChannels; ++ch)
        {
            hpFilter[ch].setCoefficients(IIRCoefficients(b0_hp_48k, b1_hp_48k, b2_hp_48k, a0_hp_48k, a1_hp_48k, a2_hp_48k));
            hsFilter[ch].setCoefficients(IIRCoefficients(b0_hs_48k, b1_hs_48k, b2_hs_48k, a0_hs_48k, a1_hs_48k, a2_hs_48k));
        }
    }
    else
    {
        const double KoverQ_hp = (2.0 - (2.0 * a2_hp_48k)) / (a2_hp_48k - a1_hp_48k + a0_hp_48k);
        const double K_hp_48k = sqrt((a1_hp_48k + a2_hp_48k +a0_hp_48k) / (a2_hp_48k - a1_hp_48k + a0_hp_48k));
        const double arctanK_hp = atan(K_hp_48k);
        const double Q_hp = K_hp_48k / KoverQ_hp;
        
        const double VB_hp = (b0_hp_48k - b2_hp_48k)/(1.0 - a2_hp_48k);
        const double VH_hp = (b0_hp_48k - b1_hp_48k + b2_hp_48k)/(a2_hp_48k - a1_hp_48k + 1.0);
        const double VL_hp = (b0_hp_48k + b1_hp_48k + b2_hp_48k)/(a1_hp_48k + a2_hp_48k + 1.0);
        

        const double K_hp = tan(arctanK_hp * 48000.0 / static_cast<double>(sampleRate));
        const double commonFactor_hp = 1.0 / (1.0 + K_hp/Q_hp + (K_hp*K_hp));
        const double b0_hp = (VH_hp + VB_hp*K_hp/Q_hp + VL_hp*(K_hp*K_hp))*commonFactor_hp;
        const double b1_hp = 2.0*(VL_hp*K_hp*K_hp - VH_hp)*commonFactor_hp;
        const double b2_hp = (VH_hp - VB_hp*K_hp/Q_hp + VL_hp*(K_hp*K_hp))*commonFactor_hp;
        const double a1_hp = 2.0*(K_hp*K_hp - 1.0)*commonFactor_hp;
        const double a2_hp = (1.0 - K_hp/Q_hp + (K_hp*K_hp))*commonFactor_hp;

        for(int ch=0; ch<numOfChannels; ++ch)
            hpFilter[ch].setCoefficients(IIRCoefficients(b0_hp, b1_hp, b2_hp, 1.0, a1_hp, a2_hp));

        const double KoverQ_hs = (2.0 - (2.0 * a2_hs_48k)) / (a2_hs_48k - a1_hs_48k + a0_hs_48k);
        const double K_hs_48k = sqrt((a1_hs_48k + a2_hs_48k +a0_hs_48k) / (a2_hs_48k - a1_hs_48k + a0_hs_48k));
        const double arctanK_hs = atan(K_hs_48k);
        const double Q_hs = K_hs_48k / KoverQ_hs;
        
        const double VB_hs = (b0_hs_48k - b2_hs_48k)/(1.0 - a2_hs_48k);
        const double VH_hs = (b0_hs_48k - b1_hs_48k + b2_hs_48k)/(a2_hs_48k - a1_hs_48k + 1.0);
        const double VL_hs = (b0_hs_48k + b1_hs_48k + b2_hs_48k)/(a1_hs_48k + a2_hs_48k + 1.0);
        

        const double K_hs = tan(arctanK_hs * 48000.0 / static_cast<double>(sampleRate));
        const double commonFactor_hs = 1.0 / (1.0 + K_hs/Q_hs + (K_hs*K_hs));
        const double b0_hs = (VH_hs + VB_hs*K_hs/Q_hs + VL_hs*(K_hs*K_hs))*commonFactor_hs;
        const double b1_hs = 2.0*(VL_hs*K_hs*K_hs - VH_hs)*commonFactor_hs;
        const double b2_hs = (VH_hs - VB_hs*K_hs/Q_hs + VL_hs*(K_hs*K_hs))*commonFactor_hs;
        const double a1_hs = 2.0*(K_hs*K_hs - 1.0)*commonFactor_hs;
        const double a2_hs = (1.0 - K_hs/Q_hs + (K_hs*K_hs))*commonFactor_hs;
        

        for(int ch=0; ch<numOfChannels; ++ch)
            _hsFilter[ch].setCoefficients(IIRCoefficients(b0_hs, b1_hs, b2_hs, 1.0, a1_hs, a2_hs));
    }

1 Like