From 0ea81b13b9a59dacf6bd36ed906f4355022c88e9 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Tue, 27 Jun 2023 05:00:05 +0300 Subject: [PATCH] BackendSettingsPage: add "EBU R 128 loudness normalization"-related settings Change `Use Replay Gain metadata if it is available` checkbox into a radio button and add button to disable any loudness normalization. Add second group(+radio button), for EBU R 128 loudness normalization. There is only one tunable: Target Level, defaulting to EBU R 128-recommended `-23 LUFS`. Care should be taken when changing Target Level! You probably don't want to go outside of `-30..-16` range! At least as implemented, there is only support for per-song normalization, i.e. no per-album normalization. We do not do anything with loudness range, although i have some further thoughts about compression. We do not do anything about clipping / peak level. NOTE: we do not need `libebur128` to *perform* the audio normalization, only for the initial analysis. Co-authored-by: Jonas Kvinge --- src/settings/backendsettingspage.cpp | 38 ++- src/settings/backendsettingspage.h | 3 + src/settings/backendsettingspage.ui | 351 ++++++++++++++++++--------- 3 files changed, 278 insertions(+), 114 deletions(-) diff --git a/src/settings/backendsettingspage.cpp b/src/settings/backendsettingspage.cpp index aa8880c3d..b59a81050 100644 --- a/src/settings/backendsettingspage.cpp +++ b/src/settings/backendsettingspage.cpp @@ -74,6 +74,8 @@ BackendSettingsPage::BackendSettingsPage(SettingsDialog *dialog, QWidget *parent ui_->label_replaygainpreamp->setMinimumWidth(QFontMetrics(ui_->label_replaygainpreamp->font()).horizontalAdvance("-WW.W dB")); ui_->label_replaygainfallbackgain->setMinimumWidth(QFontMetrics(ui_->label_replaygainfallbackgain->font()).horizontalAdvance("-WW.W dB")); + ui_->label_ebur128_target_level->setMinimumWidth(QFontMetrics(ui_->label_ebur128_target_level->font()).horizontalAdvance("-WW.W LUFS")); + QObject::connect(ui_->combobox_engine, QOverload::of(&QComboBox::currentIndexChanged), this, &BackendSettingsPage::EngineChanged); QObject::connect(ui_->combobox_output, QOverload::of(&QComboBox::currentIndexChanged), this, &BackendSettingsPage::OutputChanged); QObject::connect(ui_->combobox_device, QOverload::of(&QComboBox::currentIndexChanged), this, &BackendSettingsPage::DeviceSelectionChanged); @@ -85,6 +87,9 @@ BackendSettingsPage::BackendSettingsPage(SettingsDialog *dialog, QWidget *parent #endif QObject::connect(ui_->stickyslider_replaygainpreamp, &StickySlider::valueChanged, this, &BackendSettingsPage::RgPreampChanged); QObject::connect(ui_->stickyslider_replaygainfallbackgain, &StickySlider::valueChanged, this, &BackendSettingsPage::RgFallbackGainChanged); +#ifdef HAVE_GSTREAMER + QObject::connect(ui_->stickyslider_ebur128_target_level, &StickySlider::valueChanged, this, &BackendSettingsPage::EbuR128TargetLevelChanged); +#endif QObject::connect(ui_->checkbox_fadeout_stop, &QCheckBox::toggled, this, &BackendSettingsPage::FadingOptionsChanged); QObject::connect(ui_->checkbox_fadeout_cross, &QCheckBox::toggled, this, &BackendSettingsPage::FadingOptionsChanged); QObject::connect(ui_->checkbox_fadeout_auto, &QCheckBox::toggled, this, &BackendSettingsPage::FadingOptionsChanged); @@ -161,12 +166,21 @@ void BackendSettingsPage::Load() { ui_->spinbox_low_watermark->setValue(s.value("bufferlowwatermark", kDefaultBufferLowWatermark).toDouble()); ui_->spinbox_high_watermark->setValue(s.value("bufferhighwatermark", kDefaultBufferHighWatermark).toDouble()); - ui_->checkbox_replaygain->setChecked(s.value("rgenabled", false).toBool()); + ui_->radiobutton_replaygain->setChecked(s.value("rgenabled", false).toBool()); ui_->combobox_replaygainmode->setCurrentIndex(s.value("rgmode", 0).toInt()); ui_->stickyslider_replaygainpreamp->setValue(static_cast(s.value("rgpreamp", 0.0).toDouble() * 10 + 600)); ui_->checkbox_replaygaincompression->setChecked(s.value("rgcompression", true).toBool()); ui_->stickyslider_replaygainfallbackgain->setValue(static_cast(s.value("rgfallbackgain", 0.0).toDouble() * 10 + 600)); +#ifdef HAVE_GSTREAMER + ui_->groupbox_ebur128->show(); +#else + ui_->groupbox_ebur128->hide(); +#endif + + ui_->radiobutton_ebur128_loudness_normalization->setChecked(s.value("ebur128_loudness_normalization", false).toBool()); + ui_->stickyslider_ebur128_target_level->setValue(static_cast(s.value("ebur128_target_level_lufs", -23.0).toDouble() * 10)); + #ifdef HAVE_ALSA bool fade_default = false; #else @@ -197,6 +211,10 @@ void BackendSettingsPage::Load() { RgPreampChanged(ui_->stickyslider_replaygainpreamp->value()); RgFallbackGainChanged(ui_->stickyslider_replaygainfallbackgain->value()); +#ifdef HAVE_GSTREAMER + EbuR128TargetLevelChanged(ui_->stickyslider_ebur128_target_level->value()); +#endif + Init(ui_->layout_backendsettingspage->parentWidget()); if (!QSettings().childGroups().contains(kSettingsGroup)) set_changed(); @@ -251,6 +269,7 @@ void BackendSettingsPage::Load_Engine(const EngineBase::Type enginetype) { ui_->lineedit_device->clear(); ui_->groupbox_replaygain->setEnabled(false); + ui_->groupbox_ebur128->setEnabled(false); if (engine()->type() != enginetype) { qLog(Debug) << "Switching engine."; @@ -304,10 +323,12 @@ void BackendSettingsPage::Load_Output(QString output, QVariant device) { if (engine()->type() == EngineBase::Type::GStreamer) { ui_->groupbox_buffer->setEnabled(true); ui_->groupbox_replaygain->setEnabled(true); + ui_->groupbox_ebur128->setEnabled(true); } else { ui_->groupbox_buffer->setEnabled(false); ui_->groupbox_replaygain->setEnabled(false); + ui_->groupbox_ebur128->setEnabled(false); } if (ui_->combobox_output->count() >= 1) Load_Device(output, device); @@ -474,12 +495,15 @@ void BackendSettingsPage::Save() { s.setValue("bufferlowwatermark", ui_->spinbox_low_watermark->value()); s.setValue("bufferhighwatermark", ui_->spinbox_high_watermark->value()); - s.setValue("rgenabled", ui_->checkbox_replaygain->isChecked()); + s.setValue("rgenabled", ui_->radiobutton_replaygain->isChecked()); s.setValue("rgmode", ui_->combobox_replaygainmode->currentIndex()); s.setValue("rgpreamp", static_cast(ui_->stickyslider_replaygainpreamp->value()) / 10 - 60); s.setValue("rgfallbackgain", static_cast(ui_->stickyslider_replaygainfallbackgain->value()) / 10 - 60); s.setValue("rgcompression", ui_->checkbox_replaygaincompression->isChecked()); + s.setValue("ebur128_loudness_normalization", ui_->radiobutton_ebur128_loudness_normalization->isChecked()); + s.setValue("ebur128_target_level_lufs", static_cast(ui_->stickyslider_ebur128_target_level->value()) / 10); + s.setValue("FadeoutEnabled", ui_->checkbox_fadeout_stop->isChecked()); s.setValue("CrossfadeEnabled", ui_->checkbox_fadeout_cross->isChecked()); s.setValue("AutoCrossfadeEnabled", ui_->checkbox_fadeout_auto->isChecked()); @@ -638,6 +662,16 @@ void BackendSettingsPage::RgFallbackGainChanged(const int value) { } +#ifdef HAVE_GSTREAMER +void BackendSettingsPage::EbuR128TargetLevelChanged(const int value) { + + double db = static_cast(value) / 10; + QString db_str = QString::asprintf("%+.1f LUFS", db); + ui_->label_ebur128_target_level->setText(db_str); + +} +#endif + #ifdef HAVE_ALSA void BackendSettingsPage::SwitchALSADevices(const ALSAPluginType alsa_plugin_type) { diff --git a/src/settings/backendsettingspage.h b/src/settings/backendsettingspage.h index 3290fd55f..4f75c1dea 100644 --- a/src/settings/backendsettingspage.h +++ b/src/settings/backendsettingspage.h @@ -68,6 +68,9 @@ class BackendSettingsPage : public SettingsPage { void DeviceStringChanged(); void RgPreampChanged(const int value); void RgFallbackGainChanged(const int value); +#ifdef HAVE_GSTREAMER + void EbuR128TargetLevelChanged(const int value); +#endif void radiobutton_alsa_hw_clicked(const bool checked); void radiobutton_alsa_plughw_clicked(const bool checked); void radiobutton_alsa_pcm_clicked(const bool checked); diff --git a/src/settings/backendsettingspage.ui b/src/settings/backendsettingspage.ui index 772dd74fd..a9399bfa9 100644 --- a/src/settings/backendsettingspage.ui +++ b/src/settings/backendsettingspage.ui @@ -6,8 +6,8 @@ 0 0 - 583 - 1097 + 717 + 1245 @@ -409,131 +409,235 @@ - - - true - + - Replay Gain + Audio normalization - + - - + + + No audio normalization + + true - - Use Replay Gain metadata if it is available - + + buttonGroup_audio_normalization + - + - false + true - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Replay Gain mode + + Replay Gain + + + + + + true + + Use Replay Gain metadata if it is available + + + buttonGroup_audio_normalization + - - - - - Radio (equal loudness for all tracks) + + + + false + + + + 0 - - - - Album (ideal loudness for all tracks) + + 0 - + + 0 + + + 0 + + + + + Replay Gain mode + + + + + + + + Radio (equal loudness for all tracks) + + + + + Album (ideal loudness for all tracks) + + + + + + + + Pre-amp + + + + + + + + + + + + 1200 + + + 600 + + + Qt::Horizontal + + + 600 + + + + + + + + + Apply compression to prevent clipping + + + + + + + + + + + + 0 + + + 1200 + + + 600 + + + Qt::Horizontal + + + 600 + + + + + + + + + Fallback-gain + + + + - - - - Pre-amp + + + + + + + true + + + EBU R 128 Loudness Normalization + + + + + + true + + Perform track loudness normalization + + + buttonGroup_audio_normalization + - - - - - - - - - 1200 - - - 600 - - - Qt::Horizontal - - - 600 - - - - - - - - - Apply compression to prevent clipping - - - - - - - - - - - - 0 - - - 1200 - - - 600 - - - Qt::Horizontal - - - 600 - - - - - - - - - Fallback-gain + + + + false + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Target Level + + + + + + + + + + + + -480 + + + 0 + + + -230 + + + Qt::Horizontal + + + -230 + + + + + + @@ -735,7 +839,7 @@ spinbox_low_watermark spinbox_high_watermark button_buffer_defaults - checkbox_replaygain + radiobutton_replaygain combobox_replaygainmode stickyslider_replaygainpreamp stickyslider_replaygainfallbackgain @@ -751,20 +855,43 @@ - checkbox_replaygain + radiobutton_ebur128_loudness_normalization + toggled(bool) + widget_ebur128 + setEnabled(bool) + + + 429 + 835 + + + 429 + 890 + + + + + radiobutton_replaygain toggled(bool) widget_replaygain setEnabled(bool) - 110 - 465 + 429 + 640 - 164 - 573 + 429 + 717 + + + + true + + +