From 432f68ad29df7a368ba375d75d667c954e9c80b9 Mon Sep 17 00:00:00 2001
From: lat9nq <22451773+lat9nq@users.noreply.github.com>
Date: Thu, 18 May 2023 17:54:22 -0400
Subject: [PATCH] configure_audio: Implement ui generation

Needs a considerable amount of management specific to some of
the comoboboxes due to the audio engine configuration.

general: Partial audio config implmentation

configure_audio: Implement ui generation

Needs a considerable amount of management specific to some of
the comoboboxes due to the audio engine configuration.

general: Partial audio config implmentation

settings: Make audio settings as enums
---
 src/audio_core/sink/sink_details.cpp          |  33 +--
 src/audio_core/sink/sink_details.h            |   9 +-
 src/common/settings.cpp                       |   2 +-
 src/common/settings.h                         |  11 +-
 src/core/telemetry_session.cpp                |   3 +-
 src/yuzu/configuration/configure_audio.cpp    | 208 +++++++++---------
 src/yuzu/configuration/configure_audio.h      |  16 +-
 src/yuzu/configuration/configure_audio.ui     | 162 +-------------
 src/yuzu/configuration/configure_dialog.cpp   |   2 +-
 src/yuzu/configuration/configure_graphics.cpp |   8 +-
 src/yuzu/configuration/configure_per_game.cpp |   2 +-
 src/yuzu/configuration/shared_translation.cpp |   7 +
 src/yuzu/configuration/shared_widget.cpp      |  78 ++++---
 src/yuzu/configuration/shared_widget.h        |  11 +-
 14 files changed, 221 insertions(+), 331 deletions(-)

diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 39ea6d91b8..751e97bfca 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -15,6 +15,7 @@
 #endif
 #include "audio_core/sink/null_sink.h"
 #include "common/logging/log.h"
