// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include #include #include "audio_core/sink/sink.h" #include "audio_core/sink/sink_details.h" #include "common/common_types.h" #include "common/settings.h" #include "common/settings_common.h" #include "core/core.h" #include "ui_configure_audio.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/shared_translation.h" #include "yuzu/configuration/shared_widget.h" #include "yuzu/uisettings.h" ConfigureAudio::ConfigureAudio(const Core::System& system_, std::shared_ptr> group_, const ConfigurationShared::Builder& builder, QWidget* parent) : Tab(group_, parent), ui(std::make_unique()), system{system_} { ui->setupUi(this); Setup(builder); SetConfiguration(); } ConfigureAudio::~ConfigureAudio() = default; void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { auto& layout = *ui->audio_widget->layout(); std::vector settings; std::map hold; auto push_settings = [&](Settings::Category category) { for (auto* setting : Settings::values.linkage.by_category[category]) { settings.push_back(setting); } }; auto push_ui_settings = [&](Settings::Category category) { for (auto* setting : UISettings::values.linkage.by_category[category]) { settings.push_back(setting); } }; push_settings(Settings::Category::Audio); push_settings(Settings::Category::SystemAudio); push_ui_settings(Settings::Category::UiAudio); for (auto* setting : settings) { auto* widget = builder.BuildWidget(setting, apply_funcs); if (widget == nullptr) { continue; } if (!widget->Valid()) { widget->deleteLater(); continue; } hold.emplace(std::pair{setting->Id(), widget}); auto global_sink_match = [this] { return static_cast(sink_combo_box->currentIndex()) == Settings::values.sink_id.GetValue(true); }; if (setting->Id() == Settings::values.sink_id.Id()) { // TODO (lat9nq): Let the system manage sink_id sink_combo_box = widget->combobox; InitializeAudioSinkComboBox(); if (Settings::IsConfiguringGlobal()) { connect(sink_combo_box, qOverload(&QComboBox::currentIndexChanged), this, &ConfigureAudio::UpdateAudioDevices); } else { restore_sink_button = ConfigurationShared::Widget::CreateRestoreGlobalButton( Settings::values.sink_id.UsingGlobal(), widget); widget->layout()->addWidget(restore_sink_button); connect(restore_sink_button, &QAbstractButton::clicked, [this](bool) { Settings::values.sink_id.SetGlobal(true); const int sink_index = static_cast(Settings::values.sink_id.GetValue()); sink_combo_box->setCurrentIndex(sink_index); ConfigureAudio::UpdateAudioDevices(sink_index); Settings::values.audio_output_device_id.SetGlobal(true); Settings::values.audio_input_device_id.SetGlobal(true); restore_sink_button->setVisible(false); }); connect(sink_combo_box, qOverload(&QComboBox::currentIndexChanged), [this, global_sink_match](const int slot) { Settings::values.sink_id.SetGlobal(false); Settings::values.audio_output_device_id.SetGlobal(false); Settings::values.audio_input_device_id.SetGlobal(false); restore_sink_button->setVisible(true); restore_sink_button->setEnabled(true); output_device_combo_box->setCurrentIndex(0); restore_output_device_button->setVisible(true); restore_output_device_button->setEnabled(global_sink_match()); input_device_combo_box->setCurrentIndex(0); restore_input_device_button->setVisible(true); restore_input_device_button->setEnabled(global_sink_match()); ConfigureAudio::UpdateAudioDevices(slot); }); } } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) { // Keep track of output (and input) device comboboxes to populate them with system // devices, which are determined at run time output_device_combo_box = widget->combobox; if (!Settings::IsConfiguringGlobal()) { restore_output_device_button = ConfigurationShared::Widget::CreateRestoreGlobalButton( Settings::values.audio_output_device_id.UsingGlobal(), widget); restore_output_device_button->setEnabled(global_sink_match()); restore_output_device_button->setVisible( !Settings::values.audio_output_device_id.UsingGlobal()); widget->layout()->addWidget(restore_output_device_button); connect(restore_output_device_button, &QAbstractButton::clicked, [this](bool) { Settings::values.audio_output_device_id.SetGlobal(true); SetOutputDevicesFromDeviceID(); restore_output_device_button->setVisible(false); }); connect(output_device_combo_box, qOverload(&QComboBox::currentIndexChanged), [this, global_sink_match](int) { if (updating_devices) { return; } Settings::values.audio_output_device_id.SetGlobal(false); restore_output_device_button->setVisible(true); restore_output_device_button->setEnabled(global_sink_match()); }); } } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) { input_device_combo_box = widget->combobox; if (!Settings::IsConfiguringGlobal()) { restore_input_device_button = ConfigurationShared::Widget::CreateRestoreGlobalButton( Settings::values.audio_input_device_id.UsingGlobal(), widget); widget->layout()->addWidget(restore_input_device_button); connect(restore_input_device_button, &QAbstractButton::clicked, [this](bool) { Settings::values.audio_input_device_id.SetGlobal(true); SetInputDevicesFromDeviceID(); restore_input_device_button->setVisible(false); }); connect(input_device_combo_box, qOverload(&QComboBox::currentIndexChanged), [this, global_sink_match](int) { if (updating_devices) { return; } Settings::values.audio_input_device_id.SetGlobal(false); restore_input_device_button->setVisible(true); restore_input_device_button->setEnabled(global_sink_match()); }); } } } for (const auto& [id, widget] : hold) { layout.addWidget(widget); } } void ConfigureAudio::SetConfiguration() { SetOutputSinkFromSinkID(); // The device list cannot be pre-populated (nor listed) until the output sink is known. UpdateAudioDevices(sink_combo_box->currentIndex()); SetOutputDevicesFromDeviceID(); SetInputDevicesFromDeviceID(); } void ConfigureAudio::SetOutputSinkFromSinkID() { [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box); int new_sink_index = 0; const QString sink_id = QString::fromStdString(Settings::values.sink_id.ToString()); for (int index = 0; index < sink_combo_box->count(); index++) { if (sink_combo_box->itemText(index) == sink_id) { new_sink_index = index; break; } } sink_combo_box->setCurrentIndex(new_sink_index); } void ConfigureAudio::SetOutputDevicesFromDeviceID() { int new_device_index = 0; const QString output_device_id = QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); for (int index = 0; index < output_device_combo_box->count(); index++) { if (output_device_combo_box->itemText(index) == output_device_id) { new_device_index = index; break; } } output_device_combo_box->setCurrentIndex(new_device_index); } void ConfigureAudio::SetInputDevicesFromDeviceID() { int new_device_index = 0; const QString input_device_id = QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); for (int index = 0; index < input_device_combo_box->count(); index++) { if (input_device_combo_box->itemText(index) == input_device_id) { new_device_index = index; break; } } input_device_combo_box->setCurrentIndex(new_device_index); } void ConfigureAudio::ApplyConfiguration() { const bool is_powered_on = system.IsPoweredOn(); for (const auto& apply_func : apply_funcs) { apply_func(is_powered_on); } Settings::values.sink_id.LoadString( sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString()); Settings::values.audio_output_device_id.SetValue( output_device_combo_box->itemText(output_device_combo_box->currentIndex()).toStdString()); Settings::values.audio_input_device_id.SetValue( input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString()); } void ConfigureAudio::changeEvent(QEvent* event) { if (event->type() == QEvent::LanguageChange) { RetranslateUI(); } QWidget::changeEvent(event); } void ConfigureAudio::UpdateAudioDevices(int sink_index) { updating_devices = true; output_device_combo_box->clear(); output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); const auto sink_id = Settings::ToEnum(sink_combo_box->itemText(sink_index).toStdString()); for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) { output_device_combo_box->addItem(QString::fromStdString(device)); } input_device_combo_box->clear(); input_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { input_device_combo_box->addItem(QString::fromStdString(device)); } updating_devices = false; } void ConfigureAudio::InitializeAudioSinkComboBox() { sink_combo_box->clear(); sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); for (const auto& id : AudioCore::Sink::GetSinkIDs()) { sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id))); } } void ConfigureAudio::RetranslateUI() { ui->retranslateUi(this); }