AudioStream: Add surround expansion via FreeSurround

This commit is contained in:
Stenzek
2024-04-16 16:45:15 +10:00
parent 7932d284f1
commit 0fbc1a3a8a
31 changed files with 1977 additions and 554 deletions

View File

@ -20,9 +20,11 @@ set(SRCS
advancedsettingswidget.cpp
advancedsettingswidget.h
advancedsettingswidget.ui
audioexpansionsettingsdialog.ui
audiosettingswidget.cpp
audiosettingswidget.h
audiosettingswidget.ui
audiostretchsettingsdialog.ui
autoupdaterdialog.cpp
autoupdaterdialog.h
autoupdaterdialog.ui

View File

@ -0,0 +1,476 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioExpansionSettingsDialog</class>
<widget class="QDialog" name="AudioExpansionSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>614</width>
<height>371</height>
</rect>
</property>
<property name="windowTitle">
<string>Audio Expansion Settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Circular Wrap:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSlider" name="circularWrap">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>360</number>
</property>
<property name="value">
<number>90</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="circularWrapLabel">
<property name="text">
<string>30</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Shift:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="shift">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="shiftLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Depth:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSlider" name="depth">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="depthLabel">
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Focus:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QSlider" name="focus">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="focusLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Center Image:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSlider" name="centerImage">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="centerImageLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Front Separation:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QSlider" name="frontSeparation">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="frontSeparationLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Rear Separation:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QSlider" name="rearSeparation">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rearSeparationLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Low Cutoff:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QSlider" name="lowCutoff">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lowCutoffLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>High Cutoff:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QSlider" name="highCutoff">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="highCutoffLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="11" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Close|QDialogButtonBox::StandardButton::RestoreDefaults</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="icon">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Audio Expansion Settings&lt;/span&gt;&lt;br/&gt;These settings fine-tune the behavior of the FreeSurround-based channel expander.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Block Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QSlider" name="blockSize">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>8192</number>
</property>
<property name="singleStep">
<number>16</number>
</property>
<property name="pageStep">
<number>128</number>
</property>
<property name="value">
<number>1024</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>128</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="blockSizeLabel">
<property name="text">
<string>30</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,9 +1,12 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "audiosettingswidget.h"
#include "qtutils.h"
#include "settingswindow.h"
#include "settingwidgetbinder.h"
#include "ui_audioexpansionsettingsdialog.h"
#include "ui_audiostretchsettingsdialog.h"
#include "core/spu.h"
@ -18,19 +21,41 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* dialog, QWidget* parent
m_ui.setupUi(this);
for (u32 i = 0; i < static_cast<u32>(AudioBackend::Count); i++)
m_ui.audioBackend->addItem(QString::fromUtf8(Settings::GetAudioBackendDisplayName(static_cast<AudioBackend>(i))));
m_ui.audioBackend->addItem(QString::fromUtf8(AudioStream::GetBackendDisplayName(static_cast<AudioBackend>(i))));
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.audioBackend, "Audio", "Backend", &Settings::ParseAudioBackend,
&Settings::GetAudioBackendName, Settings::DEFAULT_AUDIO_BACKEND);
for (u32 i = 0; i < static_cast<u32>(AudioExpansionMode::Count); i++)
{
m_ui.expansionMode->addItem(
QString::fromUtf8(AudioStream::GetExpansionModeDisplayName(static_cast<AudioExpansionMode>(i))));
}
for (u32 i = 0; i < static_cast<u32>(AudioStretchMode::Count); i++)
{
m_ui.stretchMode->addItem(
QString::fromUtf8(AudioStream::GetStretchModeDisplayName(static_cast<AudioStretchMode>(i))));
}
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.audioBackend, "Audio", "Backend",
&AudioStream::ParseBackendName, &AudioStream::GetBackendName,
AudioStream::DEFAULT_BACKEND);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.expansionMode, "Audio", "ExpansionMode",
&AudioStream::ParseExpansionMode, &AudioStream::GetExpansionModeName,
AudioStreamParameters::DEFAULT_EXPANSION_MODE);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.stretchMode, "Audio", "StretchMode",
&AudioStream::ParseStretchMode, &AudioStream::GetStretchModeName,
Settings::DEFAULT_AUDIO_STRETCH_MODE);
AudioStreamParameters::DEFAULT_STRETCH_MODE);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.bufferMS, "Audio", "BufferMS",
Settings::DEFAULT_AUDIO_BUFFER_MS);
AudioStreamParameters::DEFAULT_BUFFER_MS);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.outputLatencyMS, "Audio", "OutputLatencyMS",
Settings::DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muteCDAudio, "CDROM", "MuteCDAudio", false);
connect(m_ui.audioBackend, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::updateDriverNames);
connect(m_ui.expansionMode, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::onExpansionModeChanged);
connect(m_ui.expansionSettings, &QToolButton::clicked, this, &AudioSettingsWidget::onExpansionSettingsClicked);
connect(m_ui.stretchMode, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::onStretchModeChanged);
connect(m_ui.stretchSettings, &QToolButton::clicked, this, &AudioSettingsWidget::onStretchSettingsClicked);
onExpansionModeChanged();
onStretchModeChanged();
updateDriverNames();
m_ui.outputLatencyMinimal->setChecked(m_ui.outputLatencyMS->value() == 0);
@ -79,6 +104,9 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* dialog, QWidget* parent
dialog->registerWidgetHelp(m_ui.muteCDAudio, tr("Mute CD Audio"), tr("Unchecked"),
tr("Forcibly mutes both CD-DA and XA audio from the CD-ROM. Can be used to disable "
"background music in some games."));
dialog->registerWidgetHelp(m_ui.expansionMode, tr("Expansion Mode"), tr("Disabled (Stereo)"),
tr("Determines how audio is expanded from stereo to surround for supported games. This "
"includes games that support Dolby Pro Logic/Pro Logic II."));
dialog->registerWidgetHelp(
m_ui.stretchMode, tr("Stretch Mode"), tr("Time Stretching"),
tr("When running outside of 100% speed, adjusts the tempo on audio instead of dropping frames. Produces "
@ -87,25 +115,46 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* dialog, QWidget* parent
AudioSettingsWidget::~AudioSettingsWidget() = default;
void AudioSettingsWidget::onExpansionModeChanged()
{
const AudioExpansionMode expansion_mode =
AudioStream::ParseExpansionMode(
m_dialog
->getEffectiveStringValue("Audio", "ExpansionMode",
AudioStream::GetExpansionModeName(AudioStreamParameters::DEFAULT_EXPANSION_MODE))
.c_str())
.value_or(AudioStreamParameters::DEFAULT_EXPANSION_MODE);
m_ui.expansionSettings->setEnabled(expansion_mode != AudioExpansionMode::Disabled);
}
void AudioSettingsWidget::onStretchModeChanged()
{
const AudioStretchMode stretch_mode =
AudioStream::ParseStretchMode(
m_dialog
->getEffectiveStringValue("Audio", "StretchMode",
AudioStream::GetStretchModeName(AudioStreamParameters::DEFAULT_STRETCH_MODE))
.c_str())
.value_or(AudioStreamParameters::DEFAULT_STRETCH_MODE);
m_ui.stretchSettings->setEnabled(stretch_mode != AudioStretchMode::Off);
}
void AudioSettingsWidget::updateDriverNames()
{
const AudioBackend backend =
Settings::ParseAudioBackend(
m_dialog
->getEffectiveStringValue("Audio", "Backend", Settings::GetAudioBackendName(Settings::DEFAULT_AUDIO_BACKEND))
AudioStream::ParseBackendName(
m_dialog->getEffectiveStringValue("Audio", "Backend", AudioStream::GetBackendName(AudioStream::DEFAULT_BACKEND))
.c_str())
.value_or(Settings::DEFAULT_AUDIO_BACKEND);
.value_or(AudioStream::DEFAULT_BACKEND);
std::vector<std::string> names;
std::vector<std::pair<std::string, std::string>> devices;
#ifdef ENABLE_CUBEB
if (backend == AudioBackend::Cubeb)
{
names = AudioStream::GetCubebDriverNames();
devices = AudioStream::GetCubebOutputDevices(m_dialog->getEffectiveStringValue("Audio", "Driver", "").c_str());
}
#endif
m_ui.driver->disconnect();
m_ui.driver->clear();
@ -171,7 +220,7 @@ void AudioSettingsWidget::updateVolumeLabel()
void AudioSettingsWidget::onMinimalOutputLatencyChecked(bool new_value)
{
const u32 value = new_value ? 0u : Settings::DEFAULT_AUDIO_OUTPUT_LATENCY_MS;
const u32 value = new_value ? 0u : AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS;
m_dialog->setIntSettingValue("Audio", "OutputLatencyMS", value);
QSignalBlocker sb(m_ui.outputLatencyMS);
m_ui.outputLatencyMS->setValue(value);
@ -211,3 +260,144 @@ void AudioSettingsWidget::onOutputMutedChanged(int new_state)
Host::CommitBaseSettingChanges();
g_emu_thread->setAudioOutputMuted(muted);
}
void AudioSettingsWidget::onExpansionSettingsClicked()
{
QDialog dlg(QtUtils::GetRootWidget(this));
Ui::AudioExpansionSettingsDialog dlgui;
dlgui.setupUi(&dlg);
dlgui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("volume-up-line")).pixmap(32, 32));
SettingsInterface* sif = m_dialog->getSettingsInterface();
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.blockSize, "Audio", "ExpandBlockSize",
AudioStreamParameters::DEFAULT_EXPAND_BLOCK_SIZE, 0);
QtUtils::BindLabelToSlider(dlgui.blockSize, dlgui.blockSizeLabel);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, dlgui.circularWrap, "Audio", "ExpandCircularWrap",
AudioStreamParameters::DEFAULT_EXPAND_CIRCULAR_WRAP);
QtUtils::BindLabelToSlider(dlgui.circularWrap, dlgui.circularWrapLabel);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.shift, "Audio", "ExpandShift", 100.0f,
AudioStreamParameters::DEFAULT_EXPAND_SHIFT);
QtUtils::BindLabelToSlider(dlgui.shift, dlgui.shiftLabel, 100.0f);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.depth, "Audio", "ExpandDepth", 10.0f,
AudioStreamParameters::DEFAULT_EXPAND_DEPTH);
QtUtils::BindLabelToSlider(dlgui.depth, dlgui.depthLabel, 10.0f);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.focus, "Audio", "ExpandFocus", 100.0f,
AudioStreamParameters::DEFAULT_EXPAND_FOCUS);
QtUtils::BindLabelToSlider(dlgui.focus, dlgui.focusLabel, 100.0f);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.centerImage, "Audio", "ExpandCenterImage", 100.0f,
AudioStreamParameters::DEFAULT_EXPAND_CENTER_IMAGE);
QtUtils::BindLabelToSlider(dlgui.centerImage, dlgui.centerImageLabel, 100.0f);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.frontSeparation, "Audio", "ExpandFrontSeparation",
10.0f, AudioStreamParameters::DEFAULT_EXPAND_FRONT_SEPARATION);
QtUtils::BindLabelToSlider(dlgui.frontSeparation, dlgui.frontSeparationLabel, 10.0f);
SettingWidgetBinder::BindWidgetToNormalizedSetting(sif, dlgui.rearSeparation, "Audio", "ExpandRearSeparation", 10.0f,
AudioStreamParameters::DEFAULT_EXPAND_REAR_SEPARATION);
QtUtils::BindLabelToSlider(dlgui.rearSeparation, dlgui.rearSeparationLabel, 10.0f);
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.lowCutoff, "Audio", "ExpandLowCutoff",
AudioStreamParameters::DEFAULT_EXPAND_LOW_CUTOFF);
QtUtils::BindLabelToSlider(dlgui.lowCutoff, dlgui.lowCutoffLabel);
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.highCutoff, "Audio", "ExpandHighCutoff",
AudioStreamParameters::DEFAULT_EXPAND_HIGH_CUTOFF);
QtUtils::BindLabelToSlider(dlgui.highCutoff, dlgui.highCutoffLabel);
connect(dlgui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, &dlg, &QDialog::accept);
connect(dlgui.buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, [this, &dlg]() {
m_dialog->setIntSettingValue("Audio", "ExpandBlockSize",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_EXPAND_BLOCK_SIZE));
m_dialog->setFloatSettingValue("Audio", "ExpandCircularWrap",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_CIRCULAR_WRAP));
m_dialog->setFloatSettingValue(
"Audio", "ExpandShift",
m_dialog->isPerGameSettings() ? std::nullopt : std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_SHIFT));
m_dialog->setFloatSettingValue(
"Audio", "ExpandDepth",
m_dialog->isPerGameSettings() ? std::nullopt : std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_DEPTH));
m_dialog->setFloatSettingValue(
"Audio", "ExpandFocus",
m_dialog->isPerGameSettings() ? std::nullopt : std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_FOCUS));
m_dialog->setFloatSettingValue("Audio", "ExpandCenterImage",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_CENTER_IMAGE));
m_dialog->setFloatSettingValue("Audio", "ExpandFrontSeparation",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_FRONT_SEPARATION));
m_dialog->setFloatSettingValue("Audio", "ExpandRearSeparation",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<float>(AudioStreamParameters::DEFAULT_EXPAND_REAR_SEPARATION));
m_dialog->setIntSettingValue("Audio", "ExpandLowCutoff",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_EXPAND_LOW_CUTOFF));
m_dialog->setIntSettingValue("Audio", "ExpandHighCutoff",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_EXPAND_HIGH_CUTOFF));
dlg.done(0);
QMetaObject::invokeMethod(this, &AudioSettingsWidget::onExpansionSettingsClicked, Qt::QueuedConnection);
});
dlg.exec();
}
void AudioSettingsWidget::onStretchSettingsClicked()
{
QDialog dlg(QtUtils::GetRootWidget(this));
Ui::AudioStretchSettingsDialog dlgui;
dlgui.setupUi(&dlg);
dlgui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("volume-up-line")).pixmap(32, 32));
SettingsInterface* sif = m_dialog->getSettingsInterface();
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.sequenceLength, "Audio", "StretchSequenceLengthMS",
AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH, 0);
QtUtils::BindLabelToSlider(dlgui.sequenceLength, dlgui.sequenceLengthLabel);
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.seekWindowSize, "Audio", "StretchSeekWindowMS",
AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW, 0);
QtUtils::BindLabelToSlider(dlgui.seekWindowSize, dlgui.seekWindowSizeLabel);
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.overlap, "Audio", "StretchOverlapMS",
AudioStreamParameters::DEFAULT_STRETCH_OVERLAP, 0);
QtUtils::BindLabelToSlider(dlgui.overlap, dlgui.overlapLabel);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, dlgui.useQuickSeek, "Audio", "StretchUseQuickSeek",
AudioStreamParameters::DEFAULT_STRETCH_USE_QUICKSEEK);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, dlgui.useAAFilter, "Audio", "StretchUseAAFilter",
AudioStreamParameters::DEFAULT_STRETCH_USE_AA_FILTER);
connect(dlgui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, &dlg, &QDialog::accept);
connect(dlgui.buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, [this, &dlg]() {
m_dialog->setIntSettingValue("Audio", "StretchSequenceLengthMS",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH));
m_dialog->setIntSettingValue("Audio", "StretchSeekWindowMS",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW));
m_dialog->setIntSettingValue("Audio", "StretchOverlapMS",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_OVERLAP));
m_dialog->setBoolSettingValue("Audio", "StretchUseQuickSeek",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<bool>(AudioStreamParameters::DEFAULT_STRETCH_USE_QUICKSEEK));
m_dialog->setBoolSettingValue("Audio", "StretchUseAAFilter",
m_dialog->isPerGameSettings() ?
std::nullopt :
std::optional<bool>(AudioStreamParameters::DEFAULT_STRETCH_USE_AA_FILTER));
dlg.done(0);
QMetaObject::invokeMethod(this, &AudioSettingsWidget::onStretchSettingsClicked, Qt::QueuedConnection);
});
dlg.exec();
}

