/* * Copyright (C) 2016 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "audiographer/general/loudness_reader.h" #include "pbd/fastlog.h" using namespace AudioGrapher; LoudnessReader::LoudnessReader (float sample_rate, unsigned int channels, framecnt_t bufsize) : _ebur_plugin (0) , _dbtp_plugin (0) , _sample_rate (sample_rate) , _channels (channels) , _bufsize (bufsize / channels) , _pos (0) { //printf ("NEW LoudnessReader %p r:%.1f c:%d f:%ld\n", this, sample_rate, channels, bufsize); assert (bufsize % channels == 0); assert (bufsize > 1); assert (_bufsize > 0); if (channels > 0 && channels <= 2) { using namespace Vamp::HostExt; PluginLoader* loader (PluginLoader::getInstance ()); _ebur_plugin = loader->loadPlugin ("libardourvampplugins:ebur128", sample_rate, PluginLoader::ADAPT_ALL_SAFE); assert (_ebur_plugin); _ebur_plugin->reset (); if (!_ebur_plugin->initialise (channels, _bufsize, _bufsize)) { delete _ebur_plugin; _ebur_plugin = 0; } } _dbtp_plugin = (Vamp::Plugin**) malloc (sizeof(Vamp::Plugin*) * channels); for (unsigned int c = 0; c < _channels; ++c) { using namespace Vamp::HostExt; PluginLoader* loader (PluginLoader::getInstance ()); _dbtp_plugin[c] = loader->loadPlugin ("libardourvampplugins:dBTP", sample_rate, PluginLoader::ADAPT_ALL_SAFE); assert (_dbtp_plugin[c]); _dbtp_plugin[c]->reset (); if (!_dbtp_plugin[c]->initialise (1, _bufsize, _bufsize)) { delete _dbtp_plugin[c]; _dbtp_plugin[c] = 0; } } _bufs[0] = (float*) malloc (sizeof (float) * _bufsize); _bufs[1] = (float*) malloc (sizeof (float) * _bufsize); } LoudnessReader::~LoudnessReader () { delete _ebur_plugin; for (unsigned int c = 0; c < _channels; ++c) { delete _dbtp_plugin[c]; } free (_dbtp_plugin); free (_bufs[0]); free (_bufs[1]); } void LoudnessReader::reset () { if (_ebur_plugin) { _ebur_plugin->reset (); } for (unsigned int c = 0; c < _channels; ++c) { if (_dbtp_plugin[c]) { _dbtp_plugin[c]->reset (); } } } void LoudnessReader::process (ProcessContext const & ctx) { const framecnt_t n_samples = ctx.frames () / ctx.channels (); assert (ctx.channels () == _channels); assert (ctx.frames () % ctx.channels () == 0); assert (n_samples <= _bufsize); //printf ("PROC %p @%ld F: %ld, S: %ld C:%d\n", this, _pos, ctx.frames (), n_samples, ctx.channels ()); unsigned processed_channels = 0; if (_ebur_plugin) { assert (_channels <= 2); processed_channels = _channels; framecnt_t s; float const * d = ctx.data (); for (s = 0; s < n_samples; ++s) { for (unsigned int c = 0; c < _channels; ++c, ++d) { _bufs[c][s] = *d; } } for (; s < _bufsize; ++s) { for (unsigned int c = 0; c < _channels; ++c) { _bufs[c][s] = 0.f; } } _ebur_plugin->process (_bufs, Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate)); if (_dbtp_plugin[0]) { _dbtp_plugin[0]->process (&_bufs[0], Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate)); } if (_channels == 2 && _dbtp_plugin[1]) { _dbtp_plugin[0]->process (&_bufs[1], Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate)); } } for (unsigned int c = processed_channels; c < _channels; ++c) { if (!_dbtp_plugin[c]) { continue; } framecnt_t s; float const * const d = ctx.data (); for (s = 0; s < n_samples; ++s) { _bufs[0][s] = d[s * _channels + c]; } for (; s < _bufsize; ++s) { _bufs[0][s] = 0.f; } _dbtp_plugin[c]->process (_bufs, Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate)); } _pos += n_samples; ListedSource::output (ctx); } float LoudnessReader::get_normalize_gain (float target_lufs, float target_dbtp) { float dBTP = 0; float LUFS = -200; uint32_t have_lufs = 0; uint32_t have_dbtp = 0; if (_ebur_plugin) { Vamp::Plugin::FeatureSet features = _ebur_plugin->getRemainingFeatures (); if (!features.empty () && features.size () == 3) { const float lufs = features[0][0].values[0]; LUFS = std::max (LUFS, lufs); ++have_lufs; } } for (unsigned int c = 0; c < _channels; ++c) { if (_dbtp_plugin[c]) { Vamp::Plugin::FeatureSet features = _dbtp_plugin[c]->getRemainingFeatures (); if (!features.empty () && features.size () == 2) { const float dbtp = features[0][0].values[0]; dBTP = std::max (dBTP, dbtp); ++have_dbtp; } } } float g = 100000.0; // +100dB bool set = false; if (have_lufs && LUFS > -180.0f && target_lufs <= 0.f) { const float ge = pow (10.f, (target_lufs * 0.05f)) / pow (10.f, (LUFS * 0.05f)); //printf ("LU: %f LUFS, %f\n", LUFS, ge); g = std::min (g, ge); set = true; } // TODO check that all channels were used.. ? (have_dbtp == _channels) if (have_dbtp && dBTP > 0.f && target_dbtp <= 0.f) { const float ge = pow (10.f, (target_dbtp * 0.05f)) / dBTP; //printf ("TP:(%d chn) %fdBTP -> %f\n", have_dbtp, dBTP, ge); g = std::min (g, ge); set = true; } if (!set) { g = 1.f; } //printf ("LF %f / %f\n", g, 1.f / g); return g; }