mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-05 20:28:40 +01:00
![JosJuice](/assets/img/avatar_default.png)
Some pieces of code are calling IsRunning because there's some particular action that only makes sense when emulation is running, for instance showing the state of the emulated CPU. IsRunning is appropriate to use for this. Then there are pieces of code that are calling IsRunning because there's some particular thing they must avoid doing e.g. when the CPU thread is running or IOS is running. IsRunning isn't quite appropriate for this. Such code should also be checking for the states Starting and Stopping. Keep in mind that: * When the state is Starting, the state can asynchronously change to Running at any time. * When we try to stop the core, the state gets set to Stopping before we take any action to actually stop things. This commit adds a new method Core::IsUninitialized, and changes all callers of IsRunning and GetState that look to me like they should be changed.
453 lines
15 KiB
C++
453 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 "Core/System.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, [this](Core::State state) {
|
|
OnEmulationStateChanged(state != Core::State::Uninitialized);
|
|
});
|
|
|
|
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
|
|
}
|
|
|
|
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, &QComboBox::currentIndexChanged, this, &AudioPane::SaveSettings);
|
|
connect(m_volume_slider, &QSlider::valueChanged, this, &AudioPane::SaveSettings);
|
|
if (m_latency_control_supported)
|
|
{
|
|
connect(m_latency_spin, &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, &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_label->setEnabled(m_stretching_enable->isChecked());
|
|
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->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(Core::System::GetInstance());
|
|
}
|
|
|
|
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);
|
|
}
|