View File

@ -18,6 +18,9 @@ public:
~AudioSettingsWidget();
private Q_SLOTS:
void onExpansionModeChanged();
void onStretchModeChanged();
void updateDriverNames();
void updateLatencyLabel();
void updateVolumeLabel();
@ -26,6 +29,9 @@ private Q_SLOTS:
void onFastForwardVolumeChanged(int new_value);
void onOutputMutedChanged(int new_state);
void onExpansionSettingsClicked();
void onStretchSettingsClicked();
private:
Ui::AudioSettingsWidget m_ui;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>516</width>
<height>435</height>
<width>523</width>
<height>478</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -29,13 +29,40 @@
<string>Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1">
<widget class="QComboBox" name="outputDevice"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="audioBackend"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Output Latency:</string>
<string>Driver:</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QLabel" name="bufferingLabel">
<property name="text">
<string>Maximum latency: 0 frames (0.00ms)</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Backend:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Stretch Mode:</string>
</property>
</widget>
</item>
@ -46,7 +73,51 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="outputLatencyMS">
<property name="maximum">
<number>500</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBothSides</enum>
</property>
<property name="tickInterval">
<number>20</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="outputLatencyMinimal">
<property name="text">
<string>Minimal</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="driver"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Buffer Size:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Expansion Mode:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSlider" name="bufferMS">
<property name="minimum">
<number>15</number>
@ -74,95 +145,40 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<item row="6" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Backend:</string>
<string>Output Latency:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Driver:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="driver"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Stretch Mode:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="1,0">
<item>
<widget class="QSlider" name="outputLatencyMS">
<property name="maximum">
<number>500</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBothSides</enum>
</property>
<property name="tickInterval">
<number>20</number>
</property>
</widget>
<widget class="QComboBox" name="expansionMode"/>
</item>
<item>
<widget class="QCheckBox" name="outputLatencyMinimal">
<property name="text">
<string>Minimal</string>
<widget class="QToolButton" name="expansionSettings">
<property name="icon">
<iconset theme="settings-3-line"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="outputDevice"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Buffer Size:</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="bufferingLabel">
<property name="text">
<string>Maximum latency: 0 frames (0.00ms)</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="stretchMode">
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="1,0">
<item>
<property name="text">
<string>Off (Noisy)</string>
</property>
<widget class="QComboBox" name="stretchMode"/>
</item>
<item>
<property name="text">
<string>Resampling (Pitch Shift)</string>
</property>
<widget class="QToolButton" name="stretchSettings">
<property name="icon">
<iconset theme="settings-3-line"/>
</property>
</widget>
</item>
<item>
<property name="text">
<string>Time Stretch (Tempo Change, Best Sound)</string>
</property>
</item>
</widget>
</layout>
</item>
</layout>
</widget>

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioStretchSettingsDialog</class>
<widget class="QDialog" name="AudioStretchSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>501</width>
<height>248</height>
</rect>
</property>
<property name="windowTitle">
<string>Audio Stretch Settings</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Sequence Length:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSlider" name="sequenceLength">
<property name="minimum">
<number>20</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sequenceLengthLabel">
<property name="text">
<string>30</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Seekwindow Size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSlider" name="seekWindowSize">
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="seekWindowSizeLabel">
<property name="text">
<string>20</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Overlap:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSlider" name="overlap">
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>15</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="overlapLabel">
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Close|QDialogButtonBox::StandardButton::RestoreDefaults</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="icon">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Audio Stretch Settings&lt;/span&gt;&lt;br/&gt;These settings fine-tune the behavior of the SoundTouch audio time stretcher when running outside of 100% speed.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="useQuickSeek">
<property name="text">
<string>Use Quickseek</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="useAAFilter">
<property name="text">
<string>Use Anti-Aliasing Filter</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -338,6 +338,12 @@
<QtUi Include="controllerbindingwidget_negconrumble.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="audioexpansionsettingsdialog.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="audiostretchsettingsdialog.ui">
<FileType>Document</FileType>
</QtUi>
<None Include="translations\duckstation-qt_es-es.ts" />
<None Include="translations\duckstation-qt_tr.ts" />
</ItemGroup>

