13
0

Add loudness normalization to Export Format & Graph

This commit is contained in:
Robin Gareus 2016-05-02 15:28:16 +02:00
parent 7547f02c07
commit d01cb7910f
6 changed files with 124 additions and 17 deletions

View File

@ -109,7 +109,10 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList
void select_trim_end (bool value); void select_trim_end (bool value);
void select_silence_end (AnyTime const & time); void select_silence_end (AnyTime const & time);
void select_normalize (bool value); void select_normalize (bool value);
void select_normalize_target (float value); void select_normalize_loudness (bool value);
void select_normalize_dbfs (float value);
void select_normalize_lufs (float value);
void select_normalize_dbtp (float value);
void select_tagging (bool tag); void select_tagging (bool tag);
private: private:

View File

@ -91,7 +91,10 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
void set_trim_beginning (bool value) { _trim_beginning = value; } void set_trim_beginning (bool value) { _trim_beginning = value; }
void set_trim_end (bool value) { _trim_end = value; } void set_trim_end (bool value) { _trim_end = value; }
void set_normalize (bool value) { _normalize = value; } void set_normalize (bool value) { _normalize = value; }
void set_normalize_target (float value) { _normalize_target = value; } void set_normalize_loudness (bool value) { _normalize_loudness = value; }
void set_normalize_dbfs (float value) { _normalize_dbfs = value; }
void set_normalize_lufs (float value) { _normalize_lufs = value; }
void set_normalize_dbtp (float value) { _normalize_dbtp = value; }
void set_tag (bool tag_it) { _tag = tag_it; } void set_tag (bool tag_it) { _tag = tag_it; }
void set_with_cue (bool yn) { _with_cue = yn; } void set_with_cue (bool yn) { _with_cue = yn; }
@ -157,7 +160,10 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
bool trim_beginning () const { return _trim_beginning; } bool trim_beginning () const { return _trim_beginning; }
bool trim_end () const { return _trim_end; } bool trim_end () const { return _trim_end; }
bool normalize () const { return _normalize; } bool normalize () const { return _normalize; }
float normalize_target () const { return _normalize_target; } bool normalize_loudness () const { return _normalize_loudness; }
float normalize_dbfs () const { return _normalize_dbfs; }
float normalize_lufs () const { return _normalize_lufs; }
float normalize_dbtp () const { return _normalize_dbtp; }
bool with_toc() const { return _with_toc; } bool with_toc() const { return _with_toc; }
bool with_cue() const { return _with_cue; } bool with_cue() const { return _with_cue; }
bool with_mp4chaps() const { return _with_mp4chaps; } bool with_mp4chaps() const { return _with_mp4chaps; }
@ -211,7 +217,10 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
Time _silence_end; Time _silence_end;
bool _normalize; bool _normalize;
float _normalize_target; bool _normalize_loudness;
float _normalize_dbfs;
float _normalize_lufs;
float _normalize_dbtp;
bool _with_toc; bool _with_toc;
bool _with_cue; bool _with_cue;
bool _with_mp4chaps; bool _with_mp4chaps;

View File

@ -32,6 +32,7 @@
namespace AudioGrapher { namespace AudioGrapher {
class SampleRateConverter; class SampleRateConverter;
class PeakReader; class PeakReader;
class LoudnessReader;
class Normalizer; class Normalizer;
class Analyser; class Analyser;
template <typename T> class Chunker; template <typename T> class Chunker;
@ -160,6 +161,7 @@ class LIBARDOUR_API ExportGraphBuilder
private: private:
typedef boost::shared_ptr<AudioGrapher::PeakReader> PeakReaderPtr; typedef boost::shared_ptr<AudioGrapher::PeakReader> PeakReaderPtr;
typedef boost::shared_ptr<AudioGrapher::LoudnessReader> LoudnessReaderPtr;
typedef boost::shared_ptr<AudioGrapher::Normalizer> NormalizerPtr; typedef boost::shared_ptr<AudioGrapher::Normalizer> NormalizerPtr;
typedef boost::shared_ptr<AudioGrapher::TmpFile<Sample> > TmpFilePtr; typedef boost::shared_ptr<AudioGrapher::TmpFile<Sample> > TmpFilePtr;
typedef boost::shared_ptr<AudioGrapher::Threader<Sample> > ThreaderPtr; typedef boost::shared_ptr<AudioGrapher::Threader<Sample> > ThreaderPtr;
@ -171,12 +173,13 @@ class LIBARDOUR_API ExportGraphBuilder
FileSpec config; FileSpec config;
framecnt_t max_frames_out; framecnt_t max_frames_out;
bool use_loudness;
BufferPtr buffer; BufferPtr buffer;
PeakReaderPtr peak_reader; PeakReaderPtr peak_reader;
TmpFilePtr tmp_file; TmpFilePtr tmp_file;
NormalizerPtr normalizer; NormalizerPtr normalizer;
ThreaderPtr threader; ThreaderPtr threader;
LoudnessReaderPtr loudness_reader;
boost::ptr_list<SFC> children; boost::ptr_list<SFC> children;
PBD::ScopedConnection post_processing_connection; PBD::ScopedConnection post_processing_connection;

View File

@ -343,9 +343,30 @@ ExportFormatManager::select_normalize (bool value)
} }
void void
ExportFormatManager::select_normalize_target (float value) ExportFormatManager::select_normalize_loudness (bool value)
{ {
current_selection->set_normalize_target (value); current_selection->set_normalize_loudness (value);
check_for_description_change ();
}
void
ExportFormatManager::select_normalize_dbfs (float value)
{
current_selection->set_normalize_dbfs (value);
check_for_description_change ();
}
void
ExportFormatManager::select_normalize_lufs (float value)
{
current_selection->set_normalize_lufs (value);
check_for_description_change ();
}
void
ExportFormatManager::select_normalize_dbtp (float value)
{
current_selection->set_normalize_dbtp (value);
check_for_description_change (); check_for_description_change ();
} }

