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_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:
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user