summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/sink/sink_details.cpp33
-rw-r--r--src/audio_core/sink/sink_details.h9
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h11
-rw-r--r--src/core/telemetry_session.cpp3
-rw-r--r--src/yuzu/configuration/configure_audio.cpp204
-rw-r--r--src/yuzu/configuration/configure_audio.h16
-rw-r--r--src/yuzu/configuration/configure_audio.ui162
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp8
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/shared_translation.cpp7
-rw-r--r--src/yuzu/configuration/shared_widget.cpp78
-rw-r--r--src/yuzu/configuration/shared_widget.h11
14 files changed, 219 insertions, 329 deletions
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 39ea6d91b..751e97bfc 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -15,6 +15,7 @@
15#endif 15#endif
16#include "audio_core/sink/null_sink.h" 16#include "audio_core/sink/null_sink.h"
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/settings_enums.h"
18 19
19namespace AudioCore::Sink { 20namespace AudioCore::Sink {
20namespace { 21namespace {
@@ -24,7 +25,7 @@ struct SinkDetails {
24 using LatencyFn = u32 (*)(); 25 using LatencyFn = u32 (*)();
25 26
26 /// Name for this sink. 27 /// Name for this sink.
27 std::string_view id; 28 Settings::AudioEngine id;
28 /// A method to call to construct an instance of this type of sink. 29 /// A method to call to construct an instance of this type of sink.
29 FactoryFn factory; 30 FactoryFn factory;
30 /// A method to call to list available devices. 31 /// A method to call to list available devices.
@@ -37,7 +38,7 @@ struct SinkDetails {
37constexpr SinkDetails sink_details[] = { 38constexpr SinkDetails sink_details[] = {
38#ifdef HAVE_CUBEB 39#ifdef HAVE_CUBEB
39 SinkDetails{ 40 SinkDetails{
40 "cubeb", 41 Settings::AudioEngine::Cubeb,
41 [](std::string_view device_id) -> std::unique_ptr<Sink> { 42 [](std::string_view device_id) -> std::unique_ptr<Sink> {
42 return std::make_unique<CubebSink>(device_id); 43 return std::make_unique<CubebSink>(device_id);
43 }, 44 },
@@ -47,7 +48,7 @@ constexpr SinkDetails sink_details[] = {
47#endif 48#endif
48#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
49 SinkDetails{ 50 SinkDetails{
50 "sdl2", 51 Settings::AudioEngine::Sdl2,
51 [](std::string_view device_id) -> std::unique_ptr<Sink> { 52 [](std::string_view device_id) -> std::unique_ptr<Sink> {
52 return std::make_unique<SDLSink>(device_id); 53 return std::make_unique<SDLSink>(device_id);
53 }, 54 },
@@ -55,46 +56,46 @@ constexpr SinkDetails sink_details[] = {
55 &GetSDLLatency, 56 &GetSDLLatency,
56 }, 57 },
57#endif 58#endif
58 SinkDetails{"null", 59 SinkDetails{Settings::AudioEngine::Null,
59 [](std::string_view device_id) -> std::unique_ptr<Sink> { 60 [](std::string_view device_id) -> std::unique_ptr<Sink> {
60 return std::make_unique<NullSink>(device_id); 61 return std::make_unique<NullSink>(device_id);
61 }, 62 },
62 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, 63 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
63}; 64};
64 65
65const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { 66const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
66 const auto find_backend{[](std::string_view id) { 67 const auto find_backend{[](Settings::AudioEngine id) {
67 return std::find_if(std::begin(sink_details), std::end(sink_details), 68 return std::find_if(std::begin(sink_details), std::end(sink_details),
68 [&id](const auto& sink_detail) { return sink_detail.id == id; }); 69 [&id](const auto& sink_detail) { return sink_detail.id == id; });
69 }}; 70 }};
70 71
71 auto iter = find_backend(sink_id); 72 auto iter = find_backend(sink_id);
72 73
73 if (sink_id == "auto") { 74 if (sink_id == Settings::AudioEngine::Auto) {
74 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which 75 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
75 // causes audio issues, in that case go with SDL. 76 // causes audio issues, in that case go with SDL.
76#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) 77#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
77 iter = find_backend("cubeb"); 78 iter = find_backend(Settings::AudioEngine::Cubeb);
78 if (iter->latency() > TargetSampleCount * 3) { 79 if (iter->latency() > TargetSampleCount * 3) {
79 iter = find_backend("sdl2"); 80 iter = find_backend(Settings::AudioEngine::Sdl2);
80 } 81 }
81#else 82#else
82 iter = std::begin(sink_details); 83 iter = std::begin(sink_details);
83#endif 84#endif
84 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); 85 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", Settings::TranslateEnum(iter->id));
85 } 86 }
86 87
87 if (iter == std::end(sink_details)) { 88 if (iter == std::end(sink_details)) {
88 LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); 89 LOG_ERROR(Audio, "Invalid sink_id {}", Settings::TranslateEnum(sink_id));
89 iter = find_backend("null"); 90 iter = find_backend(Settings::AudioEngine::Null);
90 } 91 }
91 92
92 return *iter; 93 return *iter;
93} 94}
94} // Anonymous namespace 95} // Anonymous namespace
95 96
96std::vector<std::string_view> GetSinkIDs() { 97std::vector<Settings::AudioEngine> GetSinkIDs() {
97 std::vector<std::string_view> sink_ids(std::size(sink_details)); 98 std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
98 99
99 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), 100 std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
100 [](const auto& sink) { return sink.id; }); 101 [](const auto& sink) { return sink.id; });
@@ -102,11 +103,11 @@ std::vector<std::string_view> GetSinkIDs() {
102 return sink_ids; 103 return sink_ids;
103} 104}
104 105
105std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture) { 106std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture) {
106 return GetOutputSinkDetails(sink_id).list_devices(capture); 107 return GetOutputSinkDetails(sink_id).list_devices(capture);
107} 108}
108 109
109std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) { 110std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id) {
110 return GetOutputSinkDetails(sink_id).factory(device_id); 111 return GetOutputSinkDetails(sink_id).factory(device_id);
111} 112}
112 113
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index e75932898..44403db71 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -7,6 +7,9 @@
7#include <string_view> 7#include <string_view>
8#include <vector> 8#include <vector>
9 9
10namespace Settings {
11enum class AudioEngine : u32;
12}
10namespace AudioCore { 13namespace AudioCore {
11class AudioManager; 14class AudioManager;
12 15
@@ -19,7 +22,7 @@ class Sink;
19 * 22 *
20 * @return Vector of available sink names. 23 * @return Vector of available sink names.
21 */ 24 */
22std::vector<std::string_view> GetSinkIDs(); 25std::vector<Settings::AudioEngine> GetSinkIDs();
23 26
24/** 27/**
25 * Gets the list of devices for a particular sink identified by the given ID. 28 * Gets the list of devices for a particular sink identified by the given ID.
@@ -28,7 +31,7 @@ std::vector<std::string_view> GetSinkIDs();
28 * @param capture - Get capture (input) devices, or output devices? 31 * @param capture - Get capture (input) devices, or output devices?
29 * @return Vector of device names. 32 * @return Vector of device names.
30 */ 33 */
31std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture); 34std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture);
32 35
33/** 36/**
34 * Creates an audio sink identified by the given device ID. 37 * Creates an audio sink identified by the given device ID.
@@ -37,7 +40,7 @@ std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool cap
37 * @param device_id - Name of the device to create. 40 * @param device_id - Name of the device to create.
38 * @return Pointer to the created sink. 41 * @return Pointer to the created sink.
39 */ 42 */
40std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id); 43std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id);
41 44
42} // namespace Sink 45} // namespace Sink
43} // namespace AudioCore 46} // namespace AudioCore
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index c8651925e..8bfda5667 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -90,7 +90,7 @@ void LogSettings() {
90 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue()); 90 log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
91 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); 91 log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
92 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); 92 log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
93 log_setting("Audio_OutputEngine", values.sink_id.GetValue()); 93 log_setting("Audio_OutputEngine", Settings::TranslateEnum(values.sink_id.GetValue()));
94 log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue()); 94 log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
95 log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue()); 95 log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
96 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); 96 log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
diff --git a/src/common/settings.h b/src/common/settings.h
index 0ac5078c6..d4b41a162 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -244,6 +244,8 @@ protected:
244 return value_.has_value() ? std::to_string(*value_) : "none"; 244 return value_.has_value() ? std::to_string(*value_) : "none";
245 } else if constexpr (std::is_same<Type, bool>()) { 245 } else if constexpr (std::is_same<Type, bool>()) {
246 return value_ ? "true" : "false"; 246 return value_ ? "true" : "false";
247 } else if (std::is_same<Type, AudioEngine>()) {
248 return TranslateEnum(value_);
247 } else { 249 } else {
248 return std::to_string(static_cast<u64>(value_)); 250 return std::to_string(static_cast<u64>(value_));
249 } 251 }
@@ -309,6 +311,8 @@ public:
309 this->SetValue(static_cast<u32>(std::stoul(input))); 311 this->SetValue(static_cast<u32>(std::stoul(input)));
310 } else if constexpr (std::is_same<Type, bool>()) { 312 } else if constexpr (std::is_same<Type, bool>()) {
311 this->SetValue(input == "true"); 313 this->SetValue(input == "true");
314 } else if constexpr (std::is_same<Type, AudioEngine>()) {
315 this->SetValue(ToEnum<Type>(input));
312 } else { 316 } else {
313 this->SetValue(static_cast<Type>(std::stoll(input))); 317 this->SetValue(static_cast<Type>(std::stoll(input)));
314 } 318 }
@@ -542,7 +546,7 @@ struct Values {
542 Linkage linkage{}; 546 Linkage linkage{};
543 547
544 // Audio 548 // Audio
545 Setting<std::string> sink_id{linkage, "auto", "output_engine", Category::Audio}; 549 Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio};
546 Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio}; 550 Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio};
547 Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio}; 551 Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio};
548 Setting<bool, false> audio_muted{linkage, false, "audio_muted", Category::Audio, false}; 552 Setting<bool, false> audio_muted{linkage, false, "audio_muted", Category::Audio, false};
@@ -731,8 +735,9 @@ struct Values {
731 SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto, 735 SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
732 TimeZone::Auto, TimeZone::Zulu, 736 TimeZone::Auto, TimeZone::Zulu,
733 "time_zone_index", Category::System}; 737 "time_zone_index", Category::System};
734 SwitchableSetting<s32, true> sound_index{ 738 SwitchableSetting<AudioMode, true> sound_index{linkage, AudioMode::Stereo,
735 linkage, 1, 0, 2, "sound_index", Category::SystemAudio}; 739 AudioMode::Mono, AudioMode::Surround,
740 "sound_index", Category::SystemAudio};
736 741
737 SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System}; 742 SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
738 743
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index a3505a505..c058ac2c7 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -254,7 +254,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
254 254
255 // Log user configuration information 255 // Log user configuration information
256 constexpr auto field_type = Telemetry::FieldType::UserConfig; 256 constexpr auto field_type = Telemetry::FieldType::UserConfig;
257 AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue()); 257 AddField(field_type, "Audio_SinkId",
258 Settings::TranslateEnum(Settings::values.sink_id.GetValue()));
258 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue()); 259 AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
259 AddField(field_type, "Renderer_Backend", 260 AddField(field_type, "Renderer_Backend",
260 TranslateRenderer(Settings::values.renderer_backend.GetValue())); 261 TranslateRenderer(Settings::values.renderer_backend.GetValue()));
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 335662144..dd9eb4dc1 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <forward_list>
4#include <memory> 5#include <memory>
5 6
6#include "audio_core/sink/sink.h" 7#include "audio_core/sink/sink.h"
@@ -10,80 +11,105 @@
10#include "ui_configure_audio.h" 11#include "ui_configure_audio.h"
11#include "yuzu/configuration/configuration_shared.h" 12#include "yuzu/configuration/configuration_shared.h"
12#include "yuzu/configuration/configure_audio.h" 13#include "yuzu/configuration/configure_audio.h"
14#include "yuzu/configuration/shared_translation.h"
15#include "yuzu/configuration/shared_widget.h"
13#include "yuzu/uisettings.h" 16#include "yuzu/uisettings.h"
14 17
15ConfigureAudio::ConfigureAudio(const Core::System& system_, 18ConfigureAudio::ConfigureAudio(const Core::System& system_,
16 std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, 19 std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group,
20 const ConfigurationShared::TranslationMap& translations_,
17 QWidget* parent) 21 QWidget* parent)
18 : Tab(group, parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} { 22 : Tab(group, parent),
23 ui(std::make_unique<Ui::ConfigureAudio>()), system{system_}, translations{translations_} {
19 ui->setupUi(this); 24 ui->setupUi(this);
25 Setup();
20 26
21 InitializeAudioSinkComboBox(); 27 SetConfiguration();
28}
22 29
23 connect(ui->volume_slider, &QSlider::valueChanged, this, 30ConfigureAudio::~ConfigureAudio() = default;
24 &ConfigureAudio::SetVolumeIndicatorText);
25 connect(ui->sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
26 &ConfigureAudio::UpdateAudioDevices);
27 31
28 ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); 32void ConfigureAudio::Setup() {
29 ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal()); 33 const bool runtime_lock = !system.IsPoweredOn();
34 auto& layout = *ui->audio_widget->layout();
30 35
31 SetupPerGameUI(); 36 std::forward_list<Settings::BasicSetting*> settings;
32 37
33 SetConfiguration(); 38 auto push = [&](Settings::Category category) {
39 for (auto* setting : Settings::values.linkage.by_category[category]) {
40 settings.push_front(setting);
41 }
42 };
43
44 push(Settings::Category::Audio);
45 push(Settings::Category::SystemAudio);
46
47 for (auto* setting : settings) {
48 auto* widget = [&]() {
49 if (setting->Id() == Settings::values.volume.Id()) {
50 return new ConfigurationShared::Widget(
51 setting, translations, this, runtime_lock, apply_funcs,
52 ConfigurationShared::RequestType::Slider, true, 1.0f, nullptr,
53 tr("%1%", "Volume percentage (e.g. 50%)"));
54 } else if (setting->Id() == Settings::values.audio_output_device_id.Id() ||
55 setting->Id() == Settings::values.audio_input_device_id.Id() ||
56 setting->Id() == Settings::values.sink_id.Id()) {
57 return new ConfigurationShared::Widget(
58 setting, translations, this, runtime_lock, apply_funcs,
59 ConfigurationShared::RequestType::ComboBox, false);
60 } else {
61 return new ConfigurationShared::Widget(setting, translations, this, runtime_lock,
62 apply_funcs);
63 }
64 }();
65
66 if (!widget->Valid()) {
67 delete widget;
68 continue;
69 }
34 70
35 const bool is_powered_on = system_.IsPoweredOn(); 71 layout.addWidget(widget);
36 ui->sink_combo_box->setEnabled(!is_powered_on);
37 ui->output_combo_box->setEnabled(!is_powered_on);
38 ui->input_combo_box->setEnabled(!is_powered_on);
39}
40 72
41ConfigureAudio::~ConfigureAudio() = default; 73 if (setting->Id() == Settings::values.sink_id.Id()) {
74 sink_combo_box = widget->combobox;
75 InitializeAudioSinkComboBox();
76
77 connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
78 &ConfigureAudio::UpdateAudioDevices);
79 } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) {
80 output_device_combo_box = widget->combobox;
81 } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) {
82 input_device_combo_box = widget->combobox;
83 }
84 }
85}
42 86
43void ConfigureAudio::SetConfiguration() { 87void ConfigureAudio::SetConfiguration() {
88 if (!Settings::IsConfiguringGlobal()) {
89 return;
90 }
91
44 SetOutputSinkFromSinkID(); 92 SetOutputSinkFromSinkID();
45 93
46 // The device list cannot be pre-populated (nor listed) until the output sink is known. 94 // The device list cannot be pre-populated (nor listed) until the output sink is known.
47 UpdateAudioDevices(ui->sink_combo_box->currentIndex()); 95 UpdateAudioDevices(sink_combo_box->currentIndex());
48 96
49 SetAudioDevicesFromDeviceID(); 97 SetAudioDevicesFromDeviceID();
50
51 const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
52 ui->volume_slider->setValue(volume_value);
53 ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue());
54
55 if (!Settings::IsConfiguringGlobal()) {
56 if (Settings::values.volume.UsingGlobal()) {
57 ui->volume_combo_box->setCurrentIndex(0);
58 ui->volume_slider->setEnabled(false);
59 } else {
60 ui->volume_combo_box->setCurrentIndex(1);
61 ui->volume_slider->setEnabled(true);
62 }
63 ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
64 ConfigurationShared::SetHighlight(ui->mode_label,
65 !Settings::values.sound_index.UsingGlobal());
66 ConfigurationShared::SetHighlight(ui->volume_layout,
67 !Settings::values.volume.UsingGlobal());
68 } else {
69 ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
70 }
71 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
72} 98}
73 99
74void ConfigureAudio::SetOutputSinkFromSinkID() { 100void ConfigureAudio::SetOutputSinkFromSinkID() {
75 [[maybe_unused]] const QSignalBlocker blocker(ui->sink_combo_box); 101 [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box);
76 102
77 int new_sink_index = 0; 103 int new_sink_index = 0;
78 const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); 104 const QString sink_id = QString::fromStdString(Settings::values.sink_id.ToString());
79 for (int index = 0; index < ui->sink_combo_box->count(); index++) { 105 for (int index = 0; index < sink_combo_box->count(); index++) {
80 if (ui->sink_combo_box->itemText(index) == sink_id) { 106 if (sink_combo_box->itemText(index) == sink_id) {
81 new_sink_index = index; 107 new_sink_index = index;
82 break; 108 break;
83 } 109 }
84 } 110 }
85 111
86 ui->sink_combo_box->setCurrentIndex(new_sink_index); 112 sink_combo_box->setCurrentIndex(new_sink_index);
87} 113}
88 114
89void ConfigureAudio::SetAudioDevicesFromDeviceID() { 115void ConfigureAudio::SetAudioDevicesFromDeviceID() {
@@ -91,57 +117,42 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() {
91 117
92 const QString output_device_id = 118 const QString output_device_id =
93 QString::fromStdString(Settings::values.audio_output_device_id.GetValue()); 119 QString::fromStdString(Settings::values.audio_output_device_id.GetValue());
94 for (int index = 0; index < ui->output_combo_box->count(); index++) { 120 for (int index = 0; index < output_device_combo_box->count(); index++) {
95 if (ui->output_combo_box->itemText(index) == output_device_id) { 121 if (output_device_combo_box->itemText(index) == output_device_id) {
96 new_device_index = index; 122 new_device_index = index;
97 break; 123 break;
98 } 124 }
99 } 125 }
100 126
101 ui->output_combo_box->setCurrentIndex(new_device_index); 127 output_device_combo_box->setCurrentIndex(new_device_index);
102 128
103 new_device_index = -1; 129 new_device_index = -1;
104 const QString input_device_id = 130 const QString input_device_id =
105 QString::fromStdString(Settings::values.audio_input_device_id.GetValue()); 131 QString::fromStdString(Settings::values.audio_input_device_id.GetValue());
106 for (int index = 0; index < ui->input_combo_box->count(); index++) { 132 for (int index = 0; index < input_device_combo_box->count(); index++) {
107 if (ui->input_combo_box->itemText(index) == input_device_id) { 133 if (input_device_combo_box->itemText(index) == input_device_id) {
108 new_device_index = index; 134 new_device_index = index;
109 break; 135 break;
110 } 136 }
111 } 137 }
112 138
113 ui->input_combo_box->setCurrentIndex(new_device_index); 139 input_device_combo_box->setCurrentIndex(new_device_index);
114}
115
116void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
117 ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
118} 140}
119 141
120void ConfigureAudio::ApplyConfiguration() { 142void ConfigureAudio::ApplyConfiguration() {
121 ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); 143 const bool is_powered_on = system.IsPoweredOn();
144 for (const auto& apply_func : apply_funcs) {
145 apply_func(is_powered_on);
146 }
122 147
123 if (Settings::IsConfiguringGlobal()) { 148 if (Settings::IsConfiguringGlobal()) {
124 Settings::values.sink_id = 149 Settings::values.sink_id.LoadString(
125 ui->sink_combo_box->itemText(ui->sink_combo_box->currentIndex()).toStdString(); 150 sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString());
126 Settings::values.audio_output_device_id.SetValue( 151 Settings::values.audio_output_device_id.SetValue(
127 ui->output_combo_box->itemText(ui->output_combo_box->currentIndex()).toStdString()); 152 output_device_combo_box->itemText(output_device_combo_box->currentIndex())
153 .toStdString());
128 Settings::values.audio_input_device_id.SetValue( 154 Settings::values.audio_input_device_id.SetValue(
129 ui->input_combo_box->itemText(ui->input_combo_box->currentIndex()).toStdString()); 155 input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
130 UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
131
132 // Guard if during game and set to game-specific value
133 if (Settings::values.volume.UsingGlobal()) {
134 const auto volume = static_cast<u8>(ui->volume_slider->value());
135 Settings::values.volume.SetValue(volume);
136 }
137 } else {
138 if (ui->volume_combo_box->currentIndex() == 0) {
139 Settings::values.volume.SetGlobal(true);
140 } else {
141 Settings::values.volume.SetGlobal(false);
142 const auto volume = static_cast<u8>(ui->volume_slider->value());
143 Settings::values.volume.SetValue(volume);
144 }
145 } 156 }
146} 157}
147 158
@@ -154,54 +165,31 @@ void ConfigureAudio::changeEvent(QEvent* event) {
154} 165}
155 166
156void ConfigureAudio::UpdateAudioDevices(int sink_index) { 167void ConfigureAudio::UpdateAudioDevices(int sink_index) {
157 ui->output_combo_box->clear(); 168 output_device_combo_box->clear();
158 ui->output_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 169 output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
159 170
160 const std::string sink_id = ui->sink_combo_box->itemText(sink_index).toStdString(); 171 const auto sink_id =
172 Settings::ToEnum<Settings::AudioEngine>(sink_combo_box->itemText(sink_index).toStdString());
161 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) { 173 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) {
162 ui->output_combo_box->addItem(QString::fromStdString(device)); 174 output_device_combo_box->addItem(QString::fromStdString(device));
163 } 175 }
164 176
165 ui->input_combo_box->clear(); 177 input_device_combo_box->clear();
166 ui->input_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 178 input_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
167 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) { 179 for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, true)) {
168 ui->input_combo_box->addItem(QString::fromStdString(device)); 180 input_device_combo_box->addItem(QString::fromStdString(device));
169 } 181 }
170} 182}
171 183
172void ConfigureAudio::InitializeAudioSinkComboBox() { 184void ConfigureAudio::InitializeAudioSinkComboBox() {
173 ui->sink_combo_box->clear(); 185 sink_combo_box->clear();
174 ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); 186 sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
175 187
176 for (const auto& id : AudioCore::Sink::GetSinkIDs()) { 188 for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
177 ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); 189 sink_combo_box->addItem(QString::fromStdString(Settings::TranslateEnum(id)));
178 } 190 }
179} 191}
180 192
181void ConfigureAudio::RetranslateUI() { 193void ConfigureAudio::RetranslateUI() {
182 ui->retranslateUi(this); 194 ui->retranslateUi(this);
183 SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
184}
185
186void ConfigureAudio::SetupPerGameUI() {
187 if (Settings::IsConfiguringGlobal()) {
188 ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
189 ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
190 return;
191 }
192
193 ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->mode_label,
194 Settings::values.sound_index.GetValue(true));
195
196 connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
197 ui->volume_slider->setEnabled(index == 1);
198 ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
199 });
200
201 ui->sink_combo_box->setVisible(false);
202 ui->sink_label->setVisible(false);
203 ui->output_combo_box->setVisible(false);
204 ui->output_label->setVisible(false);
205 ui->input_combo_box->setVisible(false);
206 ui->input_label->setVisible(false);
207} 195}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index d134ac957..170e0bce8 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -3,9 +3,14 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <forward_list>
7#include <functional>
6#include <memory> 8#include <memory>
7#include <QWidget> 9#include <QWidget>
8#include "yuzu/configuration/configuration_shared.h" 10#include "yuzu/configuration/configuration_shared.h"
11#include "yuzu/configuration/shared_translation.h"
12
13class QPushButton;
9 14
10namespace Core { 15namespace Core {
11class System; 16class System;
@@ -19,6 +24,7 @@ class ConfigureAudio : public ConfigurationShared::Tab {
19public: 24public:
20 explicit ConfigureAudio(const Core::System& system_, 25 explicit ConfigureAudio(const Core::System& system_,
21 std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group, 26 std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group,
27 const ConfigurationShared::TranslationMap& translations_,
22 QWidget* parent = nullptr); 28 QWidget* parent = nullptr);
23 ~ConfigureAudio() override; 29 ~ConfigureAudio() override;
24 30
@@ -36,11 +42,17 @@ private:
36 42
37 void SetOutputSinkFromSinkID(); 43 void SetOutputSinkFromSinkID();
38 void SetAudioDevicesFromDeviceID(); 44 void SetAudioDevicesFromDeviceID();
39 void SetVolumeIndicatorText(int percentage);
40 45
41 void SetupPerGameUI(); 46 void Setup();
42 47
43 std::unique_ptr<Ui::ConfigureAudio> ui; 48 std::unique_ptr<Ui::ConfigureAudio> ui;
44 49
45 const Core::System& system; 50 const Core::System& system;
51 const ConfigurationShared::TranslationMap& translations;
52
53 std::forward_list<std::function<void(bool)>> apply_funcs{};
54
55 QComboBox* sink_combo_box;
56 QComboBox* output_device_combo_box;
57 QComboBox* input_device_combo_box;
46}; 58};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index 4128c83ad..1181aeb00 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -21,80 +21,14 @@
21 </property> 21 </property>
22 <layout class="QVBoxLayout"> 22 <layout class="QVBoxLayout">
23 <item> 23 <item>
24 <layout class="QHBoxLayout" name="engine_layout"> 24 <widget class="QWidget" name="audio_widget" native="true">
25 <item> 25 <property name="maximumSize">
26 <widget class="QLabel" name="sink_label"> 26 <size>
27 <property name="text"> 27 <width>16777215</width>
28 <string>Output Engine:</string> 28 <height>16777213</height>
29 </property> 29 </size>
30 </widget> 30 </property>
31 </item> 31 <layout class="QVBoxLayout" name="verticalLayout">
32 <item>
33 <widget class="QComboBox" name="sink_combo_box"/>
34 </item>
35 </layout>
36 </item>
37 <item>
38 <layout class="QHBoxLayout" name="output_layout">
39 <item>
40 <widget class="QLabel" name="output_label">
41 <property name="text">
42 <string>Output Device:</string>
43 </property>
44 </widget>
45 </item>
46 <item>
47 <widget class="QComboBox" name="output_combo_box"/>
48 </item>
49 </layout>
50 </item>
51 <item>
52 <layout class="QHBoxLayout" name="input_layout">
53 <item>
54 <widget class="QLabel" name="input_label">
55 <property name="text">
56 <string>Input Device:</string>
57 </property>
58 </widget>
59 </item>
60 <item>
61 <widget class="QComboBox" name="input_combo_box"/>
62 </item>
63 </layout>
64 </item>
65 <item>
66 <layout class="QHBoxLayout" name="mode_layout">
67 <item>
68 <widget class="QLabel" name="mode_label">
69 <property name="text">
70 <string>Sound Output Mode:</string>
71 </property>
72 </widget>
73 </item>
74 <item>
75 <widget class="QComboBox" name="combo_sound">
76 <item>
77 <property name="text">
78 <string>Mono</string>
79 </property>
80 </item>
81 <item>
82 <property name="text">
83 <string>Stereo</string>
84 </property>
85 </item>
86 <item>
87 <property name="text">
88 <string>Surround</string>
89 </property>
90 </item>
91 </widget>
92 </item>
93 </layout>
94 </item>
95 <item>
96 <widget class="QWidget" name="volume_layout" native="true">
97 <layout class="QHBoxLayout" name="horizontalLayout_2">
98 <property name="leftMargin"> 32 <property name="leftMargin">
99 <number>0</number> 33 <number>0</number>
100 </property> 34 </property>
@@ -107,89 +41,9 @@
107 <property name="bottomMargin"> 41 <property name="bottomMargin">
108 <number>0</number> 42 <number>0</number>
109 </property> 43 </property>
110 <item>
111 <widget class="QComboBox" name="volume_combo_box">
112 <item>
113 <property name="text">
114 <string>Use global volume</string>
115 </property>
116 </item>
117 <item>
118 <property name="text">
119 <string>Set volume:</string>
120 </property>
121 </item>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLabel" name="volume_label">
126 <property name="text">
127 <string>Volume:</string>
128 </property>
129 </widget>
130 </item>
131 <item>
132 <spacer name="horizontalSpacer">
133 <property name="orientation">
134 <enum>Qt::Horizontal</enum>
135 </property>
136 <property name="sizeHint" stdset="0">
137 <size>
138 <width>30</width>
139 <height>20</height>
140 </size>
141 </property>
142 </spacer>
143 </item>
144 <item>
145 <widget class="QSlider" name="volume_slider">
146 <property name="sizePolicy">
147 <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
148 <horstretch>0</horstretch>
149 <verstretch>0</verstretch>
150 </sizepolicy>
151 </property>
152 <property name="maximum">
153 <number>200</number>
154 </property>
155 <property name="pageStep">
156 <number>5</number>
157 </property>
158 <property name="orientation">
159 <enum>Qt::Horizontal</enum>
160 </property>
161 </widget>
162 </item>
163 <item>
164 <widget class="QLabel" name="volume_indicator">
165 <property name="minimumSize">
166 <size>
167 <width>32</width>
168 <height>0</height>
169 </size>
170 </property>
171 <property name="text">
172 <string>0 %</string>
173 </property>
174 <property name="alignment">
175 <set>Qt::AlignCenter</set>
176 </property>
177 </widget>
178 </item>
179 </layout> 44 </layout>
180 </widget> 45 </widget>
181 </item> 46 </item>
182 <item>
183 <layout class="QHBoxLayout" name="mute_layout">
184 <item>
185 <widget class="QCheckBox" name="toggle_background_mute">
186 <property name="text">
187 <string>Mute audio when in background</string>
188 </property>
189 </widget>
190 </item>
191 </layout>
192 </item>
193 </layout> 47 </layout>
194 </widget> 48 </widget>
195 </item> 49 </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 3a94fee9e..f0f00be83 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -34,7 +34,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 34 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
35 registry(registry_), system{system_}, 35 registry(registry_), system{system_},
36 translations{ConfigurationShared::InitializeTranslations(this)}, 36 translations{ConfigurationShared::InitializeTranslations(this)},
37 audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, this)}, 37 audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *translations, this)},
38 cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, this)}, 38 cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, this)},
39 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, 39 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
40 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, 40 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2354323b8..45a4db430 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -240,12 +240,14 @@ void ConfigureGraphics::Setup() {
240 } else if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) { 240 } else if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
241 return new ConfigurationShared::Widget( 241 return new ConfigurationShared::Widget(
242 setting, translations, this, runtime_lock, apply_funcs, 242 setting, translations, this, runtime_lock, apply_funcs,
243 ConfigurationShared::RequestType::ReverseSlider, true, 0.5f); 243 ConfigurationShared::RequestType::ReverseSlider, true, 0.5f, nullptr,
244 tr("%1%", "FSR sharpening percentage (e.g. 50%)"));
244 } else if (setting->Id() == Settings::values.speed_limit.Id()) { 245 } else if (setting->Id() == Settings::values.speed_limit.Id()) {
245 return new ConfigurationShared::Widget( 246 return new ConfigurationShared::Widget(
246 setting, translations, this, runtime_lock, apply_funcs, 247 setting, translations, this, runtime_lock, apply_funcs,
247 ConfigurationShared::RequestType::SpinBox, true, 1.0f, 248 ConfigurationShared::RequestType::SpinBox, true, 1.0f,
248 &Settings::values.use_speed_limit, "%"); 249 &Settings::values.use_speed_limit,
250 tr("%", "Limit speed percentage (e.g. 50%)"));
249 } else { 251 } else {
250 return new ConfigurationShared::Widget(setting, translations, this, runtime_lock, 252 return new ConfigurationShared::Widget(setting, translations, this, runtime_lock,
251 apply_funcs); 253 apply_funcs);
@@ -304,7 +306,7 @@ void ConfigureGraphics::Setup() {
304 }); 306 });
305 } else { 307 } else {
306 QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton( 308 QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton(
307 Settings::values.bg_red, ui->bg_widget); 309 Settings::values.bg_red.UsingGlobal(), ui->bg_widget);
308 ui->bg_widget->layout()->addWidget(bg_restore_button); 310 ui->bg_widget->layout()->addWidget(bg_restore_button);
309 311
310 QObject::connect(bg_restore_button, &QAbstractButton::clicked, 312 QObject::connect(bg_restore_button, &QAbstractButton::clicked,
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index c39855334..2ee0a8ffa 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -50,7 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
50 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig); 50 game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
51 51
52 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this); 52 addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
53 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, this); 53 audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *translations, this);
54 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, this); 54 cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, this);
55 graphics_advanced_tab = 55 graphics_advanced_tab =
56 std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *translations, this); 56 std::make_unique<ConfigureGraphicsAdvanced>(system_, tab_group, *translations, this);
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 6038e8c25..c3b38f776 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -30,6 +30,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
30 INSERT(Settings, audio_input_device_id, "Input Device:", ""); 30 INSERT(Settings, audio_input_device_id, "Input Device:", "");
31 INSERT(Settings, audio_muted, "Mute audio when in background", ""); 31 INSERT(Settings, audio_muted, "Mute audio when in background", "");
32 INSERT(Settings, volume, "Volume:", ""); 32 INSERT(Settings, volume, "Volume:", "");
33 INSERT(Settings, dump_audio_commands, "", "");
33 34
34 // Core 35 // Core
35 INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); 36 INSERT(Settings, use_multi_core, "Multicore CPU Emulation", "");
@@ -270,6 +271,12 @@ std::forward_list<QString> ComboboxEnumeration(std::type_index type, QWidget* pa
270 tr("ROC"), tr("ROK"), tr("Singapore"), tr("Turkey"), tr("UCT"), 271 tr("ROC"), tr("ROK"), tr("Singapore"), tr("Turkey"), tr("UCT"),
271 tr("W-SU"), tr("WET"), tr("Zulu"), 272 tr("W-SU"), tr("WET"), tr("Zulu"),
272 }; 273 };
274 } else if (type == typeid(Settings::AudioMode)) {
275 return {
276 tr("Mono"),
277 tr("Stereo"),
278 tr("Surround"),
279 };
273 } 280 }
274 281
275 return {}; 282 return {};
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index f39e3fccb..d7b7ed164 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -24,7 +24,7 @@
24 24
25namespace ConfigurationShared { 25namespace ConfigurationShared {
26 26
27QPushButton* Widget::CreateRestoreGlobalButton(Settings::BasicSetting& setting, QWidget* parent) { 27QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
28 QStyle* style = parent->style(); 28 QStyle* style = parent->style();
29 QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton)); 29 QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton));
30 QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent); 30 QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent);
@@ -34,8 +34,8 @@ QPushButton* Widget::CreateRestoreGlobalButton(Settings::BasicSetting& setting,
34 sp_retain.setRetainSizeWhenHidden(true); 34 sp_retain.setRetainSizeWhenHidden(true);
35 restore_button->setSizePolicy(sp_retain); 35 restore_button->setSizePolicy(sp_retain);
36 36
37 restore_button->setEnabled(!setting.UsingGlobal()); 37 restore_button->setEnabled(!using_global);
38 restore_button->setVisible(!setting.UsingGlobal()); 38 restore_button->setVisible(!using_global);
39 39
40 return restore_button; 40 return restore_button;
41} 41}
@@ -57,6 +57,10 @@ QHBoxLayout* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const
57 : Qt::CheckState::Unchecked); 57 : Qt::CheckState::Unchecked);
58 checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 58 checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
59 59
60 if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) {
61 checkbox->setEnabled(false);
62 }
63
60 layout->addWidget(checkbox); 64 layout->addWidget(checkbox);
61 65
62 layout->setContentsMargins(0, 0, 0, 0); 66 layout->setContentsMargins(0, 0, 0, 0);
@@ -70,7 +74,8 @@ QHBoxLayout* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const
70 bool_setting->LoadString(checkbox->checkState() == Qt::Checked ? "true" : "false"); 74 bool_setting->LoadString(checkbox->checkState() == Qt::Checked ? "true" : "false");
71 }; 75 };
72 } else { 76 } else {
73 restore_button = CreateRestoreGlobalButton(*bool_setting, this); 77 restore_button =
78 CreateRestoreGlobalButton(bool_setting->UsingGlobal() && setting.UsingGlobal(), this);
74 layout->addWidget(restore_button); 79 layout->addWidget(restore_button);
75 80
76 QObject::connect(checkbox, &QCheckBox::stateChanged, [=](int) { 81 QObject::connect(checkbox, &QCheckBox::stateChanged, [=](int) {
@@ -128,7 +133,7 @@ void Widget::CreateCombobox(const QString& label, std::function<void()>& load_fu
128 if (Settings::IsConfiguringGlobal()) { 133 if (Settings::IsConfiguringGlobal()) {
129 load_func = [=]() { setting.LoadString(std::to_string(combobox->currentIndex())); }; 134 load_func = [=]() { setting.LoadString(std::to_string(combobox->currentIndex())); };
130 } else { 135 } else {
131 restore_button = CreateRestoreGlobalButton(setting, this); 136 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
132 layout->addWidget(restore_button); 137 layout->addWidget(restore_button);
133 138
134 QObject::connect(restore_button, &QAbstractButton::clicked, [&](bool) { 139 QObject::connect(restore_button, &QAbstractButton::clicked, [&](bool) {
@@ -194,7 +199,7 @@ void Widget::CreateLineEdit(const QString& label, std::function<void()>& load_fu
194 }; 199 };
195 } else { 200 } else {
196 if (!has_checkbox) { 201 if (!has_checkbox) {
197 restore_button = CreateRestoreGlobalButton(setting, this); 202 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
198 layout->addWidget(restore_button); 203 layout->addWidget(restore_button);
199 } 204 }
200 205
@@ -223,7 +228,7 @@ void Widget::CreateLineEdit(const QString& label, std::function<void()>& load_fu
223} 228}
224 229
225void Widget::CreateSlider(const QString& label, bool reversed, float multiplier, 230void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
226 std::function<void()>& load_func, bool managed, 231 std::function<void()>& load_func, bool managed, const QString& format,
227 Settings::BasicSetting* const other_setting) { 232 Settings::BasicSetting* const other_setting) {
228 created = true; 233 created = true;
229 234
@@ -242,15 +247,16 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
242 247
243 int max_val = std::stoi(setting.MaxVal()); 248 int max_val = std::stoi(setting.MaxVal());
244 249
250 const QString use_format = format == QStringLiteral("") ? QStringLiteral("%1") : format;
251
245 QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) { 252 QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
246 int present = (reversed ? max_val - value : value) * multiplier; 253 int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
247 feedback->setText( 254 feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
248 QStringLiteral("%1%").arg(QString::fromStdString(std::to_string(present))));
249 }); 255 });
250 256
251 slider->setValue(std::stoi(setting.ToString()));
252 slider->setMinimum(std::stoi(setting.MinVal())); 257 slider->setMinimum(std::stoi(setting.MinVal()));
253 slider->setMaximum(max_val); 258 slider->setMaximum(max_val);
259 slider->setValue(std::stoi(setting.ToString()));
254 260
255 slider->setInvertedAppearance(reversed); 261 slider->setInvertedAppearance(reversed);
256 262
@@ -261,7 +267,7 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
261 if (Settings::IsConfiguringGlobal()) { 267 if (Settings::IsConfiguringGlobal()) {
262 load_func = [=]() { setting.LoadString(std::to_string(slider->value())); }; 268 load_func = [=]() { setting.LoadString(std::to_string(slider->value())); };
263 } else { 269 } else {
264 restore_button = CreateRestoreGlobalButton(setting, this); 270 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
265 layout->addWidget(restore_button); 271 layout->addWidget(restore_button);
266 272
267 QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) { 273 QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) {
@@ -287,7 +293,7 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
287} 293}
288 294
289void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed, 295void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed,
290 const std::string& suffix, Settings::BasicSetting* other_setting) { 296 const QString& suffix, Settings::BasicSetting* other_setting) {
291 const bool has_checkbox = other_setting != nullptr; 297 const bool has_checkbox = other_setting != nullptr;
292 if (has_checkbox && other_setting->TypeId() != typeid(bool)) { 298 if (has_checkbox && other_setting->TypeId() != typeid(bool)) {
293 LOG_WARNING(Frontend, "Extra setting requested but setting is not boolean"); 299 LOG_WARNING(Frontend, "Extra setting requested but setting is not boolean");
@@ -315,7 +321,7 @@ void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_fun
315 spinbox = new QSpinBox(this); 321 spinbox = new QSpinBox(this);
316 spinbox->setRange(min_val, max_val); 322 spinbox->setRange(min_val, max_val);
317 spinbox->setValue(default_val); 323 spinbox->setValue(default_val);
318 spinbox->setSuffix(QString::fromStdString(suffix)); 324 spinbox->setSuffix(suffix);
319 spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 325 spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
320 326
321 layout->insertWidget(1, spinbox); 327 layout->insertWidget(1, spinbox);
@@ -327,7 +333,8 @@ void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_fun
327 }; 333 };
328 } else { 334 } else {
329 if (!has_checkbox) { 335 if (!has_checkbox) {
330 restore_button = CreateRestoreGlobalButton(setting, this); 336 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
337 layout->addWidget(restore_button);
331 } 338 }
332 339
333 QObject::connect(restore_button, &QAbstractButton::clicked, 340 QObject::connect(restore_button, &QAbstractButton::clicked,
@@ -382,7 +389,7 @@ void Widget::CreateHexEdit(const QString& label, std::function<void()>& load_fun
382 setting.LoadString(hex_to_dec()); 389 setting.LoadString(hex_to_dec());
383 }; 390 };
384 } else { 391 } else {
385 restore_button = CreateRestoreGlobalButton(setting, this); 392 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
386 layout->addWidget(restore_button); 393 layout->addWidget(restore_button);
387 394
388 QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) { 395 QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) {
@@ -465,7 +472,7 @@ void Widget::CreateDateTimeEdit(const QString& label, std::function<void()>& loa
465 }; 472 };
466 } else { 473 } else {
467 if (!has_checkbox) { 474 if (!has_checkbox) {
468 restore_button = CreateRestoreGlobalButton(setting, this); 475 restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
469 layout->addWidget(restore_button); 476 layout->addWidget(restore_button);
470 } 477 }
471 478
@@ -515,12 +522,12 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
515 apply_funcs{apply_funcs_} {} 522 apply_funcs{apply_funcs_} {}
516 523
517Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_, 524Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_,
518 QWidget* parent_, bool runtime_lock, 525 QWidget* parent_, bool runtime_lock_,
519 std::forward_list<std::function<void(bool)>>& apply_funcs_, RequestType request, 526 std::forward_list<std::function<void(bool)>>& apply_funcs_, RequestType request,
520 bool managed, float multiplier, Settings::BasicSetting* other_setting, 527 bool managed, float multiplier, Settings::BasicSetting* other_setting,
521 const std::string& string) 528 const QString& string)
522 : QWidget(parent_), parent{parent_}, translations{translations_}, setting{*setting_}, 529 : QWidget(parent_), parent{parent_}, translations{translations_}, setting{*setting_},
523 apply_funcs{apply_funcs_} { 530 apply_funcs{apply_funcs_}, runtime_lock{runtime_lock_} {
524 if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) { 531 if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) {
525 LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel()); 532 LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel());
526 return; 533 return;
@@ -547,23 +554,16 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
547 std::function<void()> load_func = []() {}; 554 std::function<void()> load_func = []() {};
548 555
549 if (type == typeid(bool)) { 556 if (type == typeid(bool)) {
550 switch (request) { 557 CreateCheckBox(&setting, label, load_func, managed);
551 case RequestType::Default:
552 CreateCheckBox(&setting, label, load_func, managed);
553 break;
554 default:
555 LOG_WARNING(Frontend, "Requested widget is unimplemented.");
556 break;
557 }
558 } else if (setting.IsEnum()) { 558 } else if (setting.IsEnum()) {
559 CreateCombobox(label, load_func, managed); 559 CreateCombobox(label, load_func, managed);
560 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || 560 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
561 type == typeid(s64)) { 561 type == typeid(s64) || type == typeid(u8)) {
562 switch (request) { 562 switch (request) {
563 case RequestType::Slider: 563 case RequestType::Slider:
564 case RequestType::ReverseSlider: 564 case RequestType::ReverseSlider:
565 CreateSlider(label, request == RequestType::ReverseSlider, multiplier, load_func, 565 CreateSlider(label, request == RequestType::ReverseSlider, multiplier, load_func,
566 managed); 566 managed, string);
567 break; 567 break;
568 case RequestType::LineEdit: 568 case RequestType::LineEdit:
569 case RequestType::Default: 569 case RequestType::Default:
@@ -586,7 +586,23 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
586 break; 586 break;
587 } 587 }
588 } else if (type == typeid(std::string)) { 588 } else if (type == typeid(std::string)) {
589 CreateLineEdit(label, load_func, managed); 589 switch (request) {
590 case RequestType::Default:
591 case RequestType::LineEdit:
592 CreateLineEdit(label, load_func, managed);
593 break;
594 case RequestType::ComboBox:
595 CreateCombobox(label, load_func, false);
596 break;
597 case RequestType::SpinBox:
598 case RequestType::Slider:
599 case RequestType::ReverseSlider:
600 case RequestType::HexEdit:
601 case RequestType::DateTimeEdit:
602 case RequestType::MaxEnum:
603 LOG_WARNING(Frontend, "Requested widget is unimplemented.");
604 break;
605 }
590 } 606 }
591 607
592 if (!created) { 608 if (!created) {
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
index c4e686574..331316040 100644
--- a/src/yuzu/configuration/shared_widget.h
+++ b/src/yuzu/configuration/shared_widget.h
@@ -38,15 +38,15 @@ public:
38 Widget(Settings::BasicSetting* setting, const TranslationMap& translations, QWidget* parent, 38 Widget(Settings::BasicSetting* setting, const TranslationMap& translations, QWidget* parent,
39 bool runtime_lock, std::forward_list<std::function<void(bool)>>& apply_funcs_, 39 bool runtime_lock, std::forward_list<std::function<void(bool)>>& apply_funcs_,
40 RequestType request = RequestType::Default, bool managed = true, float multiplier = 1.0f, 40 RequestType request = RequestType::Default, bool managed = true, float multiplier = 1.0f,
41 Settings::BasicSetting* other_setting = nullptr, const std::string& format = ""); 41 Settings::BasicSetting* other_setting = nullptr,
42 const QString& string = QStringLiteral(""));
42 Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_, QWidget* parent_, 43 Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_, QWidget* parent_,
43 std::forward_list<std::function<void(bool)>>& apply_funcs_); 44 std::forward_list<std::function<void(bool)>>& apply_funcs_);
44 virtual ~Widget(); 45 virtual ~Widget();
45 46
46 bool Valid(); 47 bool Valid();
47 48
48 [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(Settings::BasicSetting& setting, 49 [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent);
49 QWidget* parent);
50 50
51 QPushButton* restore_button{}; 51 QPushButton* restore_button{};
52 QLineEdit* line_edit{}; 52 QLineEdit* line_edit{};
@@ -68,12 +68,12 @@ private:
68 void CreateHexEdit(const QString& label, std::function<void()>& load_func, bool managed, 68 void CreateHexEdit(const QString& label, std::function<void()>& load_func, bool managed,
69 Settings::BasicSetting* const other_setting = nullptr); 69 Settings::BasicSetting* const other_setting = nullptr);
70 void CreateSlider(const QString& label, bool reversed, float multiplier, 70 void CreateSlider(const QString& label, bool reversed, float multiplier,
71 std::function<void()>& load_func, bool managed, 71 std::function<void()>& load_func, bool managed, const QString& format,
72 Settings::BasicSetting* const other_setting = nullptr); 72 Settings::BasicSetting* const other_setting = nullptr);
73 void CreateDateTimeEdit(const QString& label, std::function<void()>& load_func, bool managed, 73 void CreateDateTimeEdit(const QString& label, std::function<void()>& load_func, bool managed,
74 bool restrict, Settings::BasicSetting* const other_setting = nullptr); 74 bool restrict, Settings::BasicSetting* const other_setting = nullptr);
75 void CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed, 75 void CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed,
76 const std::string& suffix, Settings::BasicSetting* other_setting = nullptr); 76 const QString& suffix, Settings::BasicSetting* other_setting = nullptr);
77 77
78 QWidget* parent; 78 QWidget* parent;
79 const TranslationMap& translations; 79 const TranslationMap& translations;
@@ -81,6 +81,7 @@ private:
81 std::forward_list<std::function<void(bool)>>& apply_funcs; 81 std::forward_list<std::function<void(bool)>>& apply_funcs;
82 82
83 bool created{false}; 83 bool created{false};
84 bool runtime_lock{false};
84}; 85};
85 86
86} // namespace ConfigurationShared 87} // namespace ConfigurationShared