View File

@ -167,7 +167,10 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
, _silence_end (s) , _silence_end (s)
, _normalize (false) , _normalize (false)
, _normalize_target (GAIN_COEFF_UNITY) , _normalize_loudness (false)
, _normalize_dbfs (GAIN_COEFF_UNITY)
, _normalize_lufs (-23)
, _normalize_dbtp (-1)
, _with_toc (false) , _with_toc (false)
, _with_cue (false) , _with_cue (false)
, _with_mp4chaps (false) , _with_mp4chaps (false)
@ -184,9 +187,30 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
ExportFormatSpecification::ExportFormatSpecification (Session & s, XMLNode const & state) ExportFormatSpecification::ExportFormatSpecification (Session & s, XMLNode const & state)
: session (s) : session (s)
, has_sample_format (false)
, supports_tagging (false)
, _has_broadcast_info (false)
, _channel_limit (0)
, _dither_type (D_None)
, _src_quality (SRC_SincBest)
, _tag (true)
, _trim_beginning (false)
, _silence_beginning (s) , _silence_beginning (s)
, _trim_end (false)
, _silence_end (s) , _silence_end (s)
, _normalize (false)
, _normalize_loudness (false)
, _normalize_dbfs (GAIN_COEFF_UNITY)
, _normalize_lufs (-23)
, _normalize_dbtp (-1)
, _with_toc (false)
, _with_cue (false)
, _with_mp4chaps (false)
, _soundcloud_upload (false) , _soundcloud_upload (false)
, _command ("")
, _analyse (true) , _analyse (true)
{ {
_silence_beginning.type = Time::Timecode; _silence_beginning.type = Time::Timecode;
@ -228,7 +252,10 @@ ExportFormatSpecification::ExportFormatSpecification (ExportFormatSpecification
set_trim_beginning (other.trim_beginning()); set_trim_beginning (other.trim_beginning());
set_trim_end (other.trim_end()); set_trim_end (other.trim_end());
set_normalize (other.normalize()); set_normalize (other.normalize());
set_normalize_target (other.normalize_target()); set_normalize_loudness (other.normalize_loudness());
set_normalize_dbfs (other.normalize_dbfs());
set_normalize_lufs (other.normalize_lufs());
set_normalize_dbtp (other.normalize_dbtp());
set_tag (other.tag()); set_tag (other.tag());
@ -281,7 +308,10 @@ ExportFormatSpecification::get_state ()
node = processing->add_child ("Normalize"); node = processing->add_child ("Normalize");
node->add_property ("enabled", normalize() ? "true" : "false"); node->add_property ("enabled", normalize() ? "true" : "false");
node->add_property ("target", to_string (normalize_target(), std::dec)); node->add_property ("loudness", normalize_loudness() ? "yes" : "no");
node->add_property ("dbfs", to_string (normalize_dbfs(), std::dec));
node->add_property ("lufs", to_string (normalize_lufs(), std::dec));
node->add_property ("dbtp", to_string (normalize_dbtp(), std::dec));
XMLNode * silence = processing->add_child ("Silence"); XMLNode * silence = processing->add_child ("Silence");
XMLNode * start = silence->add_child ("Start"); XMLNode * start = silence->add_child ("Start");
@ -407,8 +437,25 @@ ExportFormatSpecification::set_state (const XMLNode & root)
_normalize = (!prop->value().compare ("true")); _normalize = (!prop->value().compare ("true"));
} }
// old formats before ~ 4.7-930ish
if ((prop = child->property ("target"))) { if ((prop = child->property ("target"))) {
_normalize_target = atof (prop->value()); _normalize_dbfs = atof (prop->value());
}
if ((prop = child->property ("loudness"))) {
_normalize_loudness = string_is_affirmative (prop->value());
}
if ((prop = child->property ("dbfs"))) {
_normalize_dbfs = atof (prop->value());
}
if ((prop = child->property ("lufs"))) {
_normalize_lufs = atof (prop->value());
}
if ((prop = child->property ("dbtp"))) {
_normalize_dbtp = atof (prop->value());
} }
} }
@ -556,7 +603,11 @@ ExportFormatSpecification::description (bool include_name)
list<string> components; list<string> components;
if (_normalize) { if (_normalize) {
components.push_back (_("normalize")); if (_normalize_loudness) {
components.push_back (_("normalize loudness"));
} else {
components.push_back (_("normalize peak"));
}
} }
if (_trim_beginning && _trim_end) { if (_trim_beginning && _trim_end) {

View File

@ -30,6 +30,7 @@
#include "audiographer/general/normalizer.h" #include "audiographer/general/normalizer.h"
#include "audiographer/general/analyser.h" #include "audiographer/general/analyser.h"
#include "audiographer/general/peak_reader.h" #include "audiographer/general/peak_reader.h"
#include "audiographer/general/loudness_reader.h"
#include "audiographer/general/sample_format_converter.h" #include "audiographer/general/sample_format_converter.h"
#include "audiographer/general/sr_converter.h" #include "audiographer/general/sr_converter.h"
#include "audiographer/general/silence_trimmer.h" #include "audiographer/general/silence_trimmer.h"
@ -405,8 +406,9 @@ ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const
/* Normalizer */ /* Normalizer */
ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t /*max_frames*/) ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpec const & new_config, framecnt_t max_frames)
: parent (parent) : parent (parent)
, use_loudness (false)
{ {
std::string tmpfile_path = parent.session.session_directory().export_path(); std::string tmpfile_path = parent.session.session_directory().export_path();
tmpfile_path = Glib::build_filename(tmpfile_path, "XXXXXX"); tmpfile_path = Glib::build_filename(tmpfile_path, "XXXXXX");
@ -417,10 +419,12 @@ ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpe
config = new_config; config = new_config;
uint32_t const channels = config.channel_config->get_n_chans(); uint32_t const channels = config.channel_config->get_n_chans();
max_frames_out = 4086 - (4086 % channels); // TODO good chunk size max_frames_out = 4086 - (4086 % channels); // TODO good chunk size
use_loudness = config.format->normalize_loudness ();
buffer.reset (new AllocatingProcessContext<Sample> (max_frames_out, channels)); buffer.reset (new AllocatingProcessContext<Sample> (max_frames_out, channels));
peak_reader.reset (new PeakReader ()); peak_reader.reset (new PeakReader ());
normalizer.reset (new AudioGrapher::Normalizer (config.format->normalize_target())); loudness_reader.reset (new LoudnessReader (config.format->sample_rate(), channels, max_frames));
normalizer.reset (new AudioGrapher::Normalizer (use_loudness ? 0.0 : config.format->normalize_dbfs()));
threader.reset (new Threader<Sample> (parent.thread_pool)); threader.reset (new Threader<Sample> (parent.thread_pool));
normalizer->alloc_buffer (max_frames_out); normalizer->alloc_buffer (max_frames_out);
@ -433,12 +437,19 @@ ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpe
add_child (new_config); add_child (new_config);
peak_reader->add_output (tmp_file); if (use_loudness) {
loudness_reader->add_output (tmp_file);
} else {
peak_reader->add_output (tmp_file);
}
} }
ExportGraphBuilder::FloatSinkPtr ExportGraphBuilder::FloatSinkPtr
ExportGraphBuilder::Normalizer::sink () ExportGraphBuilder::Normalizer::sink ()
{ {
if (use_loudness) {
return loudness_reader;
}
return peak_reader; return peak_reader;
} }
@ -471,7 +482,11 @@ bool
ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const
{ {
return config.format->normalize() == other_config.format->normalize() && return config.format->normalize() == other_config.format->normalize() &&
config.format->normalize_target() == other_config.format->normalize_target(); (
(!config.format->normalize_loudness () && config.format->normalize_dbfs() == other_config.format->normalize_dbfs())
||
(config.format->normalize_loudness () /* lufs/dbtp is a result option, not an instantaion option */)
);
} }
unsigned unsigned
@ -491,7 +506,12 @@ ExportGraphBuilder::Normalizer::process()
void void
ExportGraphBuilder::Normalizer::start_post_processing() ExportGraphBuilder::Normalizer::start_post_processing()
{ {
const float gain = normalizer->set_peak (peak_reader->get_peak()); float gain;
if (use_loudness) {
gain = normalizer->set_peak (loudness_reader->get_peak (config.format->normalize_lufs (), config.format->normalize_dbtp ()));
} else {
gain = normalizer->set_peak (peak_reader->get_peak());
}
for (boost::ptr_list<SFC>::iterator i = children.begin(); i != children.end(); ++i) { for (boost::ptr_list<SFC>::iterator i = children.begin(); i != children.end(); ++i) {
(*i).set_peak (gain); (*i).set_peak (gain);
} }