From d01cb7910faddbf13b7190ceca990a2cafb71f95 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 2 May 2016 15:28:16 +0200 Subject: [PATCH] Add loudness normalization to Export Format & Graph --- libs/ardour/ardour/export_format_manager.h | 5 +- .../ardour/export_format_specification.h | 15 ++++- libs/ardour/ardour/export_graph_builder.h | 5 +- libs/ardour/export_format_manager.cc | 25 +++++++- libs/ardour/export_format_specification.cc | 61 +++++++++++++++++-- libs/ardour/export_graph_builder.cc | 30 +++++++-- 6 files changed, 124 insertions(+), 17 deletions(-) diff --git a/libs/ardour/ardour/export_format_manager.h b/libs/ardour/ardour/export_format_manager.h index fff97299e5..5a78708efb 100644 --- a/libs/ardour/ardour/export_format_manager.h +++ b/libs/ardour/ardour/export_format_manager.h @@ -109,7 +109,10 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList void select_trim_end (bool value); void select_silence_end (AnyTime const & time); 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); private: diff --git a/libs/ardour/ardour/export_format_specification.h b/libs/ardour/ardour/export_format_specification.h index 89ce0e5168..d473d20234 100644 --- a/libs/ardour/ardour/export_format_specification.h +++ b/libs/ardour/ardour/export_format_specification.h @@ -91,7 +91,10 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase { void set_trim_beginning (bool value) { _trim_beginning = value; } void set_trim_end (bool value) { _trim_end = 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_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_end () const { return _trim_end; } 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_cue() const { return _with_cue; } bool with_mp4chaps() const { return _with_mp4chaps; } @@ -211,7 +217,10 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase { Time _silence_end; bool _normalize; - float _normalize_target; + bool _normalize_loudness; + float _normalize_dbfs; + float _normalize_lufs; + float _normalize_dbtp; bool _with_toc; bool _with_cue; bool _with_mp4chaps; diff --git a/libs/ardour/ardour/export_graph_builder.h b/libs/ardour/ardour/export_graph_builder.h index 0a9912e43e..df02c9eb57 100644 --- a/libs/ardour/ardour/export_graph_builder.h +++ b/libs/ardour/ardour/export_graph_builder.h @@ -32,6 +32,7 @@ namespace AudioGrapher { class SampleRateConverter; class PeakReader; + class LoudnessReader; class Normalizer; class Analyser; template class Chunker; @@ -160,6 +161,7 @@ class LIBARDOUR_API ExportGraphBuilder private: typedef boost::shared_ptr PeakReaderPtr; + typedef boost::shared_ptr LoudnessReaderPtr; typedef boost::shared_ptr NormalizerPtr; typedef boost::shared_ptr > TmpFilePtr; typedef boost::shared_ptr > ThreaderPtr; @@ -171,12 +173,13 @@ class LIBARDOUR_API ExportGraphBuilder FileSpec config; framecnt_t max_frames_out; - + bool use_loudness; BufferPtr buffer; PeakReaderPtr peak_reader; TmpFilePtr tmp_file; NormalizerPtr normalizer; ThreaderPtr threader; + LoudnessReaderPtr loudness_reader; boost::ptr_list children; PBD::ScopedConnection post_processing_connection; diff --git a/libs/ardour/export_format_manager.cc b/libs/ardour/export_format_manager.cc index f87a08686f..6554412273 100644 --- a/libs/ardour/export_format_manager.cc +++ b/libs/ardour/export_format_manager.cc @@ -343,9 +343,30 @@ ExportFormatManager::select_normalize (bool value) } 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 (); } diff --git a/libs/ardour/export_format_specification.cc b/libs/ardour/export_format_specification.cc index fd85c322d6..6b0e2f237f 100644 --- a/libs/ardour/export_format_specification.cc +++ b/libs/ardour/export_format_specification.cc @@ -167,7 +167,10 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s) , _silence_end (s) , _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_cue (false) , _with_mp4chaps (false) @@ -184,9 +187,30 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s) ExportFormatSpecification::ExportFormatSpecification (Session & s, XMLNode const & state) : 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) + , _trim_end (false) , _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) + , _command ("") , _analyse (true) { _silence_beginning.type = Time::Timecode; @@ -228,7 +252,10 @@ ExportFormatSpecification::ExportFormatSpecification (ExportFormatSpecification set_trim_beginning (other.trim_beginning()); set_trim_end (other.trim_end()); 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()); @@ -281,7 +308,10 @@ ExportFormatSpecification::get_state () node = processing->add_child ("Normalize"); 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 * start = silence->add_child ("Start"); @@ -407,8 +437,25 @@ ExportFormatSpecification::set_state (const XMLNode & root) _normalize = (!prop->value().compare ("true")); } + // old formats before ~ 4.7-930ish 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 components; 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) { diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc index 349c11ddbb..a78c983f0f 100644 --- a/libs/ardour/export_graph_builder.cc +++ b/libs/ardour/export_graph_builder.cc @@ -30,6 +30,7 @@ #include "audiographer/general/normalizer.h" #include "audiographer/general/analyser.h" #include "audiographer/general/peak_reader.h" +#include "audiographer/general/loudness_reader.h" #include "audiographer/general/sample_format_converter.h" #include "audiographer/general/sr_converter.h" #include "audiographer/general/silence_trimmer.h" @@ -405,8 +406,9 @@ ExportGraphBuilder::SFC::operator== (FileSpec const & other_config) const /* 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) + , use_loudness (false) { std::string tmpfile_path = parent.session.session_directory().export_path(); tmpfile_path = Glib::build_filename(tmpfile_path, "XXXXXX"); @@ -417,10 +419,12 @@ ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpe config = new_config; uint32_t const channels = config.channel_config->get_n_chans(); max_frames_out = 4086 - (4086 % channels); // TODO good chunk size + use_loudness = config.format->normalize_loudness (); buffer.reset (new AllocatingProcessContext (max_frames_out, channels)); 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 (parent.thread_pool)); normalizer->alloc_buffer (max_frames_out); @@ -433,12 +437,19 @@ ExportGraphBuilder::Normalizer::Normalizer (ExportGraphBuilder & parent, FileSpe 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::Normalizer::sink () { + if (use_loudness) { + return loudness_reader; + } return peak_reader; } @@ -471,7 +482,11 @@ bool ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const { 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 @@ -491,7 +506,12 @@ ExportGraphBuilder::Normalizer::process() void 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::iterator i = children.begin(); i != children.end(); ++i) { (*i).set_peak (gain); }