+#include "common/settings_enums.h"
 
 namespace AudioCore::Sink {
 namespace {
@@ -24,7 +25,7 @@ struct SinkDetails {
     using LatencyFn = u32 (*)();
 
     /// Name for this sink.
-    std::string_view id;
+    Settings::AudioEngine id;
     /// A method to call to construct an instance of this type of sink.
     FactoryFn factory;
     /// A method to call to list available devices.
@@ -37,7 +38,7 @@ struct SinkDetails {
 constexpr SinkDetails sink_details[] = {
 #ifdef HAVE_CUBEB
     SinkDetails{
-        "cubeb",
+        Settings::AudioEngine::Cubeb,
         [](std::string_view device_id) -> std::unique_ptr<Sink> {
             return std::make_unique<CubebSink>(device_id);
         },
@@ -47,7 +48,7 @@ constexpr SinkDetails sink_details[] = {
 #endif
 #ifdef HAVE_SDL2
     SinkDetails{
-        "sdl2",
+        Settings::AudioEngine::Sdl2,
         [](std::string_view device_id) -> std::unique_ptr<Sink> {
             return std::make_unique<SDLSink>(device_id);
         },
@@ -55,46 +56,46 @@ constexpr SinkDetails sink_details[] = {
         &GetSDLLatency,
     },
 #endif
-    SinkDetails{"null",
+    SinkDetails{Settings::AudioEngine::Null,
                 [](std::string_view device_id) -> std::unique_ptr<Sink> {
                     return std::make_unique<NullSink>(device_id);
                 },
                 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
 };
 
-const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) {
-    const auto find_backend{[](std::string_view id) {
+const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
+    const auto find_backend{[](Settings::AudioEngine id) {
         return std::find_if(std::begin(sink_details), std::end(sink_details),
                             [&id](const auto& sink_detail) { return sink_detail.id == id; });
     }};
 
     auto iter = find_backend(sink_id);
 
-    if (sink_id == "auto") {
+    if (sink_id == Settings::AudioEngine::Auto) {
         // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
         // causes audio issues, in that case go with SDL.
 #if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
-        iter = find_backend("cubeb");
+        iter = find_backend(Settings::AudioEngine::Cubeb);
         if (iter->latency() > TargetSampleCount * 3) {
-            iter = find_backend("sdl2");
+            iter = find_backend(Settings::AudioEngine::Sdl2);
         }
 #else
         iter = std::begin(sink_details);
 #endif
-        LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id);
+        LOG_INFO(Service_Audio, "Auto-selecting the {} backend", Settings::TranslateEnum(iter->id));
     }
 
     if (iter == std::end(sink_details)) {
-        LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
-        iter = find_backend("null");
+        LOG_ERROR(Audio, "Invalid sink_id {}", Settings::TranslateEnum(sink_id));
+        iter = find_backend(Settings::AudioEngine::Null);
     }
 
     return *iter;
 }
 } // Anonymous namespace
 
-std::vector<std::string_view> GetSinkIDs() {
-    std::vector<std::string_view> sink_ids(std::size(sink_details));
+std::vector<Settings::AudioEngine> GetSinkIDs() {
+    std::vector<Settings::AudioEngine> sink_ids(std::size(sink_details));
 
     std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
                    [](const auto& sink) { return sink.id; });
@@ -102,11 +103,11 @@ std::vector<std::string_view> GetSinkIDs() {
     return sink_ids;
 }
 
-std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture) {
+std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture) {
     return GetOutputSinkDetails(sink_id).list_devices(capture);
 }
 
-std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) {
+std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id) {
     return GetOutputSinkDetails(sink_id).factory(device_id);
 }
 
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index e75932898c..44403db718 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -7,6 +7,9 @@
 #include <string_view>
 #include <vector>
 
+namespace Settings {
+enum class AudioEngine : u32;
+}
 namespace AudioCore {
 class AudioManager;
 
@@ -19,7 +22,7 @@ class Sink;
  *
  * @return Vector of available sink names.
  */
-std::vector<std::string_view> GetSinkIDs();
+std::vector<Settings::AudioEngine> GetSinkIDs();
 
 /**
  * Gets the list of devices for a particular sink identified by the given ID.
@@ -28,7 +31,7 @@ std::vector<std::string_view> GetSinkIDs();
  * @param capture - Get capture (input) devices, or output devices?
  * @return Vector of device names.
  */
-std::vector<std::string> GetDeviceListForSink(std::string_view sink_id, bool capture);
+std::vector<std::string> GetDeviceListForSink(Settings::AudioEngine sink_id, bool capture);
 
 /**
  * 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
  * @param device_id - Name of the device to create.
  * @return Pointer to the created sink.
  */
-std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id);
+std::unique_ptr<Sink> CreateSinkFromID(Settings::AudioEngine sink_id, std::string_view device_id);
 
 } // namespace Sink
 } // namespace AudioCore
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index c8651925e5..8bfda56678 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -90,7 +90,7 @@ void LogSettings() {
     log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
     log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
     log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
-    log_setting("Audio_OutputEngine", values.sink_id.GetValue());
+    log_setting("Audio_OutputEngine", Settings::TranslateEnum(values.sink_id.GetValue()));
     log_setting("Audio_OutputDevice", values.audio_output_device_id.GetValue());
     log_setting("Audio_InputDevice", values.audio_input_device_id.GetValue());
     log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue());
diff --git a/src/common/settings.h b/src/common/settings.h
index 0ac5078c6e..d4b41a1627 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -244,6 +244,8 @@ protected:
             return value_.has_value() ? std::to_string(*value_) : "none";
         } else if constexpr (std::is_same<Type, bool>()) {
             return value_ ? "true" : "false";
+        } else if (std::is_same<Type, AudioEngine>()) {
+            return TranslateEnum(value_);
         } else {
             return std::to_string(static_cast<u64>(value_));
         }
@@ -309,6 +311,8 @@ public:
                 this->SetValue(static_cast<u32>(std::stoul(input)));
             } else if constexpr (std::is_same<Type, bool>()) {
                 this->SetValue(input == "true");
+            } else if constexpr (std::is_same<Type, AudioEngine>()) {
+                this->SetValue(ToEnum<Type>(input));
             } else {
                 this->SetValue(static_cast<Type>(std::stoll(input)));
             }
@@ -542,7 +546,7 @@ struct Values {
     Linkage linkage{};
 
     // Audio
-    Setting<std::string> sink_id{linkage, "auto", "output_engine", Category::Audio};
+    Setting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", Category::Audio};
     Setting<std::string> audio_output_device_id{linkage, "auto", "output_device", Category::Audio};
     Setting<std::string> audio_input_device_id{linkage, "auto", "input_device", Category::Audio};
     Setting<bool, false> audio_muted{linkage, false, "audio_muted", Category::Audio, false};
@@ -731,8 +735,9 @@ struct Values {
     SwitchableSetting<TimeZone, true> time_zone_index{linkage,           TimeZone::Auto,
                                                       TimeZone::Auto,    TimeZone::Zulu,
                                                       "time_zone_index", Category::System};
-    SwitchableSetting<s32, true> sound_index{
-        linkage, 1, 0, 2, "sound_index", Category::SystemAudio};
+    SwitchableSetting<AudioMode, true> sound_index{linkage,         AudioMode::Stereo,
+                                                   AudioMode::Mono, AudioMode::Surround,
+                                                   "sound_index",   Category::SystemAudio};
 
     SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System};
 
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index a3505a505f..c058ac2c75 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -254,7 +254,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
 
     // Log user configuration information
     constexpr auto field_type = Telemetry::FieldType::UserConfig;
-    AddField(field_type, "Audio_SinkId", Settings::values.sink_id.GetValue());
+    AddField(field_type, "Audio_SinkId",
+             Settings::TranslateEnum(Settings::values.sink_id.GetValue()));
     AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core.GetValue());
     AddField(field_type, "Renderer_Backend",
              TranslateRenderer(Settings::values.renderer_backend.GetValue()));
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 3356621440..dd9eb4dc1b 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -1,6 +1,7 @@
 // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include <forward_list>
 #include <memory>
 
 #include "audio_core/sink/sink.h"
@@ -10,80 +11,105 @@
 #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<std::forward_list<ConfigurationShared::Tab*>> group,
+                               const ConfigurationShared::TranslationMap& translations_,
                                QWidget* parent)
-    : Tab(group, parent), ui(std::make_unique<Ui::ConfigureAudio>()), system{system_} {
+    : Tab(group, parent),
+      ui(std::make_unique<Ui::ConfigureAudio>()), system{system_}, translations{translations_} {
     ui->setupUi(this);
-
-    InitializeAudioSinkComboBox();
-
-    connect(ui->volume_slider, &QSlider::valueChanged, this,
-            &ConfigureAudio::SetVolumeIndicatorText);
-    connect(ui->sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
-            &ConfigureAudio::UpdateAudioDevices);
-
-    ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
-    ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
-
-    SetupPerGameUI();
+    Setup();
 
     SetConfiguration();
-
-    const bool is_powered_on = system_.IsPoweredOn();
-    ui->sink_combo_box->setEnabled(!is_powered_on);
-    ui->output_combo_box->setEnabled(!is_powered_on);
-    ui->input_combo_box->setEnabled(!is_powered_on);
 }
 
 ConfigureAudio::~ConfigureAudio() = default;
 
+void ConfigureAudio::Setup() {
+    const bool runtime_lock = !system.IsPoweredOn();
+    auto& layout = *ui->audio_widget->layout();
+
+    std::forward_list<Settings::BasicSetting*> settings;
+
+    auto push = [&](Settings::Category category) {
+        for (auto* setting : Settings::values.linkage.by_category[category]) {
+            settings.push_front(setting);
+        }
+    };
+
+    push(Settings::Category::Audio);
+    push(Settings::Category::SystemAudio);
+
+    for (auto* setting : settings) {
+        auto* widget = [&]() {
+            if (setting->Id() == Settings::values.volume.Id()) {
+                return new ConfigurationShared::Widget(
+                    setting, translations, this, runtime_lock, apply_funcs,
+                    ConfigurationShared::RequestType::Slider, true, 1.0f, nullptr,
+                    tr("%1%", "Volume percentage (e.g. 50%)"));
+            } else if (setting->Id() == Settings::values.audio_output_device_id.Id() ||
+                       setting->Id() == Settings::values.audio_input_device_id.Id() ||
+                       setting->Id() == Settings::values.sink_id.Id()) {
+                return new ConfigurationShared::Widget(
+                    setting, translations, this, runtime_lock, apply_funcs,
+                    ConfigurationShared::RequestType::ComboBox, false);
+            } else {
+                return new ConfigurationShared::Widget(setting, translations, this, runtime_lock,
+                                                       apply_funcs);
+            }
+        }();
+
+        if (!widget->Valid()) {
+            delete widget;
+            continue;
+        }
+
+        layout.addWidget(widget);
+
+        if (setting->Id() == Settings::values.sink_id.Id()) {
+            sink_combo_box = widget->combobox;
+            InitializeAudioSinkComboBox();
+
+            connect(sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
+                    &ConfigureAudio::UpdateAudioDevices);
+        } else if (setting->Id() == Settings::values.audio_output_device_id.Id()) {
+            output_device_combo_box = widget->combobox;
+        } else if (setting->Id() == Settings::values.audio_input_device_id.Id()) {
+            input_device_combo_box = widget->combobox;
+        }
+    }
+}
+
 void ConfigureAudio::SetConfiguration() {
+    if (!Settings::IsConfiguringGlobal()) {
+        return;
+    }
+
     SetOutputSinkFromSinkID();
 
     // The device list cannot be pre-populated (nor listed) until the output sink is known.
-    UpdateAudioDevices(ui->sink_combo_box->currentIndex());
+    UpdateAudioDevices(sink_combo_box->currentIndex());
 
     SetAudioDevicesFromDeviceID();
-
-    const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
-    ui->volume_slider->setValue(volume_value);
-    ui->toggle_background_mute->setChecked(UISettings::values.mute_when_in_background.GetValue());
-
-    if (!Settings::IsConfiguringGlobal()) {
-        if (Settings::values.volume.UsingGlobal()) {
-            ui->volume_combo_box->setCurrentIndex(0);
-            ui->volume_slider->setEnabled(false);
-        } else {
-            ui->volume_combo_box->setCurrentIndex(1);
-            ui->volume_slider->setEnabled(true);
-        }
-        ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index);
-        ConfigurationShared::SetHighlight(ui->mode_label,
-                                          !Settings::values.sound_index.UsingGlobal());
-        ConfigurationShared::SetHighlight(ui->volume_layout,
-                                          !Settings::values.volume.UsingGlobal());
-    } else {
-        ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue());
-    }
-    SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
 }
 
 void ConfigureAudio::SetOutputSinkFromSinkID() {
-    [[maybe_unused]] const QSignalBlocker blocker(ui->sink_combo_box);
+    [[maybe_unused]] const QSignalBlocker blocker(sink_combo_box);
 
     int new_sink_index = 0;
-    const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
-    for (int index = 0; index < ui->sink_combo_box->count(); index++) {
-        if (ui->sink_combo_box->itemText(index) == sink_id) {
+    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;
         }
     }
 
-    ui->sink_combo_box->setCurrentIndex(new_sink_index);
+    sink_combo_box->setCurrentIndex(new_sink_index);
 }
 
 void ConfigureAudio::SetAudioDevicesFromDeviceID() {
@@ -91,57 +117,42 @@ void ConfigureAudio::SetAudioDevicesFromDeviceID() {
 
     const QString output_device_id =
         QString::fromStdString(Settings::values.audio_output_device_id.GetValue());
-    for (int index = 0; index < ui->output_combo_box->count(); index++) {
-        if (ui->output_combo_box->itemText(index) == output_device_id) {
+    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;
         }
     }
 
-    ui->output_combo_box->setCurrentIndex(new_device_index);
+    output_device_combo_box->setCurrentIndex(new_device_index);
 
     new_device_index = -1;
     const QString input_device_id =
         QString::fromStdString(Settings::values.audio_input_device_id.GetValue());
-    for (int index = 0; index < ui->input_combo_box->count(); index++) {
-        if (ui->input_combo_box->itemText(index) == input_device_id) {
+    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;
         }
     }
 
-    ui->input_combo_box->setCurrentIndex(new_device_index);
-}
-
-void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
-    ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage));
+    input_device_combo_box->setCurrentIndex(new_device_index);
 }
 
 void ConfigureAudio::ApplyConfiguration() {
-    ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound);
+    const bool is_powered_on = system.IsPoweredOn();
+    for (const auto& apply_func : apply_funcs) {
+        apply_func(is_powered_on);
+    }
 
     if (Settings::IsConfiguringGlobal()) {
-        Settings::values.sink_id =
-            ui->sink_combo_box->itemText(ui->sink_combo_box->currentIndex()).toStdString();
+        Settings::values.sink_id.LoadString(
+            sink_combo_box->itemText(sink_combo_box->currentIndex()).toStdString());
         Settings::values.audio_output_device_id.SetValue(
-            ui->output_combo_box->itemText(ui->output_combo_box->currentIndex()).toStdString());
+            output_device_combo_box->itemText(output_device_combo_box->currentIndex())
+                .toStdString());
         Settings::values.audio_input_device_id.SetValue(
-            ui->input_combo_box->itemText(ui->input_combo_box->currentIndex()).toStdString());
-        UISettings::values.mute_when_in_background = ui->toggle_background_mute->isChecked();
-
-        // Guard if during game and set to game-specific value
-        if (Settings::values.volume.UsingGlobal()) {
-            const auto volume = static_cast<u8>(ui->volume_slider->value());
-            Settings::values.volume.SetValue(volume);
-        }
-    } else {
-        if (ui->volume_combo_box->currentIndex() == 0) {
-            Settings::values.volume.SetGlobal(true);
-        } else {
-            Settings::values.volume.SetGlobal(false);
-            const auto volume = static_cast<u8>(ui->volume_slider->value());
-            Settings::values.volume.SetValue(volume);
-        }
+            input_device_combo_box->itemText(input_device_combo_box->currentIndex()).toStdString());
     }
 }
 
