diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 4e754dc594..1a85b7ec8a 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -5412,9 +5412,17 @@ Editor::normalize_region () */ list max_amps; list rms_vals; - double max_amp = 0; - double max_rms = 0; - bool use_rms = dialog.constrain_rms (); + list dbtp_vals; + list lufs_vals; + + double max_amp = 0; + double max_rms = 0; + double max_tp = 0; + float max_lufs_i = -200; + + bool use_rms = dialog.constrain_rms (); + bool use_lufs = dialog.constrain_lufs (); + bool use_dbtp = dialog.use_true_peak (); for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) { AudioRegionView const * arv = dynamic_cast (*i); @@ -5422,6 +5430,7 @@ Editor::normalize_region () continue; } dialog.descend (1.0 / regions); + double const a = arv->audio_region()->maximum_amplitude (&dialog); if (use_rms) { double r = arv->audio_region()->rms (&dialog); @@ -5429,7 +5438,23 @@ Editor::normalize_region () rms_vals.push_back (r); } - if (a == -1) { + if ((use_dbtp || use_lufs) && !dialog.cancelled ()) { + float true_peak, integrated, max_short, max_momentary; + arv->audio_region()->loudness (true_peak, integrated, max_short, max_momentary, &dialog); + float lufs = integrated; + if (lufs == -200) { + lufs = max_short; + } + if (lufs == -200) { + lufs = max_momentary; + } + max_tp = max (max_tp, true_peak); + max_lufs_i = max (max_lufs_i, lufs); + dbtp_vals.push_back (true_peak); + lufs_vals.push_back (lufs); + } + + if (a == -1 || dialog.cancelled ()) { /* the user cancelled the operation */ return; } @@ -5441,8 +5466,12 @@ Editor::normalize_region () list::const_iterator a = max_amps.begin (); list::const_iterator l = rms_vals.begin (); + list::const_iterator t = dbtp_vals.begin (); + list::const_iterator i = lufs_vals.begin (); bool in_command = false; + max_tp = max (max_tp, max_amp); + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { AudioRegionView* const arv = dynamic_cast (*r); if (!arv) { @@ -5450,21 +5479,40 @@ Editor::normalize_region () } arv->region()->clear_changes (); - - double amp = dialog.normalize_individually() ? *a : max_amp; double target = dialog.target_peak (); // dB + double amp; + if (use_dbtp) { + amp = dialog.normalize_individually() ? *t : max_tp; + } else { + amp = dialog.normalize_individually() ? *a : max_amp; + } + if (use_rms) { double const amp_rms = dialog.normalize_individually() ? *l : max_rms; - const double t_rms = dialog.target_rms (); + const double t_rms = dialog.target_rms (); const gain_t c_peak = dB_to_coefficient (target); const gain_t c_rms = dB_to_coefficient (t_rms); + assert (c_peak >= GAIN_COEFF_SMALL && c_rms > GAIN_COEFF_SMALL); if ((amp_rms / c_rms) > (amp / c_peak)) { amp = amp_rms; target = t_rms; } } + if (use_lufs) { + double const tg_lufs = dialog.target_lufs (); + double const db_lufs = dialog.normalize_individually() ? *i : max_lufs_i; // dB + const gain_t ct_lufs = dB_to_coefficient (tg_lufs); + const gain_t cv_lufs = dB_to_coefficient (db_lufs); + const gain_t c_tgt = dB_to_coefficient (target); + + if (db_lufs > -200 && (cv_lufs / ct_lufs) > (amp / c_tgt)) { + amp = cv_lufs; + target = tg_lufs; + } + } + arv->audio_region()->normalize (amp, target); if (!in_command) { @@ -5475,6 +5523,8 @@ Editor::normalize_region () ++a; ++l; + ++i; + ++t; } if (in_command) { diff --git a/gtk2_ardour/normalize_dialog.cc b/gtk2_ardour/normalize_dialog.cc index 1210985bc7..e31899f4eb 100644 --- a/gtk2_ardour/normalize_dialog.cc +++ b/gtk2_ardour/normalize_dialog.cc @@ -18,21 +18,28 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include +#include #include #include #include #include +#include #include + #include "normalize_dialog.h" + #include "pbd/i18n.h" using namespace Gtk; double NormalizeDialog::_last_normalization_value = 0; double NormalizeDialog::_last_rms_target_value = -9; +double NormalizeDialog::_last_lufs_target_value = -14; + bool NormalizeDialog::_last_normalize_individually = true; bool NormalizeDialog::_last_constrain_rms = false; +bool NormalizeDialog::_last_constrain_lufs = false; +bool NormalizeDialog::_last_normalize_true_peak = false; NormalizeDialog::NormalizeDialog (bool more_than_one) : ArdourDialog (more_than_one ? _("Normalize regions") : _("Normalize region")) @@ -44,6 +51,11 @@ NormalizeDialog::NormalizeDialog (bool more_than_one) tbl->set_spacings (6); tbl->set_border_width (6); + _dbfs_dbtp = manage (new Gtk::ComboBoxText); + _dbfs_dbtp->append_text (_("dBFS")); + _dbfs_dbtp->append_text (_("dBTP")); + _dbfs_dbtp->set_active (_last_normalize_true_peak ? 1 : 0); + _spin_peak = manage (new SpinButton (0.2, 2)); _spin_peak->set_range (-112, 0); _spin_peak->set_increments (0.1, 1); @@ -52,19 +64,32 @@ NormalizeDialog::NormalizeDialog (bool more_than_one) _constrain_rms = manage (new CheckButton (_("Constrain RMS to:"))); _constrain_rms->set_active (_last_constrain_rms); + + _constrain_lufs = manage (new CheckButton (_("Constrain LUFS to:"))); + _constrain_lufs->set_active (_last_constrain_lufs); + _spin_rms = manage (new SpinButton (0.2, 2)); _spin_rms->set_range (-112, 0); _spin_rms->set_increments (0.1, 1); _spin_rms->set_value (_last_rms_target_value); + _spin_lufs = manage (new SpinButton (0.2, 2)); + _spin_lufs->set_range (-48, 0); + _spin_lufs->set_increments (0.5, 1); + _spin_lufs->set_value (_last_lufs_target_value); + tbl->attach (*manage (new Label (_("Normalize to:"), ALIGN_END)), 0, 1, 0, 1, FILL, SHRINK); tbl->attach (*_spin_peak, 1, 2, 0, 1, SHRINK, SHRINK); - tbl->attach (*manage (new Label (_("dBFS"))), 2, 3, 0, 1, SHRINK, SHRINK); + tbl->attach (*_dbfs_dbtp, 2, 3, 0, 1, SHRINK, SHRINK); tbl->attach (*_constrain_rms, 0, 1, 1, 2, SHRINK, SHRINK); tbl->attach (*_spin_rms, 1, 2, 1, 2, SHRINK, SHRINK); tbl->attach (*manage (new Label (_("dBFS"))), 2, 3, 1, 2, SHRINK, SHRINK); + tbl->attach (*_constrain_lufs, 0, 1, 2, 3, SHRINK, SHRINK); + tbl->attach (*_spin_lufs, 1, 2, 2, 3, SHRINK, SHRINK); + tbl->attach (*manage (new Label (_("LUFS"))), 2, 3, 2, 3, SHRINK, SHRINK); + get_vbox()->pack_start (*tbl); if (more_than_one) { @@ -94,6 +119,7 @@ NormalizeDialog::NormalizeDialog (bool more_than_one) set_default_response (RESPONSE_ACCEPT); _constrain_rms->signal_toggled ().connect (sigc::mem_fun (*this, &NormalizeDialog::update_sensitivity)); + _constrain_lufs->signal_toggled ().connect (sigc::mem_fun (*this, &NormalizeDialog::update_sensitivity)); signal_response().connect (sigc::mem_fun (*this, &NormalizeDialog::button_clicked)); } @@ -101,6 +127,7 @@ void NormalizeDialog::update_sensitivity () { _spin_rms->set_sensitive (constrain_rms ()); + _spin_lufs->set_sensitive (constrain_lufs ()); } bool @@ -119,6 +146,18 @@ NormalizeDialog::constrain_rms () const return _constrain_rms->get_active (); } +bool +NormalizeDialog::constrain_lufs () const +{ + return _constrain_lufs->get_active (); +} + +bool +NormalizeDialog::use_true_peak () const +{ + return _dbfs_dbtp->get_active_row_number () == 1; +} + double NormalizeDialog::target_peak () const { @@ -131,6 +170,12 @@ NormalizeDialog::target_rms () const return _spin_rms->get_value (); } +double +NormalizeDialog::target_lufs () const +{ + return _spin_lufs->get_value (); +} + void NormalizeDialog::update_progress_gui (float p) { @@ -146,8 +191,11 @@ NormalizeDialog::run () { int const r = ArdourDialog::run (); _last_normalization_value = target_peak (); - _last_rms_target_value = target_rms (); - _last_constrain_rms = constrain_rms (); + _last_rms_target_value = target_rms (); + _last_lufs_target_value = target_lufs (); + _last_constrain_rms = constrain_rms (); + _last_constrain_lufs = constrain_lufs (); + _last_normalize_true_peak = use_true_peak (); if (_normalize_individually) { _last_normalize_individually = _normalize_individually->get_active (); } diff --git a/gtk2_ardour/normalize_dialog.h b/gtk2_ardour/normalize_dialog.h index 2b15126cd8..ce2c9aa56a 100644 --- a/gtk2_ardour/normalize_dialog.h +++ b/gtk2_ardour/normalize_dialog.h @@ -24,6 +24,7 @@ namespace Gtk { class RadioButton; class SpinButton; class ProgressBar; + class ComboBoxText; } class NormalizeDialog : public ArdourDialog, public ProgressReporter @@ -32,9 +33,14 @@ public: NormalizeDialog (bool); bool normalize_individually () const; + bool use_true_peak () const; bool constrain_rms () const; + bool constrain_lufs () const; + double target_peak () const; double target_rms () const; + double target_lufs () const; + int run (); void on_response (int response_id) { @@ -46,14 +52,21 @@ private: void button_clicked (int); void update_sensitivity (); - Gtk::RadioButton* _normalize_individually; - Gtk::CheckButton* _constrain_rms; - Gtk::SpinButton* _spin_peak; - Gtk::SpinButton* _spin_rms; - Gtk::ProgressBar* _progress_bar; + Gtk::ComboBoxText* _dbfs_dbtp; + Gtk::RadioButton* _normalize_individually; + Gtk::CheckButton* _constrain_rms; + Gtk::CheckButton* _constrain_lufs; + Gtk::SpinButton* _spin_peak; + Gtk::SpinButton* _spin_rms; + Gtk::SpinButton* _spin_lufs; + Gtk::ProgressBar* _progress_bar; static double _last_normalization_value; static double _last_rms_target_value; + static double _last_lufs_target_value; + static bool _last_normalize_individually; + static bool _last_normalize_true_peak; static bool _last_constrain_rms; + static bool _last_constrain_lufs; };