dolphin/Source/Core/DolphinQt/Settings/AudioPane.cpp
sowens99 8ea6bef98f Port Main.DSP to MainSettings
While trying to work on adding audiodump support for CLI, I was alerted that it was important to first try moving the DSP configs to the new config before continuing, as that makes it substantially easier to write clean code to add such a feature.

This commit aims to allow for Dolphin to only rely on the new config for DSP-related settings.
2021-10-15 23:24:46 -04:00

451 lines
15 KiB
C++

// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Settings/AudioPane.h"
#include <QCheckBox>
#include <QComboBox>
#include <QFontMetrics>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QRadioButton>
#include <QSlider>
#include <QSpacerItem>
#include <QSpinBox>
#include <QVBoxLayout>
#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/Enums.h"
#include "AudioCommon/WASAPIStream.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "DolphinQt/Config/SettingsWindow.h"
#include "DolphinQt/Settings.h"
AudioPane::AudioPane()
{
CheckNeedForLatencyControl();
CreateWidgets();
LoadSettings();
ConnectWidgets();
connect(&Settings::Instance(), &Settings::VolumeChanged, this, &AudioPane::OnVolumeChanged);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[=](Core::State state) { OnEmulationStateChanged(state != Core::State::Uninitialized); });
OnEmulationStateChanged(Core::GetState() != Core::State::Uninitialized);
}
void AudioPane::CreateWidgets()
{
auto* dsp_box = new QGroupBox(tr("DSP Emulation Engine"));
auto* dsp_layout = new QVBoxLayout;
dsp_box->setLayout(dsp_layout);
m_dsp_hle = new QRadioButton(tr("DSP HLE (recommended)"));
m_dsp_lle = new QRadioButton(tr("DSP LLE Recompiler (slow)"));
m_dsp_interpreter = new QRadioButton(tr("DSP LLE Interpreter (very slow)"));
dsp_layout->addStretch(1);
dsp_layout->addWidget(m_dsp_hle);
dsp_layout->addWidget(m_dsp_lle);
dsp_layout->addWidget(m_dsp_interpreter);
dsp_layout->addStretch(1);
auto* volume_box = new QGroupBox(tr("Volume"));
auto* volume_layout = new QVBoxLayout;
m_volume_slider = new QSlider;
m_volume_indicator = new QLabel();
volume_box->setLayout(volume_layout);
m_volume_slider->setMinimum(0);
m_volume_slider->setMaximum(100);
m_volume_indicator->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
m_volume_indicator->setFixedWidth(QFontMetrics(font()).boundingRect(tr("%1 %").arg(100)).width());
volume_layout->addWidget(m_volume_slider, 0, Qt::AlignHCenter);
volume_layout->addWidget(m_volume_indicator, 0, Qt::AlignHCenter);
auto* backend_box = new QGroupBox(tr("Backend Settings"));
auto* backend_layout = new QFormLayout;
backend_box->setLayout(backend_layout);
m_backend_label = new QLabel(tr("Audio Backend:"));
m_backend_combo = new QComboBox();
m_dolby_pro_logic = new QCheckBox(tr("Dolby Pro Logic II Decoder"));
if (m_latency_control_supported)
{
m_latency_label = new QLabel(tr("Latency:"));
m_latency_spin = new QSpinBox();
m_latency_spin->setMinimum(0);
m_latency_spin->setMaximum(200);
m_latency_spin->setToolTip(
tr("Sets the latency in milliseconds. Higher values may reduce audio "
"crackling. Certain backends only."));
}
m_dolby_pro_logic->setToolTip(
tr("Enables Dolby Pro Logic II emulation using 5.1 surround. Certain backends only."));
auto* dolby_quality_layout = new QHBoxLayout;
m_dolby_quality_label = new QLabel(tr("Decoding Quality:"));
m_dolby_quality_slider = new QSlider(Qt::Horizontal);
m_dolby_quality_slider->setMinimum(0);
m_dolby_quality_slider->setMaximum(3);
m_dolby_quality_slider->setPageStep(1);
m_dolby_quality_slider->setTickPosition(QSlider::TicksBelow);
m_dolby_quality_slider->setToolTip(
tr("Quality of the DPLII decoder. Audio latency increases with quality."));
m_dolby_quality_slider->setTracking(true);
m_dolby_quality_low_label = new QLabel(GetDPL2QualityLabel(AudioCommon::DPL2Quality::Lowest));
m_dolby_quality_highest_label =
new QLabel(GetDPL2QualityLabel(AudioCommon::DPL2Quality::Highest));
m_dolby_quality_latency_label =
new QLabel(GetDPL2ApproximateLatencyLabel(AudioCommon::DPL2Quality::Highest));
dolby_quality_layout->addWidget(m_dolby_quality_low_label);
dolby_quality_layout->addWidget(m_dolby_quality_slider);
dolby_quality_layout->addWidget(m_dolby_quality_highest_label);
backend_layout->setFormAlignment(Qt::AlignLeft | Qt::AlignTop);
backend_layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
backend_layout->addRow(m_backend_label, m_backend_combo);
if (m_latency_control_supported)
backend_layout->addRow(m_latency_label, m_latency_spin);
#ifdef _WIN32
m_wasapi_device_label = new QLabel(tr("Device:"));
m_wasapi_device_combo = new QComboBox;
backend_layout->addRow(m_wasapi_device_label, m_wasapi_device_combo);
#endif
backend_layout->addRow(m_dolby_pro_logic);
backend_layout->addRow(m_dolby_quality_label);
backend_layout->addRow(dolby_quality_layout);
backend_layout->addRow(m_dolby_quality_latency_label);
auto* stretching_box = new QGroupBox(tr("Audio Stretching Settings"));
auto* stretching_layout = new QGridLayout;
m_stretching_enable = new QCheckBox(tr("Enable Audio Stretching"));
m_stretching_buffer_slider = new QSlider(Qt::Horizontal);
m_stretching_buffer_indicator = new QLabel();
m_stretching_buffer_label = new QLabel(tr("Buffer Size:"));
stretching_box->setLayout(stretching_layout);
m_stretching_buffer_slider->setMinimum(5);
m_stretching_buffer_slider->setMaximum(300);
m_stretching_enable->setToolTip(tr("Enables stretching of the audio to match emulation speed."));
m_stretching_buffer_slider->setToolTip(tr("Size of stretch buffer in milliseconds. "
"Values too low may cause audio crackling."));
stretching_layout->addWidget(m_stretching_enable, 0, 0, 1, -1);
stretching_layout->addWidget(m_stretching_buffer_label, 1, 0);
stretching_layout->addWidget(m_stretching_buffer_slider, 1, 1);
stretching_layout->addWidget(m_stretching_buffer_indicator, 1, 2);
dsp_box->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
auto* const main_vbox_layout = new QVBoxLayout;
main_vbox_layout->addWidget(dsp_box);
main_vbox_layout->addWidget(backend_box);
main_vbox_layout->addWidget(stretching_box);
m_main_layout = new QHBoxLayout;
m_main_layout->addLayout(main_vbox_layout);
m_main_layout->addWidget(volume_box);
setLayout(m_main_layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
void AudioPane::ConnectWidgets()
{
connect(m_backend_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&AudioPane::SaveSettings);
connect(m_volume_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings);
if (m_latency_control_supported)
{
connect(m_latency_spin, qOverload<int>(&QSpinBox::valueChanged), this,
&AudioPane::SaveSettings);
}
connect(m_stretching_buffer_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings);
connect(m_dolby_pro_logic, &QCheckBox::toggled, this, &AudioPane::SaveSettings);
connect(m_dolby_quality_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings);
connect(m_stretching_enable, &QCheckBox::toggled, this, &AudioPane::SaveSettings);
connect(m_dsp_hle, &QRadioButton::toggled, this, &AudioPane::SaveSettings);
connect(m_dsp_lle, &QRadioButton::toggled, this, &AudioPane::SaveSettings);
connect(m_dsp_interpreter, &QRadioButton::toggled, this, &AudioPane::SaveSettings);
#ifdef _WIN32
connect(m_wasapi_device_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&AudioPane::SaveSettings);
#endif
}
void AudioPane::LoadSettings()
{
auto& settings = Settings::Instance();
// DSP
if (Config::Get(Config::MAIN_DSP_HLE))
{
m_dsp_hle->setChecked(true);
}
else
{
m_dsp_lle->setChecked(Config::Get(Config::MAIN_DSP_JIT));
m_dsp_interpreter->setChecked(!Config::Get(Config::MAIN_DSP_JIT));
}
// Backend
const auto current = Config::Get(Config::MAIN_AUDIO_BACKEND);
bool selection_set = false;
for (const auto& backend : AudioCommon::GetSoundBackends())
{
m_backend_combo->addItem(tr(backend.c_str()), QVariant(QString::fromStdString(backend)));
if (backend == current)
{
m_backend_combo->setCurrentIndex(m_backend_combo->count() - 1);
selection_set = true;
}
}
if (!selection_set)
m_backend_combo->setCurrentIndex(-1);
OnBackendChanged();
// Volume
OnVolumeChanged(settings.GetVolume());
// DPL2
m_dolby_pro_logic->setChecked(Config::Get(Config::MAIN_DPL2_DECODER));
m_dolby_quality_slider->setValue(int(Config::Get(Config::MAIN_DPL2_QUALITY)));
m_dolby_quality_latency_label->setText(
GetDPL2ApproximateLatencyLabel(Config::Get(Config::MAIN_DPL2_QUALITY)));
if (AudioCommon::SupportsDPL2Decoder(current) && !m_dsp_hle->isChecked())
{
EnableDolbyQualityWidgets(m_dolby_pro_logic->isChecked());
}
// Latency
if (m_latency_control_supported)
m_latency_spin->setValue(Config::Get(Config::MAIN_AUDIO_LATENCY));
// Stretch
m_stretching_enable->setChecked(Config::Get(Config::MAIN_AUDIO_STRETCH));
m_stretching_buffer_slider->setValue(Config::Get(Config::MAIN_AUDIO_STRETCH_LATENCY));
m_stretching_buffer_slider->setEnabled(m_stretching_enable->isChecked());
m_stretching_buffer_indicator->setText(tr("%1 ms").arg(m_stretching_buffer_slider->value()));
#ifdef _WIN32
if (Config::Get(Config::MAIN_WASAPI_DEVICE) == "default")
{
m_wasapi_device_combo->setCurrentIndex(0);
}
else
{
m_wasapi_device_combo->setCurrentText(
QString::fromStdString(Config::Get(Config::MAIN_WASAPI_DEVICE)));
}
#endif
}
void AudioPane::SaveSettings()
{
auto& settings = Settings::Instance();
// DSP
if (Config::Get(Config::MAIN_DSP_HLE) != m_dsp_hle->isChecked() ||
Config::Get(Config::MAIN_DSP_JIT) != m_dsp_lle->isChecked())
{
OnDspChanged();
}
Config::SetBaseOrCurrent(Config::MAIN_DSP_HLE, m_dsp_hle->isChecked());
Config::SetBaseOrCurrent(Config::MAIN_DSP_JIT, m_dsp_lle->isChecked());
// Backend
const auto selection =
m_backend_combo->itemData(m_backend_combo->currentIndex()).toString().toStdString();
std::string backend = Config::Get(Config::MAIN_AUDIO_BACKEND);
if (selection != backend)
{
backend = selection;
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_BACKEND, selection);
OnBackendChanged();
}
// Volume
if (m_volume_slider->value() != settings.GetVolume())
{
settings.SetVolume(m_volume_slider->value());
OnVolumeChanged(settings.GetVolume());
}
// DPL2
Config::SetBaseOrCurrent(Config::MAIN_DPL2_DECODER, m_dolby_pro_logic->isChecked());
Config::SetBase(Config::MAIN_DPL2_QUALITY,
static_cast<AudioCommon::DPL2Quality>(m_dolby_quality_slider->value()));
m_dolby_quality_latency_label->setText(
GetDPL2ApproximateLatencyLabel(Config::Get(Config::MAIN_DPL2_QUALITY)));
if (AudioCommon::SupportsDPL2Decoder(backend) && !m_dsp_hle->isChecked())
{
EnableDolbyQualityWidgets(m_dolby_pro_logic->isChecked());
}
// Latency
if (m_latency_control_supported)
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_LATENCY, m_latency_spin->value());
// Stretch
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_STRETCH, m_stretching_enable->isChecked());
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_STRETCH_LATENCY, m_stretching_buffer_slider->value());
m_stretching_buffer_label->setEnabled(m_stretching_enable->isChecked());
m_stretching_buffer_slider->setEnabled(m_stretching_enable->isChecked());
m_stretching_buffer_indicator->setEnabled(m_stretching_enable->isChecked());
m_stretching_buffer_indicator->setText(
tr("%1 ms").arg(Config::Get(Config::MAIN_AUDIO_STRETCH_LATENCY)));
#ifdef _WIN32
std::string device = "default";
if (m_wasapi_device_combo->currentIndex() != 0)
device = m_wasapi_device_combo->currentText().toStdString();
Config::SetBaseOrCurrent(Config::MAIN_WASAPI_DEVICE, device);
#endif
AudioCommon::UpdateSoundStream();
}
void AudioPane::OnDspChanged()
{
const auto backend = Config::Get(Config::MAIN_AUDIO_BACKEND);
m_dolby_pro_logic->setEnabled(AudioCommon::SupportsDPL2Decoder(backend) &&
!m_dsp_hle->isChecked());
EnableDolbyQualityWidgets(AudioCommon::SupportsDPL2Decoder(backend) && !m_dsp_hle->isChecked() &&
m_dolby_pro_logic->isChecked());
}
void AudioPane::OnBackendChanged()
{
const auto backend = Config::Get(Config::MAIN_AUDIO_BACKEND);
m_dolby_pro_logic->setEnabled(AudioCommon::SupportsDPL2Decoder(backend) &&
!m_dsp_hle->isChecked());
EnableDolbyQualityWidgets(AudioCommon::SupportsDPL2Decoder(backend) && !m_dsp_hle->isChecked() &&
m_dolby_pro_logic->isChecked());
if (m_latency_control_supported)
{
m_latency_label->setEnabled(AudioCommon::SupportsLatencyControl(backend));
m_latency_spin->setEnabled(AudioCommon::SupportsLatencyControl(backend));
}
#ifdef _WIN32
bool is_wasapi = backend == BACKEND_WASAPI;
m_wasapi_device_label->setHidden(!is_wasapi);
m_wasapi_device_combo->setHidden(!is_wasapi);
if (is_wasapi)
{
m_wasapi_device_combo->clear();
m_wasapi_device_combo->addItem(tr("Default Device"));
for (const auto device : WASAPIStream::GetAvailableDevices())
m_wasapi_device_combo->addItem(QString::fromStdString(device));
}
#endif
m_volume_slider->setEnabled(AudioCommon::SupportsVolumeChanges(backend));
m_volume_indicator->setEnabled(AudioCommon::SupportsVolumeChanges(backend));
}
void AudioPane::OnEmulationStateChanged(bool running)
{
m_dsp_hle->setEnabled(!running);
m_dsp_lle->setEnabled(!running);
m_dsp_interpreter->setEnabled(!running);
m_backend_label->setEnabled(!running);
m_backend_combo->setEnabled(!running);
if (AudioCommon::SupportsDPL2Decoder(Config::Get(Config::MAIN_AUDIO_BACKEND)) &&
!m_dsp_hle->isChecked())
{
m_dolby_pro_logic->setEnabled(!running);
EnableDolbyQualityWidgets(!running && m_dolby_pro_logic->isChecked());
}
if (m_latency_control_supported &&
AudioCommon::SupportsLatencyControl(Config::Get(Config::MAIN_AUDIO_BACKEND)))
{
m_latency_label->setEnabled(!running);
m_latency_spin->setEnabled(!running);
}
#ifdef _WIN32
m_wasapi_device_combo->setEnabled(!running);
#endif
}
void AudioPane::OnVolumeChanged(int volume)
{
m_volume_slider->setValue(volume);
m_volume_indicator->setText(tr("%1%").arg(volume));
}
void AudioPane::CheckNeedForLatencyControl()
{
std::vector<std::string> backends = AudioCommon::GetSoundBackends();
m_latency_control_supported =
std::any_of(backends.cbegin(), backends.cend(), AudioCommon::SupportsLatencyControl);
}
QString AudioPane::GetDPL2QualityLabel(AudioCommon::DPL2Quality value) const
{
switch (value)
{
case AudioCommon::DPL2Quality::Lowest:
return tr("Lowest");
case AudioCommon::DPL2Quality::Low:
return tr("Low");
case AudioCommon::DPL2Quality::Highest:
return tr("Highest");
default:
return tr("High");
}
}
QString AudioPane::GetDPL2ApproximateLatencyLabel(AudioCommon::DPL2Quality value) const
{
switch (value)
{
case AudioCommon::DPL2Quality::Lowest:
return tr("Latency: ~10 ms");
case AudioCommon::DPL2Quality::Low:
return tr("Latency: ~20 ms");
case AudioCommon::DPL2Quality::Highest:
return tr("Latency: ~80 ms");
default:
return tr("Latency: ~40 ms");
}
}
void AudioPane::EnableDolbyQualityWidgets(bool enabled) const
{
m_dolby_quality_label->setEnabled(enabled);
m_dolby_quality_slider->setEnabled(enabled);
m_dolby_quality_low_label->setEnabled(enabled);
m_dolby_quality_highest_label->setEnabled(enabled);
m_dolby_quality_latency_label->setEnabled(enabled);
}