@@ -154,54 +165,31 @@ void ConfigureAudio::changeEvent(QEvent* event) {
 }
 
 void ConfigureAudio::UpdateAudioDevices(int sink_index) {
-    ui->output_combo_box->clear();
-    ui->output_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
+    output_device_combo_box->clear();
+    output_device_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
 
-    const std::string sink_id = ui->sink_combo_box->itemText(sink_index).toStdString();
+    const auto sink_id =
+        Settings::ToEnum<Settings::AudioEngine>(sink_combo_box->itemText(sink_index).toStdString());
     for (const auto& device : AudioCore::Sink::GetDeviceListForSink(sink_id, false)) {
-        ui->output_combo_box->addItem(QString::fromStdString(device));
+        output_device_combo_box->addItem(QString::fromStdString(device));
     }
 
-    ui->input_combo_box->clear();
-    ui->input_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
+    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)) {
-        ui->input_combo_box->addItem(QString::fromStdString(device));
+        input_device_combo_box->addItem(QString::fromStdString(device));
     }
 }
 
 void ConfigureAudio::InitializeAudioSinkComboBox() {
-    ui->sink_combo_box->clear();
-    ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
+    sink_combo_box->clear();
+    sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
 
     for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
-        ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length())));
+        sink_combo_box->addItem(QString::fromStdString(Settings::TranslateEnum(id)));
     }
 }
 
 void ConfigureAudio::RetranslateUI() {
     ui->retranslateUi(this);
-    SetVolumeIndicatorText(ui->volume_slider->sliderPosition());
-}
-
-void ConfigureAudio::SetupPerGameUI() {
-    if (Settings::IsConfiguringGlobal()) {
-        ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal());
-        ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
-        return;
-    }
-
-    ConfigurationShared::SetColoredComboBox(ui->combo_sound, ui->mode_label,
-                                            Settings::values.sound_index.GetValue(true));
-
-    connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
-        ui->volume_slider->setEnabled(index == 1);
-        ConfigurationShared::SetHighlight(ui->volume_layout, index == 1);
-    });
-
-    ui->sink_combo_box->setVisible(false);
-    ui->sink_label->setVisible(false);
-    ui->output_combo_box->setVisible(false);
-    ui->output_label->setVisible(false);
-    ui->input_combo_box->setVisible(false);
-    ui->input_label->setVisible(false);
 }
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
index d134ac957f..170e0bce86 100644
--- a/src/yuzu/configuration/configure_audio.h
+++ b/src/yuzu/configuration/configure_audio.h
@@ -3,9 +3,14 @@
 
 #pragma once
 
