prepare loudness normalization
[ardour.git] / libs / audiographer / src / general / loudness_reader.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #include "audiographer/general/loudness_reader.h"
20 #include "pbd/fastlog.h"
21
22 using namespace AudioGrapher;
23
24 LoudnessReader::LoudnessReader (float sample_rate, unsigned int channels, framecnt_t bufsize)
25         : _ebur_plugin (0)
26         , _dbtp_plugin (0)
27         , _sample_rate (sample_rate)
28         , _channels (channels)
29         , _bufsize (bufsize / channels)
30         , _pos (0)
31 {
32         //printf ("NEW LoudnessReader %p r:%.1f c:%d f:%ld\n", this, sample_rate, channels, bufsize);
33         assert (bufsize % channels == 0);
34         assert (bufsize > 1);
35         assert (_bufsize > 0);
36
37         if (channels > 0 && channels <= 2) {
38                 using namespace Vamp::HostExt;
39                 PluginLoader* loader (PluginLoader::getInstance ());
40                 _ebur_plugin = loader->loadPlugin ("libardourvampplugins:ebur128", sample_rate, PluginLoader::ADAPT_ALL_SAFE);
41                 assert (_ebur_plugin);
42                 _ebur_plugin->reset ();
43                 if (!_ebur_plugin->initialise (channels, _bufsize, _bufsize)) {
44                         delete _ebur_plugin;
45                         _ebur_plugin = 0;
46                 }
47         }
48
49         _dbtp_plugin = (Vamp::Plugin**) malloc (sizeof(Vamp::Plugin*) * channels);
50         for (unsigned int c = 0; c < _channels; ++c) {
51                 using namespace Vamp::HostExt;
52                 PluginLoader* loader (PluginLoader::getInstance ());
53                 _dbtp_plugin[c] = loader->loadPlugin ("libardourvampplugins:dBTP", sample_rate, PluginLoader::ADAPT_ALL_SAFE);
54                 assert (_dbtp_plugin[c]);
55                 _dbtp_plugin[c]->reset ();
56                 if (!_dbtp_plugin[c]->initialise (1, _bufsize, _bufsize)) {
57                         delete _dbtp_plugin[c];
58                         _dbtp_plugin[c] = 0;
59                 }
60         }
61
62         _bufs[0] = (float*) malloc (sizeof (float) * _bufsize);
63         _bufs[1] = (float*) malloc (sizeof (float) * _bufsize);
64 }
65
66 LoudnessReader::~LoudnessReader ()
67 {
68         delete _ebur_plugin;
69         for (unsigned int c = 0; c < _channels; ++c) {
70                 delete _dbtp_plugin[c];
71         }
72         free (_dbtp_plugin);
73         free (_bufs[0]);
74         free (_bufs[1]);
75 }
76
77 void
78 LoudnessReader::reset ()
79 {
80         if (_ebur_plugin) {
81                 _ebur_plugin->reset ();
82         }
83
84         for (unsigned int c = 0; c < _channels; ++c) {
85                 if (_dbtp_plugin[c]) {
86                         _dbtp_plugin[c]->reset ();
87                 }
88         }
89 }
90
91 void
92 LoudnessReader::process (ProcessContext<float> const & ctx)
93 {
94         const framecnt_t n_samples = ctx.frames () / ctx.channels ();
95         assert (ctx.channels () == _channels);
96         assert (ctx.frames () % ctx.channels () == 0);
97         assert (n_samples <= _bufsize);
98         //printf ("PROC %p @%ld F: %ld, S: %ld C:%d\n", this, _pos, ctx.frames (), n_samples, ctx.channels ());
99
100         unsigned processed_channels = 0;
101         if (_ebur_plugin) {
102                 assert (_channels <= 2);
103                 processed_channels = _channels;
104                 framecnt_t s;
105                 float const * d = ctx.data ();
106                 for (s = 0; s < n_samples; ++s) {
107                         for (unsigned int c = 0; c < _channels; ++c, ++d) {
108                                 _bufs[c][s] = *d;
109                         }
110                 }
111                 for (; s < _bufsize; ++s) {
112                         for (unsigned int c = 0; c < _channels; ++c) {
113                                 _bufs[c][s] = 0.f;
114                         }
115                 }
116                 _ebur_plugin->process (_bufs, Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate));
117                 if (_dbtp_plugin[0]) {
118                         _dbtp_plugin[0]->process (&_bufs[0], Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate));
119                 }
120                 if (_channels == 2 && _dbtp_plugin[1]) {
121                         _dbtp_plugin[0]->process (&_bufs[1], Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate));
122                 }
123         }
124
125         for (unsigned int c = processed_channels; c < _channels; ++c) {
126                 if (!_dbtp_plugin[c]) {
127                         continue;
128                 }
129                 framecnt_t s;
130                 float const * const d = ctx.data ();
131                 for (s = 0; s < n_samples; ++s) {
132                         _bufs[0][s] = d[s * _channels + c];
133                 }
134                 for (; s < _bufsize; ++s) {
135                         _bufs[0][s] = 0.f;
136                 }
137                 _dbtp_plugin[c]->process (_bufs, Vamp::RealTime::fromSeconds ((double) _pos / _sample_rate));
138         }
139
140         _pos += n_samples;
141         ListedSource<float>::output (ctx);
142 }
143
144 float
145 LoudnessReader::get_normalize_gain (float target_lufs, float target_dbtp)
146 {
147         float dBTP = 0;
148         float LUFS = -200;
149         uint32_t have_lufs = 0;
150         uint32_t have_dbtp = 0;
151
152         if (_ebur_plugin) {
153                 Vamp::Plugin::FeatureSet features = _ebur_plugin->getRemainingFeatures ();
154                 if (!features.empty () && features.size () == 3) {
155                         const float lufs = features[0][0].values[0];
156                         LUFS = std::max (LUFS, lufs);
157                         ++have_lufs;
158                 }
159         }
160
161         for (unsigned int c = 0; c < _channels; ++c) {
162                 if (_dbtp_plugin[c]) {
163                         Vamp::Plugin::FeatureSet features = _dbtp_plugin[c]->getRemainingFeatures ();
164                         if (!features.empty () && features.size () == 2) {
165                                 const float dbtp = features[0][0].values[0];
166                                 dBTP = std::max (dBTP, dbtp);
167                                 ++have_dbtp;
168                         }
169                 }
170         }
171
172         float g = 100000.0; // +100dB
173         bool set = false;
174         if (have_lufs && LUFS > -180.0f && target_lufs <= 0.f) {
175                 const float ge = pow (10.f, (target_lufs * 0.05f)) / pow (10.f, (LUFS * 0.05f));
176                 //printf ("LU: %f LUFS, %f\n", LUFS, ge);
177                 g = std::min (g, ge);
178                 set = true;
179         }
180
181         // TODO check that all channels were used.. ? (have_dbtp == _channels)
182         if (have_dbtp && dBTP > 0.f && target_dbtp <= 0.f) {
183                 const float ge = pow (10.f, (target_dbtp * 0.05f)) / dBTP;
184                 //printf ("TP:(%d chn) %fdBTP -> %f\n", have_dbtp, dBTP, ge);
185                 g = std::min (g, ge);
186                 set = true;
187         }
188
189         if (!set) {
190                 g = 1.f;
191         }
192         //printf ("LF %f  / %f\n", g, 1.f / g);
193         return g;
194 }