View File

@ -294,6 +294,8 @@
<QtUi Include="graphicssettingswidget.ui" />
<QtUi Include="memoryscannerwindow.ui" />
<QtUi Include="controllerbindingwidget_negconrumble.ui" />
<QtUi Include="audioexpansionsettingsdialog.ui" />
<QtUi Include="audiostretchsettingsdialog.ui" />
</ItemGroup>
<ItemGroup>
<Natvis Include="qt5.natvis" />

View File

@ -166,6 +166,11 @@ QString QtHost::GetResourcesBasePath()
return QString::fromStdString(EmuFolders::Resources);
}
INISettingsInterface* QtHost::GetBaseSettingsInterface()
{
return s_base_settings_interface.get();
}
QIcon QtHost::GetAppIcon()
{
return QIcon(QStringLiteral(":/icons/duck.png"));

View File

@ -275,6 +275,9 @@ QIcon GetAppIcon();
/// Returns the base path for resources. This may be : prefixed, if we're using embedded resources.
QString GetResourcesBasePath();
/// Returns the base settings interface. Should lock before manipulating.
INISettingsInterface* GetBaseSettingsInterface();
/// Downloads the specified URL to the provided path.
bool DownloadFile(QWidget* parent, const QString& title, std::string url, const char* path);

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "qtutils.h"
@ -17,9 +17,11 @@
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QSlider>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QStyle>
#include <QtWidgets/QTableView>
@ -218,6 +220,15 @@ void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited)
}
}
void BindLabelToSlider(QSlider* slider, QLabel* label, float range /*= 1.0f*/)
{
auto update_label = [label, range](int new_value) {
label->setText(QString::number(static_cast<int>(new_value) / range));
};
update_label(slider->value());
QObject::connect(slider, &QSlider::valueChanged, label, std::move(update_label));
}
void SetWindowResizeable(QWidget* widget, bool resizeable)
{
if (QMainWindow* window = qobject_cast<QMainWindow*>(widget); window)

View File

@ -21,6 +21,8 @@ class ByteStream;
class QComboBox;
class QFrame;
class QKeyEvent;
class QLabel;
class QSlider;
class QTableView;
class QTreeView;
class QVariant;
@ -96,6 +98,9 @@ QString StringViewToQString(const std::string_view& str);
/// Sets a widget to italics if the setting value is inherited.
void SetWidgetFontForInheritedSetting(QWidget* widget, bool inherited);
/// Binds a label to a slider's value.
void BindLabelToSlider(QSlider* slider, QLabel* label, float range = 1.0f);
/// Changes whether a window is resizable.
void SetWindowResizeable(QWidget* widget, bool resizeable);