+#include <forward_list>
+#include <functional>
 #include <memory>
 #include <QWidget>
 #include "yuzu/configuration/configuration_shared.h"
+#include "yuzu/configuration/shared_translation.h"
+
+class QPushButton;
 
 namespace Core {
 class System;
@@ -19,6 +24,7 @@ class ConfigureAudio : public ConfigurationShared::Tab {
 public:
     explicit ConfigureAudio(const Core::System& system_,
                             std::shared_ptr<std::forward_list<ConfigurationShared::Tab*>> group,
+                            const ConfigurationShared::TranslationMap& translations_,
                             QWidget* parent = nullptr);
     ~ConfigureAudio() override;
 
@@ -36,11 +42,17 @@ private:
 
     void SetOutputSinkFromSinkID();
     void SetAudioDevicesFromDeviceID();
-    void SetVolumeIndicatorText(int percentage);
 
-    void SetupPerGameUI();
+    void Setup();
 
     std::unique_ptr<Ui::ConfigureAudio> ui;
 
     const Core::System& system;
+    const ConfigurationShared::TranslationMap& translations;
+
+    std::forward_list<std::function<void(bool)>> apply_funcs{};
+
+    QComboBox* sink_combo_box;
+    QComboBox* output_device_combo_box;
+    QComboBox* input_device_combo_box;
 };
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
index 4128c83ad4..1181aeb007 100644
--- a/src/yuzu/configuration/configure_audio.ui
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -21,80 +21,14 @@
      </property>
      <layout class="QVBoxLayout">
       <item>
-       <layout class="QHBoxLayout" name="engine_layout">
-        <item>
-         <widget class="QLabel" name="sink_label">
-          <property name="text">
-           <string>Output Engine:</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QComboBox" name="sink_combo_box"/>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <layout class="QHBoxLayout" name="output_layout">
-        <item>
-         <widget class="QLabel" name="output_label">
-          <property name="text">
-           <string>Output Device:</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QComboBox" name="output_combo_box"/>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <layout class="QHBoxLayout" name="input_layout">
-        <item>
-         <widget class="QLabel" name="input_label">
-          <property name="text">
-           <string>Input Device:</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QComboBox" name="input_combo_box"/>
-        </item>
-       </layout>
-      </item>
-       <item>
-       <layout class="QHBoxLayout" name="mode_layout">
-        <item>
-         <widget class="QLabel" name="mode_label">
-          <property name="text">
-           <string>Sound Output Mode:</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-          <widget class="QComboBox" name="combo_sound">
-            <item>
-              <property name="text">
-                <string>Mono</string>
-              </property>
-            </item>
-            <item>
-              <property name="text">
-                <string>Stereo</string>
-              </property>
-            </item>
-            <item>
-              <property name="text">
-                <string>Surround</string>
-              </property>
-            </item>
-          </widget>
-        </item>
-       </layout>
-      </item>
-      <item>
-       <widget class="QWidget" name="volume_layout" native="true">
-        <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <widget class="QWidget" name="audio_widget" native="true">
+        <property name="maximumSize">
+         <size>
+          <width>16777215</width>
+          <height>16777213</height>
+         </size>
+        </property>
+        <layout class="QVBoxLayout" name="verticalLayout">
          <property name="leftMargin">
           <number>0</number>
          </property>
@@ -107,89 +41,9 @@
          <property name="bottomMargin">
           <number>0</number>
          </property>
-         <item>
-          <widget class="QComboBox" name="volume_combo_box">
-           <item>
-            <property name="text">
-             <string>Use global volume</string>
-            </property>
-           </item>
-           <item>
-            <property name="text">
-             <string>Set volume:</string>
-            </property>
-           </item>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="volume_label">
-           <property name="text">
-            <string>Volume:</string>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <spacer name="horizontalSpacer">
-           <property name="orientation">
-            <enum>Qt::Horizontal</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>30</width>
-             <height>20</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QSlider" name="volume_slider">
-           <property name="sizePolicy">
-            <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-             <horstretch>0</horstretch>
-             <verstretch>0</verstretch>
-            </sizepolicy>
-           </property>
-           <property name="maximum">
-            <number>200</number>
-           </property>
-           <property name="pageStep">
-            <number>5</number>
-           </property>
-           <property name="orientation">
-            <enum>Qt::Horizontal</enum>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <widget class="QLabel" name="volume_indicator">
-           <property name="minimumSize">
-            <size>
-             <width>32</width>
-             <height>0</height>
-            </size>
-           </property>
-           <property name="text">
-            <string>0 %</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
         </layout>
        </widget>
       </item>
-      <item>
-       <layout class="QHBoxLayout" name="mute_layout">
-         <item>
-           <widget class="QCheckBox" name="toggle_background_mute">
-             <property name="text">
-               <string>Mute audio when in background</string>
-             </property>
-           </widget>
-         </item>
-       </layout>
-      </item>
      </layout>
     </widget>
    </item>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 3a94fee9e8..f0f00be838 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_,
     : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
       registry(registry_), system{system_},
       translations{ConfigurationShared::InitializeTranslations(this)},
-      audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, this)},
+      audio_tab{std::make_unique<ConfigureAudio>(system_, nullptr, *translations, this)},
       cpu_tab{std::make_unique<ConfigureCpu>(system_, nullptr, this)},
       debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
       filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2354323b82..45a4db4307 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -240,12 +240,14 @@ void ConfigureGraphics::Setup() {
             } else if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
                 return new ConfigurationShared::Widget(
                     setting, translations, this, runtime_lock, apply_funcs,
-                    ConfigurationShared::RequestType::ReverseSlider, true, 0.5f);
+                    ConfigurationShared::RequestType::ReverseSlider, true, 0.5f, nullptr,
+                    tr("%1%", "FSR sharpening percentage (e.g. 50%)"));
             } else if (setting->Id() == Settings::values.speed_limit.Id()) {
                 return new ConfigurationShared::Widget(
                     setting, translations, this, runtime_lock, apply_funcs,
                     ConfigurationShared::RequestType::SpinBox, true, 1.0f,
-                    &Settings::values.use_speed_limit, "%");
+                    &Settings::values.use_speed_limit,
+                    tr("%", "Limit speed percentage (e.g. 50%)"));
             } else {
                 return new ConfigurationShared::Widget(setting, translations, this, runtime_lock,
                                                        apply_funcs);
@@ -304,7 +306,7 @@ void ConfigureGraphics::Setup() {
         });
     } else {
         QPushButton* bg_restore_button = ConfigurationShared::Widget::CreateRestoreGlobalButton(
-            Settings::values.bg_red, ui->bg_widget);
+            Settings::values.bg_red.UsingGlobal(), ui->bg_widget);
         ui->bg_widget->layout()->addWidget(bg_restore_button);
 
         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 c398553346..2ee0a8ffae 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
     game_config = std::make_unique<Config>(config_file_name, Config::ConfigType::PerGameConfig);
 
     addons_tab = std::make_unique<ConfigurePerGameAddons>(system_, this);
