audacia/src/effects/EBUR128.cpp
Max Maisel 17ef5b1c75 Fix two bugs in loudness effect (#410)
* Calculate loudness for short or silent selections as well.

In case of selections shorter than 400ms (one momentary loudness block),
take what we have got and divide only be the actual length.

Abort loudness normalization silently if the selected audio is all
silent.

* Fix loudness effect bug when selection includes a gap.

If the selected audio in a track contained a gap between two clips,
an incorrect amount of samples was processed.
2020-01-24 18:04:19 +00:00

196 lines
6.0 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
EBUR128.cpp
Max Maisel
***********************************************************************/
#include "EBUR128.h"
EBUR128::EBUR128(double rate, size_t channels)
: mChannelCount(channels)
, mRate(rate)
{
mBlockSize = ceil(0.4 * mRate); // 400 ms blocks
mBlockOverlap = ceil(0.1 * mRate); // 100 ms overlap
mLoudnessHist.reinit(HIST_BIN_COUNT, false);
mBlockRingBuffer.reinit(mBlockSize);
mWeightingFilter.reinit(mChannelCount, false);
for(size_t channel = 0; channel < mChannelCount; ++channel)
mWeightingFilter[channel] = CalcWeightingFilter(mRate);
}
void EBUR128::Initialize()
{
mSampleCount = 0;
mBlockRingPos = 0;
mBlockRingSize = 0;
memset(mLoudnessHist.get(), 0, HIST_BIN_COUNT*sizeof(long int));
for(size_t channel = 0; channel < mChannelCount; ++channel)
{
mWeightingFilter[channel][0].Reset();
mWeightingFilter[channel][1].Reset();
}
}
// fs: sample rate
// returns array of two Biquads
//
// EBU R128 parameter sampling rate adaption after
// Mansbridge, Stuart, Saoirse Finn, and Joshua D. Reiss.
// "Implementation and Evaluation of Autonomous Multi-track Fader Control."
// Paper presented at the 132nd Audio Engineering Society Convention,
// Budapest, Hungary, 2012."
ArrayOf<Biquad> EBUR128::CalcWeightingFilter(double fs)
{
ArrayOf<Biquad> pBiquad(size_t(2), true);
//
// HSF pre filter
//
double db = 3.999843853973347;
double f0 = 1681.974450955533;
double Q = 0.7071752369554196;
double K = tan(M_PI * f0 / fs);
double Vh = pow(10.0, db / 20.0);
double Vb = pow(Vh, 0.4996667741545416);
double a0 = 1.0 + K / Q + K * K;
pBiquad[0].fNumerCoeffs[Biquad::B0] = (Vh + Vb * K / Q + K * K) / a0;
pBiquad[0].fNumerCoeffs[Biquad::B1] = 2.0 * (K * K - Vh) / a0;
pBiquad[0].fNumerCoeffs[Biquad::B2] = (Vh - Vb * K / Q + K * K) / a0;
pBiquad[0].fDenomCoeffs[Biquad::A1] = 2.0 * (K * K - 1.0) / a0;
pBiquad[0].fDenomCoeffs[Biquad::A2] = (1.0 - K / Q + K * K) / a0;
//
// HPF weighting filter
//
f0 = 38.13547087602444;
Q = 0.5003270373238773;
K = tan(M_PI * f0 / fs);
pBiquad[1].fNumerCoeffs[Biquad::B0] = 1.0;
pBiquad[1].fNumerCoeffs[Biquad::B1] = -2.0;
pBiquad[1].fNumerCoeffs[Biquad::B2] = 1.0;
pBiquad[1].fDenomCoeffs[Biquad::A1] = 2.0 * (K * K - 1.0) / (1.0 + K / Q + K * K);
pBiquad[1].fDenomCoeffs[Biquad::A2] = (1.0 - K / Q + K * K) / (1.0 + K / Q + K * K);
return std::move(pBiquad);
}
void EBUR128::ProcessSampleFromChannel(float x_in, size_t channel)
{
double value;
value = mWeightingFilter[channel][0].ProcessOne(x_in);
value = mWeightingFilter[channel][1].ProcessOne(value);
if(channel == 0)
mBlockRingBuffer[mBlockRingPos] = value * value;
else
{
// Add the power of additional channels to the power of first channel.
// As a result, stereo tracks appear about 3 LUFS louder, as specified.
mBlockRingBuffer[mBlockRingPos] += value * value;
}
}
void EBUR128::NextSample()
{
++mBlockRingPos;
++mBlockRingSize;
if(mBlockRingPos % mBlockOverlap == 0)
{
// A new full block of samples was submitted.
if(mBlockRingSize >= mBlockSize)
AddBlockToHistogram(mBlockSize);
}
// Close the ring.
if(mBlockRingPos == mBlockSize)
mBlockRingPos = 0;
++mSampleCount;
}
double EBUR128::IntegrativeLoudness()
{
// EBU R128: z_i = mean square without root
// Calculate Gamma_R from histogram.
double sum_v;
long int sum_c;
HistogramSums(0, sum_v, sum_c);
// Handle incomplete block if no non-zero block was found.
if(sum_c == 0)
{
AddBlockToHistogram(mBlockRingSize);
HistogramSums(0, sum_v, sum_c);
}
// Histogram values are simplified log(x^2) immediate values
// without -0.691 + 10*(...) to safe computing power. This is
// possible because they will cancel out anyway.
// The -1 in the line below is the -10 LUFS from the EBU R128
// specification without the scaling factor of 10.
double Gamma_R = log10(sum_v/sum_c) - 1;
size_t idx_R = round((Gamma_R - GAMMA_A) * double(HIST_BIN_COUNT) / -GAMMA_A - 1);
// Apply Gamma_R threshold and calculate gated loudness (extent).
HistogramSums(idx_R+1, sum_v, sum_c);
if(sum_c == 0)
// Silence was processed.
return 0;
// LUFS is defined as -0.691 dB + 10*log10(sum(channels))
return 0.8529037031 * sum_v / sum_c;
}
void EBUR128::HistogramSums(size_t start_idx, double& sum_v, long int& sum_c)
{
double val;
sum_v = 0;
sum_c = 0;
for(size_t i = start_idx; i < HIST_BIN_COUNT; ++i)
{
val = -GAMMA_A / double(HIST_BIN_COUNT) * (i+1) + GAMMA_A;
sum_v += pow(10, val) * mLoudnessHist[i];
sum_c += mLoudnessHist[i];
}
}
/// Process new full block. Incomplete blocks shall be discarded
/// according to the EBU R128 specification there is usually no need
/// to call this on the last block.
/// However, allow to override the block size if the audio to be
/// processed is shorter than one block.
void EBUR128::AddBlockToHistogram(size_t validLen)
{
// Reset mBlockRingSize to full state to avoid overflow.
// The actual value of mBlockRingSize does not matter
// since this is only used to detect if blocks are complete (>= mBlockSize).
mBlockRingSize = mBlockSize;
size_t idx;
double blockVal = 0;
for(size_t i = 0; i < validLen; ++i)
blockVal += mBlockRingBuffer[i];
// Histogram values are simplified log10() immediate values
// without -0.691 + 10*(...) to safe computing power. This is
// possible because these constant cancel out anyway during the
// following processing steps.
blockVal = log10(blockVal/double(validLen));
// log(blockVal) is within ]-inf, 1]
idx = round((blockVal - GAMMA_A) * double(HIST_BIN_COUNT) / -GAMMA_A - 1);
// idx is within ]-inf, HIST_BIN_COUNT-1], discard indices below 0
// as they are below the EBU R128 absolute threshold anyway.
if(idx < HIST_BIN_COUNT)
++mLoudnessHist[idx];
}