Path: blob/master/src/duckstation-qt/audiosettingswidget.cpp
7447 views
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#include "audiosettingswidget.h"4#include "qthost.h"5#include "qtutils.h"6#include "settingswindow.h"7#include "settingwidgetbinder.h"89#include "core/core.h"10#include "core/spu.h"1112#include "util/audio_stream.h"1314#include <cmath>1516#include "moc_audiosettingswidget.cpp"1718AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog)19{20SettingsInterface* sif = dialog->getSettingsInterface();2122m_ui.setupUi(this);2324SettingWidgetBinder::BindWidgetToEnumSetting(25sif, m_ui.audioBackend, "Audio", "Backend", &AudioStream::ParseBackendName, &AudioStream::GetBackendName,26&AudioStream::GetBackendDisplayName, AudioStream::DEFAULT_BACKEND, AudioBackend::Count);27SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.stretchMode, "Audio", "StretchMode",28&CoreAudioStream::ParseStretchMode, &CoreAudioStream::GetStretchModeName,29&CoreAudioStream::GetStretchModeDisplayName,30AudioStreamParameters::DEFAULT_STRETCH_MODE, AudioStretchMode::Count);31SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.bufferMS, "Audio", "BufferMS",32AudioStreamParameters::DEFAULT_BUFFER_MS);33QtUtils::BindLabelToSlider(m_ui.bufferMS, m_ui.bufferMSLabel, 1.0f, tr("%1 ms"));34SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.outputLatencyMS, "Audio", "OutputLatencyMS",35AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);36SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.outputLatencyMinimal, "Audio", "OutputLatencyMinimal",37AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MINIMAL);38SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.sequenceLength, "Audio", "StretchSequenceLengthMS",39AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH, 0);40QtUtils::BindLabelToSlider(m_ui.sequenceLength, m_ui.sequenceLengthLabel, 1.0f, tr("%1 ms"));41SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.seekWindowSize, "Audio", "StretchSeekWindowMS",42AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW, 0);43QtUtils::BindLabelToSlider(m_ui.seekWindowSize, m_ui.seekWindowSizeLabel, 1.0f, tr("%1 ms"));44SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overlap, "Audio", "StretchOverlapMS",45AudioStreamParameters::DEFAULT_STRETCH_OVERLAP, 0);46QtUtils::BindLabelToSlider(m_ui.overlap, m_ui.overlapLabel, 1.0f, tr("%1 ms"));47SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useQuickSeek, "Audio", "StretchUseQuickSeek",48AudioStreamParameters::DEFAULT_STRETCH_USE_QUICKSEEK);49SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useAAFilter, "Audio", "StretchUseAAFilter",50AudioStreamParameters::DEFAULT_STRETCH_USE_AA_FILTER);51SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muteCDAudio, "CDROM", "MuteCDAudio", false);5253connect(m_ui.audioBackend, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::updateDriverNames);54connect(m_ui.stretchMode, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::onStretchModeChanged);55connect(m_ui.outputLatencyMS, &QSlider::valueChanged, this, &AudioSettingsWidget::updateLatencyLabel);56connect(m_ui.outputLatencyMinimal, &QCheckBox::checkStateChanged, this,57[this]() { onMinimalOutputLatencyToggled(); });58connect(m_ui.bufferMS, &QSlider::valueChanged, this, &AudioSettingsWidget::updateMinimumLatencyLabel);59connect(m_ui.sequenceLength, &QSlider::valueChanged, this, &AudioSettingsWidget::updateMinimumLatencyLabel);6061updateDriverNames();62onStretchModeChanged();63onMinimalOutputLatencyToggled(); // also calls updateLatencyLabel()6465// for per-game, just use the normal path, since it needs to re-read/apply66if (!dialog->isPerGameSettings())67{68m_ui.volume->setValue(m_dialog->getEffectiveIntValue("Audio", "OutputVolume", 100));69m_ui.fastForwardVolume->setValue(m_dialog->getEffectiveIntValue("Audio", "FastForwardVolume", 100));70m_ui.muted->setChecked(m_dialog->getEffectiveBoolValue("Audio", "OutputMuted", false));71connect(m_ui.volume, &QSlider::valueChanged, this, &AudioSettingsWidget::onOutputVolumeChanged);72connect(m_ui.fastForwardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onFastForwardVolumeChanged);73connect(m_ui.muted, &QCheckBox::checkStateChanged, this, &AudioSettingsWidget::onOutputMutedChanged);74updateVolumeLabel();75}76else77{78SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.volume, m_ui.volumeLabel, tr("%"), "Audio",79"OutputVolume", 100);80SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.fastForwardVolume, m_ui.fastForwardVolumeLabel,81tr("%"), "Audio", "FastForwardVolume", 100);82SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muted, "Audio", "OutputMuted", false);83}84connect(m_ui.resetVolume, &QPushButton::clicked, this, [this]() { resetVolume(false); });85connect(m_ui.resetFastForwardVolume, &QPushButton::clicked, this, [this]() { resetVolume(true); });86connect(m_ui.resetBufferSize, &QPushButton::clicked, this, &AudioSettingsWidget::onResetBufferSizeClicked);87connect(m_ui.resetSequenceLength, &QPushButton::clicked, this,88&AudioSettingsWidget::onResetStretchSequenceLengthClicked);89connect(m_ui.resetSeekWindowSize, &QPushButton::clicked, this, &AudioSettingsWidget::onResetStretchSeekWindowClicked);90connect(m_ui.resetOverlap, &QPushButton::clicked, this, &AudioSettingsWidget::onResetStretchOverlapClicked);9192dialog->registerWidgetHelp(93m_ui.audioBackend, tr("Audio Backend"), QStringLiteral("Cubeb"),94tr("The audio backend determines how frames produced by the emulator are submitted to the host. Cubeb provides the "95"lowest latency, if you encounter issues, try the SDL backend. The null backend disables all host audio "96"output."));97dialog->registerWidgetHelp(98m_ui.bufferMS, tr("Buffer Size"), tr("%1 ms").arg(AudioStreamParameters::DEFAULT_BUFFER_MS),99tr("The buffer size determines the size of the chunks of audio which will be pulled by the "100"host. Smaller values reduce the output latency, but may cause hitches if the emulation "101"speed is inconsistent. Note that the Cubeb backend uses smaller chunks regardless of "102"this value, so using a low value here may not significantly change latency."));103dialog->registerWidgetHelp(104m_ui.outputLatencyMS, tr("Output Latency"), tr("%1 ms").arg(AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS),105tr("Determines how much latency there is between the audio being picked up by the host API, and "106"played through speakers."));107dialog->registerWidgetHelp(m_ui.outputLatencyMinimal, tr("Minimal Output Latency"), tr("Unchecked"),108tr("When enabled, the minimum supported output latency will be used for the host API."));109dialog->registerWidgetHelp(m_ui.volume, tr("Output Volume"), "100%",110tr("Controls the volume of the audio played on the host."));111dialog->registerWidgetHelp(m_ui.fastForwardVolume, tr("Fast Forward Volume"), "100%",112tr("Controls the volume of the audio played on the host when fast forwarding."));113dialog->registerWidgetHelp(m_ui.muted, tr("Mute All Sound"), tr("Unchecked"),114tr("Prevents the emulator from producing any audible sound."));115dialog->registerWidgetHelp(m_ui.muteCDAudio, tr("Mute CD Audio"), tr("Unchecked"),116tr("Forcibly mutes both CD-DA and XA audio from the CD-ROM. Can be used to disable "117"background music in some games."));118dialog->registerWidgetHelp(119m_ui.stretchMode, tr("Stretch Mode"), tr("Time Stretching"),120tr("When running outside of 100% speed, adjusts the tempo on audio instead of dropping frames. Produces "121"much nicer fast forward/slowdown audio at a small cost to performance."));122dialog->registerWidgetHelp(m_ui.resetVolume, tr("Reset Volume"), tr("N/A"),123m_dialog->isPerGameSettings() ? tr("Resets volume back to the global/inherited setting.") :124tr("Resets volume back to the default, i.e. full."));125dialog->registerWidgetHelp(m_ui.resetFastForwardVolume, tr("Reset Fast Forward Volume"), tr("N/A"),126m_dialog->isPerGameSettings() ? tr("Resets volume back to the global/inherited setting.") :127tr("Resets volume back to the default, i.e. full."));128dialog->registerWidgetHelp(m_ui.sequenceLength, tr("Stretch Sequence Length"),129tr("%1 ms").arg(AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH),130tr("Determines how long individual sequences are when the time-stretch algorithm chops "131"the audio. Longer sequences can improve quality but increase latency."));132dialog->registerWidgetHelp(133m_ui.seekWindowSize, tr("Stretch Seek Window"), tr("%1 ms").arg(AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW),134tr("Controls how wide a window the algorithm searches for the best overlap position when joining "135"consecutive sequences. Larger windows may yield better joins at the cost of increased CPU work."));136dialog->registerWidgetHelp(m_ui.overlap, tr("Stretch Overlap Length"),137tr("%1 ms").arg(AudioStreamParameters::DEFAULT_STRETCH_OVERLAP),138tr("Specifies how long two consecutive sequences are overlapped when mixed back together. "139"Greater overlap can make transitions smoother but increases latency."));140dialog->registerWidgetHelp(m_ui.useQuickSeek, tr("Enable Quick Seek"),141AudioStreamParameters::DEFAULT_STRETCH_USE_QUICKSEEK ? tr("Checked") : tr("Unchecked"),142tr("Enables the quick seeking algorithm in the time-stretch routine. Reduces CPU usage at "143"a minor cost to audio quality."));144dialog->registerWidgetHelp(m_ui.useAAFilter, tr("Enable Anti-Alias Filter"),145AudioStreamParameters::DEFAULT_STRETCH_USE_AA_FILTER ? tr("Checked") : tr("Unchecked"),146tr("Enables an anti-aliasing filter used by the pitch transposer. Disabling it may reduce "147"quality when pitch shifting but can slightly reduce CPU usage."));148dialog->registerWidgetHelp(m_ui.resetSequenceLength, tr("Reset Sequence Length"), tr("N/A"),149m_dialog->isPerGameSettings() ? tr("Resets value back to the global/inherited setting.") :150tr("Resets value back to the default."));151dialog->registerWidgetHelp(m_ui.resetSeekWindowSize, tr("Reset Seek Window"), tr("N/A"),152m_dialog->isPerGameSettings() ? tr("Resets value back to the global/inherited setting.") :153tr("Resets value back to the default."));154dialog->registerWidgetHelp(m_ui.resetOverlap, tr("Reset Overlap"), tr("N/A"),155m_dialog->isPerGameSettings() ? tr("Resets value back to the global/inherited setting.") :156tr("Resets value back to the default."));157}158159AudioSettingsWidget::~AudioSettingsWidget() = default;160161void AudioSettingsWidget::onStretchModeChanged()162{163const AudioStretchMode stretch_mode =164CoreAudioStream::ParseStretchMode(165m_dialog166->getEffectiveStringValue("Audio", "StretchMode",167CoreAudioStream::GetStretchModeName(AudioStreamParameters::DEFAULT_STRETCH_MODE))168.c_str())169.value_or(AudioStreamParameters::DEFAULT_STRETCH_MODE);170m_ui.timeStretchGroup->setEnabled(stretch_mode == AudioStretchMode::TimeStretch);171updateMinimumLatencyLabel();172}173174AudioBackend AudioSettingsWidget::getEffectiveBackend() const175{176return AudioStream::ParseBackendName(177m_dialog178->getEffectiveStringValue("Audio", "Backend", AudioStream::GetBackendName(AudioStream::DEFAULT_BACKEND))179.c_str())180.value_or(AudioStream::DEFAULT_BACKEND);181}182183void AudioSettingsWidget::updateDriverNames()184{185const AudioBackend backend = getEffectiveBackend();186std::vector<std::pair<std::string, std::string>> names = AudioStream::GetDriverNames(backend);187188SettingWidgetBinder::DisconnectWidget(m_ui.driver);189m_ui.driver->clear();190if (names.empty())191{192m_ui.driver->addItem(tr("Default"));193m_ui.driver->setVisible(false);194195// I hate this so much but it's the only way to stop Qt leaving a gap on the edge.196// Of course could use a nested layout, but that breaks on MacOS.197m_ui.configurationLayout->removeWidget(m_ui.driver);198m_ui.configurationLayout->removeWidget(m_ui.audioBackend);199m_ui.configurationLayout->addWidget(m_ui.audioBackend, 0, 1, 1, 2);200}201else202{203m_ui.driver->setVisible(true);204m_ui.configurationLayout->removeWidget(m_ui.audioBackend);205m_ui.configurationLayout->removeWidget(m_ui.driver);206m_ui.configurationLayout->addWidget(m_ui.audioBackend, 0, 1, 1, 1);207m_ui.configurationLayout->addWidget(m_ui.driver, 0, 2, 1, 1);208209for (const auto& [name, display_name] : names)210m_ui.driver->addItem(QString::fromStdString(display_name), QString::fromStdString(name));211212SettingWidgetBinder::BindWidgetToStringSetting(m_dialog->getSettingsInterface(), m_ui.driver, "Audio", "Driver",213std::move(names.front().first));214connect(m_ui.driver, &QComboBox::currentIndexChanged, this, &AudioSettingsWidget::queueUpdateDeviceNames);215}216217queueUpdateDeviceNames();218}219220void AudioSettingsWidget::queueUpdateDeviceNames()221{222SettingWidgetBinder::DisconnectWidget(m_ui.outputDevice);223m_ui.outputDevice->clear();224m_ui.outputDevice->setEnabled(false);225m_output_device_latency = 0;226227const AudioBackend backend = getEffectiveBackend();228std::string driver_name = m_dialog->getEffectiveStringValue("Audio", "Driver");229QtAsyncTask::create(this, [this, driver_name = std::move(driver_name), backend]() {230std::vector<AudioStream::DeviceInfo> devices =231AudioStream::GetOutputDevices(backend, driver_name, SPU::SAMPLE_RATE);232return [this, devices = std::move(devices), driver_name = std::move(driver_name), backend]() {233// just in case we executed out of order...234if (backend != getEffectiveBackend() || driver_name != m_dialog->getEffectiveStringValue("Audio", "Driver"))235return;236237if (devices.empty())238{239m_ui.outputDevice->addItem(tr("Default"));240}241else242{243const std::string current_device = m_dialog->getEffectiveStringValue("Audio", "Device");244245m_ui.outputDevice->setEnabled(true);246247bool is_known_device = false;248for (const AudioStream::DeviceInfo& di : devices)249{250m_ui.outputDevice->addItem(QString::fromStdString(di.display_name), QString::fromStdString(di.name));251if (di.name == current_device)252{253m_output_device_latency = di.minimum_latency_frames;254is_known_device = true;255}256}257258if (!is_known_device)259{260m_ui.outputDevice->addItem(tr("Unknown Device \"%1\"").arg(QString::fromStdString(current_device)),261QString::fromStdString(current_device));262}263264SettingWidgetBinder::BindWidgetToStringSetting(m_dialog->getSettingsInterface(), m_ui.outputDevice, "Audio",265"OutputDevice", std::move(devices.front().name));266}267268updateMinimumLatencyLabel();269};270});271}272273void AudioSettingsWidget::updateLatencyLabel()274{275const bool minimal_output_latency = m_dialog->getEffectiveBoolValue(276"Audio", "OutputLatencyMinimal", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MINIMAL);277const int config_output_latency_ms =278minimal_output_latency ?2790 :280m_dialog->getEffectiveIntValue("Audio", "OutputLatencyMS", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);281282m_ui.outputLatencyLabel->setText(minimal_output_latency ? tr("N/A") : tr("%1 ms").arg(config_output_latency_ms));283284updateMinimumLatencyLabel();285}286287void AudioSettingsWidget::updateMinimumLatencyLabel()288{289const AudioStretchMode stretch_mode =290CoreAudioStream::ParseStretchMode(m_dialog->getEffectiveStringValue("Audio", "StretchMode").c_str())291.value_or(AudioStreamParameters::DEFAULT_STRETCH_MODE);292const bool minimal_output_latency = m_dialog->getEffectiveBoolValue(293"Audio", "OutputLatencyMinimal", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MINIMAL);294const int config_buffer_ms =295m_dialog->getEffectiveIntValue("Audio", "BufferMS", AudioStreamParameters::DEFAULT_BUFFER_MS);296const int config_output_latency_ms =297minimal_output_latency ?2980 :299m_dialog->getEffectiveIntValue("Audio", "OutputLatencyMS", AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS);300const int stretch_sequence_length_ms =301(stretch_mode == AudioStretchMode::TimeStretch) ?302m_dialog->getEffectiveIntValue("Audio", "StretchSequenceLengthMS",303AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH) :3040;305306const int output_latency_ms =307minimal_output_latency ?308static_cast<int>(CoreAudioStream::GetMSForBufferSize(SPU::SAMPLE_RATE, m_output_device_latency)) :309config_output_latency_ms;310const int total_latency_ms = stretch_sequence_length_ms + config_buffer_ms + output_latency_ms;311if (output_latency_ms > 0)312{313if (stretch_sequence_length_ms > 0)314{315m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (%2 ms stretch + %3 ms buffer + %4 ms output)")316.arg(total_latency_ms)317.arg(stretch_sequence_length_ms)318.arg(config_buffer_ms)319.arg(output_latency_ms));320}321else322{323m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (%2 ms buffer + %3 ms output)")324.arg(total_latency_ms)325.arg(config_buffer_ms)326.arg(output_latency_ms));327}328}329else330{331if (stretch_sequence_length_ms > 0)332{333m_ui.bufferingLabel->setText(334tr("Maximum Latency: %1 ms (%2 ms stretch + %3 ms buffer, minimum output latency unknown)")335.arg(total_latency_ms)336.arg(stretch_sequence_length_ms)337.arg(config_buffer_ms));338}339else340{341m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (minimum output latency unknown)").arg(config_buffer_ms));342}343}344}345346void AudioSettingsWidget::updateVolumeLabel()347{348m_ui.volumeLabel->setText(tr("%1%").arg(m_ui.volume->value()));349m_ui.fastForwardVolumeLabel->setText(tr("%1%").arg(m_ui.fastForwardVolume->value()));350}351352void AudioSettingsWidget::onMinimalOutputLatencyToggled()353{354const bool minimal = m_dialog->getEffectiveBoolValue("Audio", "OutputLatencyMinimal", false);355m_ui.outputLatencyMS->setEnabled(!minimal);356updateLatencyLabel();357}358359void AudioSettingsWidget::onOutputVolumeChanged(int new_value)360{361// only called for base settings362DebugAssert(!m_dialog->isPerGameSettings());363Core::SetBaseIntSettingValue("Audio", "OutputVolume", new_value);364Host::CommitBaseSettingChanges();365g_core_thread->setAudioOutputVolume(new_value, m_ui.fastForwardVolume->value());366367updateVolumeLabel();368}369370void AudioSettingsWidget::onFastForwardVolumeChanged(int new_value)371{372// only called for base settings373DebugAssert(!m_dialog->isPerGameSettings());374Core::SetBaseIntSettingValue("Audio", "FastForwardVolume", new_value);375Host::CommitBaseSettingChanges();376g_core_thread->setAudioOutputVolume(m_ui.volume->value(), new_value);377378updateVolumeLabel();379}380381void AudioSettingsWidget::onOutputMutedChanged(int new_state)382{383// only called for base settings384DebugAssert(!m_dialog->isPerGameSettings());385386const bool muted = (new_state != 0);387Core::SetBaseBoolSettingValue("Audio", "OutputMuted", muted);388Host::CommitBaseSettingChanges();389g_core_thread->setAudioOutputMuted(muted);390}391392void AudioSettingsWidget::resetVolume(bool fast_forward)393{394const char* key = fast_forward ? "FastForwardVolume" : "OutputVolume";395QSlider* const slider = fast_forward ? m_ui.fastForwardVolume : m_ui.volume;396QLabel* const label = fast_forward ? m_ui.fastForwardVolumeLabel : m_ui.volumeLabel;397398if (m_dialog->isPerGameSettings())399{400m_dialog->removeSettingValue("Audio", key);401402const int value = m_dialog->getEffectiveIntValue("Audio", key, 100);403QSignalBlocker sb(slider);404slider->setValue(value);405label->setText(QStringLiteral("%1%2").arg(value).arg(tr("%")));406407// remove bold font if it was previously overridden408QFont font(label->font());409font.setBold(false);410label->setFont(font);411}412else413{414slider->setValue(100);415}416}417418void AudioSettingsWidget::onResetBufferSizeClicked()419{420m_dialog->setIntSettingValue(421"Audio", "BufferMS",422m_dialog->isPerGameSettings() ? std::nullopt : std::optional<int>(AudioStreamParameters::DEFAULT_BUFFER_MS));423SettingWidgetBinder::DisconnectWidget(m_ui.bufferMS);424SettingWidgetBinder::BindWidgetToIntSetting(m_dialog->getSettingsInterface(), m_ui.bufferMS, "Audio", "BufferMS",425AudioStreamParameters::DEFAULT_BUFFER_MS);426QtUtils::BindLabelToSlider(m_ui.bufferMS, m_ui.bufferMSLabel, 1.0f, tr("%1 ms"));427connect(m_ui.bufferMS, &QSlider::valueChanged, this, &AudioSettingsWidget::updateMinimumLatencyLabel);428updateMinimumLatencyLabel();429}430431void AudioSettingsWidget::onResetStretchSequenceLengthClicked()432{433m_dialog->setIntSettingValue("Audio", "StretchSequenceLengthMS",434m_dialog->isPerGameSettings() ?435std::nullopt :436std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH));437438SettingWidgetBinder::DisconnectWidget(m_ui.sequenceLength);439SettingWidgetBinder::BindWidgetToIntSetting(m_dialog->getSettingsInterface(), m_ui.sequenceLength, "Audio",440"StretchSequenceLengthMS",441AudioStreamParameters::DEFAULT_STRETCH_SEQUENCE_LENGTH, 0);442QtUtils::BindLabelToSlider(m_ui.sequenceLength, m_ui.sequenceLengthLabel, 1.0f, tr("%1 ms"));443connect(m_ui.sequenceLength, &QSlider::valueChanged, this, &AudioSettingsWidget::updateMinimumLatencyLabel);444updateMinimumLatencyLabel();445}446447void AudioSettingsWidget::onResetStretchSeekWindowClicked()448{449m_dialog->setIntSettingValue("Audio", "StretchSeekWindowMS",450m_dialog->isPerGameSettings() ?451std::nullopt :452std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW));453454SettingWidgetBinder::DisconnectWidget(m_ui.seekWindowSize);455SettingWidgetBinder::BindWidgetToIntSetting(m_dialog->getSettingsInterface(), m_ui.seekWindowSize, "Audio",456"StretchSeekWindowMS", AudioStreamParameters::DEFAULT_STRETCH_SEEKWINDOW,4570);458QtUtils::BindLabelToSlider(m_ui.seekWindowSize, m_ui.seekWindowSizeLabel, 1.0f, tr("%1 ms"));459}460461void AudioSettingsWidget::onResetStretchOverlapClicked()462{463m_dialog->setIntSettingValue(464"Audio", "StretchOverlapMS",465m_dialog->isPerGameSettings() ? std::nullopt : std::optional<int>(AudioStreamParameters::DEFAULT_STRETCH_OVERLAP));466467SettingWidgetBinder::DisconnectWidget(m_ui.overlap);468SettingWidgetBinder::BindWidgetToIntSetting(m_dialog->getSettingsInterface(), m_ui.overlap, "Audio",469"StretchOverlapMS", AudioStreamParameters::DEFAULT_STRETCH_OVERLAP, 0);470QtUtils::BindLabelToSlider(m_ui.overlap, m_ui.overlapLabel, 1.0f, tr("%1 ms"));471}472473474