// ------------------------------------------------------------------------ // // Copyright (C) 2010-2011 Fons Adriaensen // Copyright (C) 2015 Robin Gareus // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // ------------------------------------------------------------------------ #include "ebu_r128_proc.h" #include #include #ifdef COMPILER_MSVC #include // C99 'isfinite()' is not available in MSVC. #define isfinite_local(val) (bool)_finite((double)val) #else #define isfinite_local isfinite #endif using namespace FonsEBU; float Ebu_r128_proc::Ebu_r128_hist::_bin_power[100] = { 0.0f }; float Ebu_r128_proc::_chan_gain[5] = { 1.0f, 1.0f, 1.0f, 1.41f, 1.41f }; Ebu_r128_proc::Ebu_r128_hist::Ebu_r128_hist () { _histc = new int[751]; initstat (); reset (); } Ebu_r128_proc::Ebu_r128_hist::~Ebu_r128_hist () { delete[] _histc; } void Ebu_r128_proc::Ebu_r128_hist::reset () { memset (_histc, 0, 751 * sizeof (float)); _count = 0; _error = 0; } void Ebu_r128_proc::Ebu_r128_hist::initstat () { if (_bin_power[0]) return; for (int i = 0; i < 100; i++) { _bin_power[i] = powf (10.0f, i / 100.0f); } } void Ebu_r128_proc::Ebu_r128_hist::addpoint (float v) { int k = (int)floorf (10 * v + 700.5f); if (k < 0) return; if (k > 750) { k = 750; _error++; } _histc[k]++; _count++; } float Ebu_r128_proc::Ebu_r128_hist::integrate (int i) { int j, k, n; float s; j = i % 100; n = 0; s = 0; while (i <= 750) { k = _histc[i++]; n += k; s += k * _bin_power[j++]; if (j == 100) { j = 0; s /= 10.0f; } } return s / n; } void Ebu_r128_proc::Ebu_r128_hist::calc_integ (float* vi, float* th) { int k; float s; if (_count < 50) { *vi = -200.0f; return; } s = integrate (0); // Original threshold was -8 dB below result of first integration // if (th) *th = 10 * log10f (s) - 8.0f; // k = (int)(floorf (100 * log10f (s) + 0.5f)) + 620; // Threshold redefined to -10 dB below result of first integration if (th) { *th = 10 * log10f (s) - 10.0f; } k = (int)(floorf (100 * log10f (s) + 0.5f)) + 600; if (k < 0) { k = 0; } s = integrate (k); *vi = 10 * log10f (s); } void Ebu_r128_proc::Ebu_r128_hist::calc_range (float* v0, float* v1, float* th) { int i, j, k, n; float a, b, s; if (_count < 20) { *v0 = -200.0f; *v1 = -200.0f; return; } s = integrate (0); if (th) { *th = 10 * log10f (s) - 20.0f; } k = (int)(floorf (100 * log10f (s) + 0.5)) + 500; if (k < 0) { k = 0; } for (i = k, n = 0; i <= 750; i++) { n += _histc[i]; } a = 0.10f * n; b = 0.95f * n; for (i = k, s = 0; s < a; i++) { s += _histc[i]; } for (j = 750, s = n; s > b; j--) { s -= _histc[j]; } *v0 = (i - 701) / 10.0f; *v1 = (j - 699) / 10.0f; } Ebu_r128_proc::Ebu_r128_proc () { reset (); } Ebu_r128_proc::~Ebu_r128_proc () { } void Ebu_r128_proc::init (int nchan, float fsamp) { _nchan = nchan; _fsamp = fsamp; _fragm = (int)fsamp / 20; detect_init (_fsamp); reset (); } void Ebu_r128_proc::reset () { _integr = false; _frcnt = _fragm; _frpwr = 1e-30f; _wrind = 0; _div1 = 0; _div2 = 0; _loudness_M = -200.0f; _loudness_S = -200.0f; memset (_power, 0, 64 * sizeof (float)); integr_reset (); detect_reset (); } void Ebu_r128_proc::integr_reset () { _hist_M.reset (); _hist_S.reset (); _maxloudn_M = -200.0f; _maxloudn_S = -200.0f; _integrated = -200.0f; _integ_thr = -200.0f; _range_min = -200.0f; _range_max = -200.0f; _range_thr = -200.0f; _div1 = _div2 = 0; } void Ebu_r128_proc::process (int nfram, const float* const* input) { int i, k; for (i = 0; i < _nchan; i++) { _ipp[i] = input[i]; } while (nfram) { k = (_frcnt < nfram) ? _frcnt : nfram; _frpwr += detect_process (k); _frcnt -= k; if (_frcnt == 0) { _power[_wrind++] = _frpwr / _fragm; _frcnt = _fragm; _frpwr = 1e-30f; _wrind &= 63; _loudness_M = addfrags (8); _loudness_S = addfrags (60); if (!isfinite_local (_loudness_M) || _loudness_M < -200.f) { _loudness_M = -200.0f; } if (!isfinite_local (_loudness_S) || _loudness_S < -200.f) { _loudness_S = -200.0f; } if (_loudness_M > _maxloudn_M) { _maxloudn_M = _loudness_M; } if (_loudness_S > _maxloudn_S) { _maxloudn_S = _loudness_S; } if (_integr) { if (++_div1 == 2) { _hist_M.addpoint (_loudness_M); _div1 = 0; } if (++_div2 == 10) { _hist_S.addpoint (_loudness_S); _div2 = 0; _hist_M.calc_integ (&_integrated, &_integ_thr); _hist_S.calc_range (&_range_min, &_range_max, &_range_thr); } } } for (i = 0; i < _nchan; i++) { _ipp[i] += k; } nfram -= k; } } float Ebu_r128_proc::addfrags (int nfrag) { int i, k; float s; s = 0; k = (_wrind - nfrag) & 63; for (i = 0; i < nfrag; i++) { s += _power[(i + k) & 63]; } return -0.6976f + 10 * log10f (s / nfrag); } void Ebu_r128_proc::detect_init (float fsamp) { float a, b, c, d, r, u1, u2, w1, w2; r = 1 / tan (4712.3890f / fsamp); w1 = r / 1.12201f; w2 = r * 1.12201f; u1 = u2 = 1.4085f + 210.0f / fsamp; a = u1 * w1; b = w1 * w1; c = u2 * w2; d = w2 * w2; r = 1 + a + b; _a0 = (1 + c + d) / r; _a1 = (2 - 2 * d) / r; _a2 = (1 - c + d) / r; _b1 = (2 - 2 * b) / r; _b2 = (1 - a + b) / r; r = 48.0f / fsamp; a = 4.9886075f * r; b = 6.2298014f * r * r; r = 1 + a + b; a *= 2 / r; b *= 4 / r; _c3 = a + b; _c4 = b; r = 1.004995f / r; _a0 *= r; _a1 *= r; _a2 *= r; } void Ebu_r128_proc::detect_reset () { for (int i = 0; i < MAXCH; i++) _fst[i].reset (); } float Ebu_r128_proc::detect_process (int nfram) { int i, j; float si, sj; float x, y, z1, z2, z3, z4; float const* p; Ebu_r128_fst* S; si = 0; for (i = 0, S = _fst; i < _nchan; i++, S++) { z1 = S->_z1; z2 = S->_z2; z3 = S->_z3; z4 = S->_z4; p = _ipp[i]; sj = 0; for (j = 0; j < nfram; j++) { x = p[j] - _b1 * z1 - _b2 * z2 + 1e-15f; y = _a0 * x + _a1 * z1 + _a2 * z2 - _c3 * z3 - _c4 * z4; z2 = z1; z1 = x; z4 += z3; z3 += y; sj += y * y; } si += _chan_gain[i] * sj; S->_z1 = !isfinite_local (z1) ? 0 : z1; S->_z2 = !isfinite_local (z2) ? 0 : z2; S->_z3 = !isfinite_local (z3) ? 0 : z3; S->_z4 = !isfinite_local (z4) ? 0 : z4; } return si; }