Add loudness normalization to Export Format & Graph
This commit is contained in:
parent
7547f02c07
commit
d01cb7910f
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -32,6 +32,7 @@
|
||||
namespace AudioGrapher {
|
||||
class SampleRateConverter;
|
||||
class PeakReader;
|
||||
class LoudnessReader;
|
||||
class Normalizer;
|
||||
class Analyser;
|
||||
template <typename T> class Chunker;
|
||||
@ -160,6 +161,7 @@ class LIBARDOUR_API ExportGraphBuilder
|
||||
|
||||
private:
|
||||
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::TmpFile<Sample> > TmpFilePtr;
|
||||
typedef boost::shared_ptr<AudioGrapher::Threader<Sample> > 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<SFC> children;
|
||||
|
||||
PBD::ScopedConnection post_processing_connection;
|
||||
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
@ -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<string> 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) {
|
||||
|
@ -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<Sample> (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<Sample> (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<SFC>::iterator i = children.begin(); i != children.end(); ++i) {
|
||||
(*i).set_peak (gain);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user