From 5a35099043d76935eb3d4051420a40afc3d7f033 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 5 Mar 2022 01:30:49 +0100 Subject: [PATCH] Add support for bs2b Improve headphone listening of stereo audio records --- src/engine/enginebase.cpp | 3 +++ src/engine/enginebase.h | 3 +++ src/engine/gstengine.cpp | 1 + src/engine/gstenginepipeline.cpp | 27 +++++++++++++++++++++++++++ src/engine/gstenginepipeline.h | 4 ++++ src/settings/backendsettingspage.cpp | 28 ++++++++++++++++------------ src/settings/backendsettingspage.ui | 19 +++++++++++++++++-- 7 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/engine/enginebase.cpp b/src/engine/enginebase.cpp index 5fa24385b..19042f96d 100644 --- a/src/engine/enginebase.cpp +++ b/src/engine/enginebase.cpp @@ -67,6 +67,7 @@ Engine::Base::Base(const EngineType type, QObject *parent) proxy_authentication_(false), channels_enabled_(false), channels_(0), + bs2b_enabled_(false), about_to_end_emitted_(false) {} Engine::Base::~Base() = default; @@ -142,6 +143,8 @@ void Engine::Base::ReloadSettings() { fadeout_pause_duration_ = s.value("FadeoutPauseDuration", 250).toLongLong(); fadeout_pause_duration_nanosec_ = (fadeout_pause_duration_ * kNsecPerMsec); + bs2b_enabled_ = s.value("bs2b", false).toBool(); + s.endGroup(); s.beginGroup(NetworkProxySettingsPage::kSettingsGroup); diff --git a/src/engine/enginebase.h b/src/engine/enginebase.h index 911ce5ae3..15dde6e20 100644 --- a/src/engine/enginebase.h +++ b/src/engine/enginebase.h @@ -210,6 +210,9 @@ class Base : public QObject { bool channels_enabled_; int channels_; + // Options + bool bs2b_enabled_; + private: bool about_to_end_emitted_; Q_DISABLE_COPY(Base) diff --git a/src/engine/gstengine.cpp b/src/engine/gstengine.cpp index 3d17b78c6..a79e8e1ea 100644 --- a/src/engine/gstengine.cpp +++ b/src/engine/gstengine.cpp @@ -801,6 +801,7 @@ std::shared_ptr GstEngine::CreatePipeline() { ret->set_buffer_high_watermark(buffer_high_watermark_); ret->set_proxy_settings(proxy_address_, proxy_authentication_, proxy_user_, proxy_pass_); ret->set_channels(channels_enabled_, channels_); + ret->set_bs2b_enabled(bs2b_enabled_); ret->AddBufferConsumer(this); for (GstBufferConsumer *consumer : buffer_consumers_) { diff --git a/src/engine/gstenginepipeline.cpp b/src/engine/gstenginepipeline.cpp index 035a16bf0..38099e98d 100644 --- a/src/engine/gstenginepipeline.cpp +++ b/src/engine/gstenginepipeline.cpp @@ -86,6 +86,7 @@ GstEnginePipeline::GstEnginePipeline(QObject *parent) proxy_authentication_(false), channels_enabled_(false), channels_(0), + bs2b_enabled_(false), segment_start_(0), segment_start_received_(false), end_offset_nanosec_(-1), @@ -216,6 +217,10 @@ void GstEnginePipeline::set_channels(const bool enabled, const int channels) { channels_ = channels; } +void GstEnginePipeline::set_bs2b_enabled(const bool enabled) { + bs2b_enabled_ = enabled; +} + GstElement *GstEnginePipeline::CreateElement(const QString &factory_name, const QString &name, GstElement *bin, QString &error) const { QString unique_name = QString("pipeline") + "-" + QString::number(id_) + "-" + (name.isEmpty() ? factory_name : name); @@ -515,6 +520,16 @@ bool GstEnginePipeline::InitAudioBin(QString &error) { g_object_set(G_OBJECT(rglimiter), "enabled", static_cast(rg_compression_), nullptr); } + GstElement *bs2b = nullptr; + if (bs2b_enabled_) { + bs2b = CreateElement("bs2b", "bs2b", audiobin_, error); + if (!bs2b) { + gst_object_unref(GST_OBJECT(audiobin_)); + audiobin_ = nullptr; + return false; + } + } + { // Create a pad on the outside of the audiobin and connect it to the pad of the first element. GstPad *pad = gst_element_get_static_pad(audioqueue_, "sink"); if (pad) { @@ -595,6 +610,18 @@ bool GstEnginePipeline::InitAudioBin(QString &error) { next = volume_; } + // Link bs2b element if enabled. + if (bs2b_enabled_ && bs2b) { + qLog(Debug) << "Enabling bs2b"; + if (!gst_element_link(next, bs2b)) { + gst_object_unref(GST_OBJECT(audiobin_)); + audiobin_ = nullptr; + error = "gst_element_link() failed."; + return false; + } + next = bs2b; + } + if (!gst_element_link(next, audioconverter)) { gst_object_unref(GST_OBJECT(audiobin_)); audiobin_ = nullptr; diff --git a/src/engine/gstenginepipeline.h b/src/engine/gstenginepipeline.h index 1c789dc37..0fd14b72f 100644 --- a/src/engine/gstenginepipeline.h +++ b/src/engine/gstenginepipeline.h @@ -73,6 +73,7 @@ class GstEnginePipeline : public QObject { void set_buffer_high_watermark(const double value); void set_proxy_settings(const QString &address, const bool authentication, const QString &user, const QString &pass); void set_channels(const bool enabled, const int channels); + void set_bs2b_enabled(const bool enabled); // Creates the pipeline, returns false on error bool InitFromUrl(const QByteArray &stream_url, const QUrl &original_url, const qint64 end_nanosec, QString &error); @@ -221,6 +222,9 @@ class GstEnginePipeline : public QObject { bool channels_enabled_; int channels_; + // Options + bool bs2b_enabled_; + // These get called when there is a new audio buffer available QList buffer_consumers_; QMutex buffer_consumers_mutex_; diff --git a/src/settings/backendsettingspage.cpp b/src/settings/backendsettingspage.cpp index 3f7fdd657..e679f65b5 100644 --- a/src/settings/backendsettingspage.cpp +++ b/src/settings/backendsettingspage.cpp @@ -164,6 +164,8 @@ void BackendSettingsPage::Load() { ui_->spinbox_channels->setValue(s.value("channels", 2).toInt()); ui_->widget_channels->setEnabled(ui_->checkbox_channels->isChecked()); + ui_->checkbox_bs2b->setChecked(s.value("bs2b", false).toBool()); + ui_->spinbox_bufferduration->setValue(s.value("bufferduration", kDefaultBufferDuration).toInt()); ui_->spinbox_low_watermark->setValue(s.value("bufferlowwatermark", kDefaultBufferLowWatermark).toDouble()); ui_->spinbox_high_watermark->setValue(s.value("bufferhighwatermark", kDefaultBufferHighWatermark).toDouble()); @@ -460,6 +462,20 @@ void BackendSettingsPage::Save() { s.setValue("output", output_name); s.setValue("device", device_value); +#ifdef HAVE_ALSA + if (ui_->radiobutton_alsa_hw->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_hw)); + else if (ui_->radiobutton_alsa_plughw->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_plughw)); + else if (ui_->radiobutton_alsa_pcm->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_pcm)); + else s.remove("alsaplugin"); +#endif + + s.setValue("volume_control", ui_->checkbox_volume_control->isChecked()); + + s.setValue("channels_enabled", ui_->checkbox_channels->isChecked()); + s.setValue("channels", ui_->spinbox_channels->value()); + + s.setValue("bs2b", ui_->checkbox_bs2b->isChecked()); + s.setValue("bufferduration", ui_->spinbox_bufferduration->value()); s.setValue("bufferlowwatermark", ui_->spinbox_low_watermark->value()); s.setValue("bufferhighwatermark", ui_->spinbox_high_watermark->value()); @@ -478,18 +494,6 @@ void BackendSettingsPage::Save() { s.setValue("FadeoutDuration", ui_->spinbox_fadeduration->value()); s.setValue("FadeoutPauseDuration", ui_->spinbox_fadeduration_pauseresume->value()); -#ifdef HAVE_ALSA - if (ui_->radiobutton_alsa_hw->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_hw)); - else if (ui_->radiobutton_alsa_plughw->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_plughw)); - else if (ui_->radiobutton_alsa_pcm->isChecked()) s.setValue("alsaplugin", static_cast(alsa_plugin::alsa_pcm)); - else s.remove("alsaplugin"); -#endif - - s.setValue("volume_control", ui_->checkbox_volume_control->isChecked()); - - s.setValue("channels_enabled", ui_->checkbox_channels->isChecked()); - s.setValue("channels", ui_->spinbox_channels->value()); - s.endGroup(); } diff --git a/src/settings/backendsettingspage.ui b/src/settings/backendsettingspage.ui index a7cf5c42f..4a5af428f 100644 --- a/src/settings/backendsettingspage.ui +++ b/src/settings/backendsettingspage.ui @@ -7,7 +7,7 @@ 0 0 583 - 880 + 1080 @@ -153,6 +153,15 @@ + + + + + + + Options + + @@ -236,6 +245,13 @@ + + + + Improve headphone listening of stereo audio records (bs2b) + + + @@ -699,7 +715,6 @@ radiobutton_alsa_hw radiobutton_alsa_plughw radiobutton_alsa_pcm - checkbox_volume_control checkbox_channels spinbox_channels spinbox_bufferduration