-    audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, this);
+    audio_tab = std::make_unique<ConfigureAudio>(system_, tab_group, *translations, this);
     cpu_tab = std::make_unique<ConfigureCpu>(system_, tab_group, this);
     graphics_advanced_tab =
         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 6038e8c254..c3b38f776f 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) {
     INSERT(Settings, audio_input_device_id, "Input Device:", "");
     INSERT(Settings, audio_muted, "Mute audio when in background", "");
     INSERT(Settings, volume, "Volume:", "");
+    INSERT(Settings, dump_audio_commands, "", "");
 
     // Core
     INSERT(Settings, use_multi_core, "Multicore CPU Emulation", "");
@@ -270,6 +271,12 @@ std::forward_list<QString> ComboboxEnumeration(std::type_index type, QWidget* pa
             tr("ROC"),     tr("ROK"),       tr("Singapore"), tr("Turkey"),    tr("UCT"),
             tr("W-SU"),    tr("WET"),       tr("Zulu"),
         };
+    } else if (type == typeid(Settings::AudioMode)) {
+        return {
+            tr("Mono"),
+            tr("Stereo"),
+            tr("Surround"),
+        };
     }
 
     return {};
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index f39e3fccb6..d7b7ed164e 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -24,7 +24,7 @@
 
 namespace ConfigurationShared {
 
-QPushButton* Widget::CreateRestoreGlobalButton(Settings::BasicSetting& setting, QWidget* parent) {
+QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
     QStyle* style = parent->style();
     QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton));
     QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent);
@@ -34,8 +34,8 @@ QPushButton* Widget::CreateRestoreGlobalButton(Settings::BasicSetting& setting,
     sp_retain.setRetainSizeWhenHidden(true);
     restore_button->setSizePolicy(sp_retain);
 
-    restore_button->setEnabled(!setting.UsingGlobal());
-    restore_button->setVisible(!setting.UsingGlobal());
+    restore_button->setEnabled(!using_global);
+    restore_button->setVisible(!using_global);
 
     return restore_button;
 }
@@ -57,6 +57,10 @@ QHBoxLayout* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const
                                                                : Qt::CheckState::Unchecked);
     checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
+    if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) {
+        checkbox->setEnabled(false);
+    }
+
     layout->addWidget(checkbox);
 
     layout->setContentsMargins(0, 0, 0, 0);
@@ -70,7 +74,8 @@ QHBoxLayout* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const
             bool_setting->LoadString(checkbox->checkState() == Qt::Checked ? "true" : "false");
         };
     } else {
-        restore_button = CreateRestoreGlobalButton(*bool_setting, this);
+        restore_button =
+            CreateRestoreGlobalButton(bool_setting->UsingGlobal() && setting.UsingGlobal(), this);
         layout->addWidget(restore_button);
 
         QObject::connect(checkbox, &QCheckBox::stateChanged, [=](int) {
@@ -128,7 +133,7 @@ void Widget::CreateCombobox(const QString& label, std::function<void()>& load_fu
     if (Settings::IsConfiguringGlobal()) {
         load_func = [=]() { setting.LoadString(std::to_string(combobox->currentIndex())); };
     } else {
-        restore_button = CreateRestoreGlobalButton(setting, this);
+        restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
         layout->addWidget(restore_button);
 
         QObject::connect(restore_button, &QAbstractButton::clicked, [&](bool) {
@@ -194,7 +199,7 @@ void Widget::CreateLineEdit(const QString& label, std::function<void()>& load_fu
         };
     } else {
         if (!has_checkbox) {
-            restore_button = CreateRestoreGlobalButton(setting, this);
+            restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
             layout->addWidget(restore_button);
         }
 
@@ -223,7 +228,7 @@ void Widget::CreateLineEdit(const QString& label, std::function<void()>& load_fu
 }
 
 void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
-                          std::function<void()>& load_func, bool managed,
+                          std::function<void()>& load_func, bool managed, const QString& format,
                           Settings::BasicSetting* const other_setting) {
     created = true;
 
@@ -242,15 +247,16 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
 
     int max_val = std::stoi(setting.MaxVal());
 
+    const QString use_format = format == QStringLiteral("") ? QStringLiteral("%1") : format;
+
     QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
-        int present = (reversed ? max_val - value : value) * multiplier;
-        feedback->setText(
-            QStringLiteral("%1%").arg(QString::fromStdString(std::to_string(present))));
+        int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
+        feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
     });
 
-    slider->setValue(std::stoi(setting.ToString()));
     slider->setMinimum(std::stoi(setting.MinVal()));
     slider->setMaximum(max_val);
+    slider->setValue(std::stoi(setting.ToString()));
 
     slider->setInvertedAppearance(reversed);
 
@@ -261,7 +267,7 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
     if (Settings::IsConfiguringGlobal()) {
         load_func = [=]() { setting.LoadString(std::to_string(slider->value())); };
     } else {
-        restore_button = CreateRestoreGlobalButton(setting, this);
+        restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
         layout->addWidget(restore_button);
 
         QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) {
@@ -287,7 +293,7 @@ void Widget::CreateSlider(const QString& label, bool reversed, float multiplier,
 }
 
 void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed,
-                           const std::string& suffix, Settings::BasicSetting* other_setting) {
+                           const QString& suffix, Settings::BasicSetting* other_setting) {
     const bool has_checkbox = other_setting != nullptr;
     if (has_checkbox && other_setting->TypeId() != typeid(bool)) {
         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
     spinbox = new QSpinBox(this);
     spinbox->setRange(min_val, max_val);
     spinbox->setValue(default_val);
-    spinbox->setSuffix(QString::fromStdString(suffix));
+    spinbox->setSuffix(suffix);
     spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
     layout->insertWidget(1, spinbox);
@@ -327,7 +333,8 @@ void Widget::CreateSpinBox(const QString& label, std::function<void()>& load_fun
         };
     } else {
         if (!has_checkbox) {
-            restore_button = CreateRestoreGlobalButton(setting, this);
+            restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
+            layout->addWidget(restore_button);
         }
 
         QObject::connect(restore_button, &QAbstractButton::clicked,
@@ -382,7 +389,7 @@ void Widget::CreateHexEdit(const QString& label, std::function<void()>& load_fun
             setting.LoadString(hex_to_dec());
         };
     } else {
-        restore_button = CreateRestoreGlobalButton(setting, this);
+        restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
         layout->addWidget(restore_button);
 
         QObject::connect(restore_button, &QAbstractButton::clicked, [=](bool) {
@@ -465,7 +472,7 @@ void Widget::CreateDateTimeEdit(const QString& label, std::function<void()>& loa
         };
     } else {
         if (!has_checkbox) {
-            restore_button = CreateRestoreGlobalButton(setting, this);
+            restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
             layout->addWidget(restore_button);
         }
 
@@ -515,12 +522,12 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
       apply_funcs{apply_funcs_} {}
 
 Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_,
-               QWidget* parent_, bool runtime_lock,
+               QWidget* parent_, bool runtime_lock_,
                std::forward_list<std::function<void(bool)>>& apply_funcs_, RequestType request,
                bool managed, float multiplier, Settings::BasicSetting* other_setting,
-               const std::string& string)
+               const QString& string)
     : QWidget(parent_), parent{parent_}, translations{translations_}, setting{*setting_},
-      apply_funcs{apply_funcs_} {
+      apply_funcs{apply_funcs_}, runtime_lock{runtime_lock_} {
     if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) {
         LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel());
         return;
@@ -547,23 +554,16 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
     std::function<void()> load_func = []() {};
 
     if (type == typeid(bool)) {
-        switch (request) {
-        case RequestType::Default:
-            CreateCheckBox(&setting, label, load_func, managed);
-            break;
-        default:
-            LOG_WARNING(Frontend, "Requested widget is unimplemented.");
-            break;
-        }
+        CreateCheckBox(&setting, label, load_func, managed);
     } else if (setting.IsEnum()) {
         CreateCombobox(label, load_func, managed);
     } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
-               type == typeid(s64)) {
+               type == typeid(s64) || type == typeid(u8)) {
         switch (request) {
         case RequestType::Slider:
         case RequestType::ReverseSlider:
             CreateSlider(label, request == RequestType::ReverseSlider, multiplier, load_func,
-                         managed);
+                         managed, string);
             break;
         case RequestType::LineEdit:
         case RequestType::Default:
@@ -586,7 +586,23 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
             break;
         }
     } else if (type == typeid(std::string)) {
-        CreateLineEdit(label, load_func, managed);
+        switch (request) {
+        case RequestType::Default:
+        case RequestType::LineEdit:
+            CreateLineEdit(label, load_func, managed);
+            break;
+        case RequestType::ComboBox:
+            CreateCombobox(label, load_func, false);
+            break;
+        case RequestType::SpinBox:
+        case RequestType::Slider:
+        case RequestType::ReverseSlider:
+        case RequestType::HexEdit:
+        case RequestType::DateTimeEdit:
+        case RequestType::MaxEnum:
+            LOG_WARNING(Frontend, "Requested widget is unimplemented.");
+            break;
+        }
     }
 
     if (!created) {
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
index c4e6865741..331316040e 100644
--- a/src/yuzu/configuration/shared_widget.h
+++ b/src/yuzu/configuration/shared_widget.h
@@ -38,15 +38,15 @@ public:
     Widget(Settings::BasicSetting* setting, const TranslationMap& translations, QWidget* parent,
            bool runtime_lock, std::forward_list<std::function<void(bool)>>& apply_funcs_,
            RequestType request = RequestType::Default, bool managed = true, float multiplier = 1.0f,
-           Settings::BasicSetting* other_setting = nullptr, const std::string& format = "");
+           Settings::BasicSetting* other_setting = nullptr,
+           const QString& string = QStringLiteral(""));
     Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_, QWidget* parent_,
            std::forward_list<std::function<void(bool)>>& apply_funcs_);
     virtual ~Widget();
 
     bool Valid();
 
-    [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(Settings::BasicSetting& setting,
-                                                                QWidget* parent);
+    [[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent);
 
     QPushButton* restore_button{};
     QLineEdit* line_edit{};
@@ -68,12 +68,12 @@ private:
     void CreateHexEdit(const QString& label, std::function<void()>& load_func, bool managed,
                        Settings::BasicSetting* const other_setting = nullptr);
     void CreateSlider(const QString& label, bool reversed, float multiplier,
-                      std::function<void()>& load_func, bool managed,
+                      std::function<void()>& load_func, bool managed, const QString& format,
                       Settings::BasicSetting* const other_setting = nullptr);
     void CreateDateTimeEdit(const QString& label, std::function<void()>& load_func, bool managed,
                             bool restrict, Settings::BasicSetting* const other_setting = nullptr);
     void CreateSpinBox(const QString& label, std::function<void()>& load_func, bool managed,
-                       const std::string& suffix, Settings::BasicSetting* other_setting = nullptr);
+                       const QString& suffix, Settings::BasicSetting* other_setting = nullptr);
 
     QWidget* parent;
     const TranslationMap& translations;
@@ -81,6 +81,7 @@ private:
     std::forward_list<std::function<void(bool)>>& apply_funcs;
 
     bool created{false};
+    bool runtime_lock{false};
 };
 
 } // namespace ConfigurationShared