Skip to content

Commit 5b1ac3e

Browse files
committed
Qt: Fast forward volume multiplier
Replaced the fast forward volume absolute slider with a multiplier slider that is bound to the standard volume, guaranteeing that the fast forward volume is always less than or equal to the standard volume
1 parent 2c608b4 commit 5b1ac3e

File tree

3 files changed

+150
-29
lines changed

3 files changed

+150
-29
lines changed

pcsx2-qt/Settings/AudioSettingsWidget.cpp

Lines changed: 133 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,20 @@
1515
#include "pcsx2/VMManager.h"
1616

1717
#include <QtWidgets/QMessageBox>
18+
#include <array>
1819
#include <algorithm>
1920
#include <bit>
21+
#include <cmath>
22+
23+
const int FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT = 5;
24+
const std::array<float, FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT + 1> s_fast_forward_multipliers = {
25+
0.0f,
26+
0.1f,
27+
0.25f,
28+
0.5f,
29+
0.75f,
30+
1.0f,
31+
};
2032

2133
AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
2234
: SettingsWidget(settings_dialog, parent)
@@ -73,20 +85,29 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
7385
// for per-game, just use the normal path, since it needs to re-read/apply
7486
if (!dialog()->isPerGameSettings())
7587
{
76-
m_ui.standardVolume->setValue(dialog()->getEffectiveIntValue("SPU2/Output", "StandardVolume", 100));
77-
m_ui.fastForwardVolume->setValue(dialog()->getEffectiveIntValue("SPU2/Output", "FastForwardVolume", 100));
88+
const int standard_volume = dialog()->getEffectiveIntValue("SPU2/Output", "StandardVolume", 100);
89+
const int fast_forward_volume = dialog()->getEffectiveIntValue("SPU2/Output", "FastForwardVolume", standard_volume);
90+
91+
m_ui.standardVolume->setValue(standard_volume);
92+
m_ui.fastForwardVolume->setValue(getNearestFastForwardMultiplierIndex(standard_volume, fast_forward_volume));
7893
m_ui.muted->setChecked(dialog()->getEffectiveBoolValue("SPU2/Output", "OutputMuted", false));
7994
connect(m_ui.standardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onStandardVolumeChanged);
8095
connect(m_ui.fastForwardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onFastForwardVolumeChanged);
8196
connect(m_ui.muted, &QCheckBox::checkStateChanged, this, &AudioSettingsWidget::onOutputMutedChanged);
82-
updateVolumeLabel();
8397
}
8498
else
8599
{
86100
SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.standardVolume, m_ui.standardVolumeLabel, tr("%"), "SPU2/Output", "StandardVolume", 100);
87-
SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.fastForwardVolume, m_ui.fastForwardVolumeLabel, tr("%"), "SPU2/Output", "FastForwardVolume", 100);
88101
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muted, "SPU2/Output", "OutputMuted", false);
102+
103+
const int standard_volume = dialog()->getEffectiveIntValue("SPU2/Output", "StandardVolume", 100);
104+
const int fast_forward_volume = dialog()->getEffectiveIntValue("SPU2/Output", "FastForwardVolume", standard_volume);
105+
m_ui.fastForwardVolume->setValue(getNearestFastForwardMultiplierIndex(standard_volume, fast_forward_volume));
106+
107+
connect(m_ui.standardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onStandardVolumeChanged);
108+
connect(m_ui.fastForwardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onFastForwardVolumeChanged);
89109
}
110+
updateVolumeLabel();
90111
connect(m_ui.resetStandardVolume, &QToolButton::clicked, this, [this]() { resetVolume(false); });
91112
connect(m_ui.resetFastForwardVolume, &QToolButton::clicked, this, [this]() { resetVolume(true); });
92113

@@ -105,8 +126,8 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
105126
"to reduce audio delay."));
106127
dialog()->registerWidgetHelp(m_ui.standardVolume, tr("Standard Volume"), "100%",
107128
tr("Controls the volume of the audio played on the host at normal speed."));
108-
dialog()->registerWidgetHelp(m_ui.fastForwardVolume, tr("Fast Forward Volume"), "100%",
109-
tr("Controls the volume of the audio played on the host when fast forwarding."));
129+
dialog()->registerWidgetHelp(m_ui.fastForwardVolume, tr("Fast Forward Volume Multiplier"), tr("1x Standard (Default)"),
130+
tr("Controls the volume multiplier used while fast forwarding, relative to the standard volume."));
110131
dialog()->registerWidgetHelp(m_ui.muted, tr("Mute All Sound"), tr("Unchecked"),
111132
tr("Prevents the emulator from producing any audible sound."));
112133
dialog()->registerWidgetHelp(m_ui.expansionMode, tr("Expansion Mode"), tr("Disabled (Stereo)"),
@@ -121,9 +142,9 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
121142
dialog()->registerWidgetHelp(m_ui.resetStandardVolume, tr("Reset Standard Volume"), tr("N/A"),
122143
dialog()->isPerGameSettings() ? tr("Resets standard volume back to the global/inherited setting.") :
123144
tr("Resets standard volume back to the default."));
124-
dialog()->registerWidgetHelp(m_ui.resetFastForwardVolume, tr("Reset Fast Forward Volume"), tr("N/A"),
125-
dialog()->isPerGameSettings() ? tr("Resets fast forward volume back to the global/inherited setting.") :
126-
tr("Resets fast forward volume back to the default."));
145+
dialog()->registerWidgetHelp(m_ui.resetFastForwardVolume, tr("Reset Fast Forward Volume Multiplier"), tr("N/A"),
146+
dialog()->isPerGameSettings() ? tr("Resets fast forward volume multiplier back to the global/inherited setting.") :
147+
tr("Resets fast forward volume multiplier back to the default."));
127148
}
128149

129150
AudioSettingsWidget::~AudioSettingsWidget() = default;
@@ -293,7 +314,7 @@ void AudioSettingsWidget::updateLatencyLabel()
293314
void AudioSettingsWidget::updateVolumeLabel()
294315
{
295316
m_ui.standardVolumeLabel->setText(tr("%1%").arg(m_ui.standardVolume->value()));
296-
m_ui.fastForwardVolumeLabel->setText(tr("%1%").arg(m_ui.fastForwardVolume->value()));
317+
m_ui.fastForwardVolumeLabel->setText(getFastForwardVolumeLabel(m_ui.fastForwardVolume->value()));
297318
}
298319

299320
void AudioSettingsWidget::onMinimalOutputLatencyChanged()
@@ -308,20 +329,15 @@ void AudioSettingsWidget::onStandardVolumeChanged(const int new_value)
308329
// only called for base settings
309330
pxAssert(!dialog()->isPerGameSettings());
310331
Host::SetBaseIntSettingValue("SPU2/Output", "StandardVolume", new_value);
311-
Host::CommitBaseSettingChanges();
312-
g_emu_thread->applySettings();
313332

333+
updateFastForwardVolumeSetting();
314334
updateVolumeLabel();
315335
}
316336

317-
void AudioSettingsWidget::onFastForwardVolumeChanged(const int new_value)
337+
void AudioSettingsWidget::onFastForwardVolumeChanged()
318338
{
319-
// only called for base settings
320-
pxAssert(!dialog()->isPerGameSettings());
321-
Host::SetBaseIntSettingValue("SPU2/Output", "FastForwardVolume", new_value);
322-
Host::CommitBaseSettingChanges();
323-
g_emu_thread->applySettings();
324339

340+
updateFastForwardVolumeSetting();
325341
updateVolumeLabel();
326342
}
327343

@@ -336,6 +352,73 @@ void AudioSettingsWidget::onOutputMutedChanged(const int new_state)
336352
g_emu_thread->applySettings();
337353
}
338354

355+
int AudioSettingsWidget::getNearestFastForwardMultiplierIndex(const int standard_volume, const int fast_forward_volume) const
356+
{
357+
if (standard_volume <= 0)
358+
return FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT;
359+
360+
const float ratio = static_cast<float>(std::clamp(fast_forward_volume, 0, standard_volume)) /
361+
static_cast<float>(standard_volume);
362+
int best_index = FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT;
363+
float best_distance = std::abs(ratio - s_fast_forward_multipliers[best_index]);
364+
365+
for (int i = 0; i < static_cast<int>(s_fast_forward_multipliers.size()); i++)
366+
{
367+
const float distance = std::abs(ratio - s_fast_forward_multipliers[i]);
368+
if (distance < best_distance)
369+
{
370+
best_index = i;
371+
best_distance = distance;
372+
}
373+
}
374+
375+
return best_index;
376+
}
377+
378+
int AudioSettingsWidget::getComputedFastForwardVolume(const int standard_volume, const int multiplier_index) const
379+
{
380+
const int clamped_index = std::clamp(multiplier_index, 0, FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT);
381+
const float multiplier = s_fast_forward_multipliers[clamped_index];
382+
return std::clamp(static_cast<int>(std::lround(static_cast<float>(standard_volume) * multiplier)), 0, 200);
383+
}
384+
385+
QString AudioSettingsWidget::getFastForwardVolumeLabel(const int multiplier_index) const
386+
{
387+
switch (std::clamp(multiplier_index, 0, FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT))
388+
{
389+
case 0:
390+
return tr("Muted");
391+
case 1:
392+
return tr("0.10x");
393+
case 2:
394+
return tr("0.25x");
395+
case 3:
396+
return tr("0.50x");
397+
case 4:
398+
return tr("0.75x");
399+
case 5:
400+
default:
401+
return tr("1.00x");
402+
}
403+
}
404+
405+
void AudioSettingsWidget::updateFastForwardVolumeSetting()
406+
{
407+
const int standard_volume = m_ui.standardVolume->value();
408+
const int fast_forward_volume = getComputedFastForwardVolume(standard_volume, m_ui.fastForwardVolume->value());
409+
410+
if (!dialog()->isPerGameSettings())
411+
{
412+
Host::SetBaseIntSettingValue("SPU2/Output", "FastForwardVolume", fast_forward_volume);
413+
Host::CommitBaseSettingChanges();
414+
g_emu_thread->applySettings();
415+
}
416+
else
417+
{
418+
dialog()->setIntSettingValue("SPU2/Output", "FastForwardVolume", fast_forward_volume);
419+
}
420+
}
421+
339422
void AudioSettingsWidget::onExpansionSettingsClicked()
340423
{
341424
QDialog dlg(QtUtils::GetRootWidget(this));
@@ -483,24 +566,51 @@ void AudioSettingsWidget::resetVolume(const bool fast_forward)
483566
const char* key = fast_forward ? "FastForwardVolume" : "StandardVolume";
484567
QSlider* const slider = fast_forward ? m_ui.fastForwardVolume : m_ui.standardVolume;
485568
QLabel* const label = fast_forward ? m_ui.fastForwardVolumeLabel : m_ui.standardVolumeLabel;
569+
const bool perGame = dialog()->isPerGameSettings();
486570

487-
if (dialog()->isPerGameSettings())
571+
resetVolumeAction(perGame, key, slider, label, fast_forward);
572+
updateVolumeLabel();
573+
}
574+
575+
void AudioSettingsWidget::resetVolumeAction(bool per_game, const char* key, QSlider* slider, QLabel* label,
576+
bool fast_forward)
577+
{
578+
if (per_game)
488579
{
489580
dialog()->removeSettingValue("SPU2/Output", key);
490581

491-
const int value = dialog()->getEffectiveIntValue("SPU2/Output", key, 100);
492582
QSignalBlocker sb(slider);
493-
slider->setValue(value);
494-
label->setText(QStringLiteral("%1%2").arg(value).arg(tr("%")));
583+
if (fast_forward)
584+
{
585+
const int standard_volume = dialog()->getEffectiveIntValue("SPU2/Output", "StandardVolume", 100);
586+
const int fast_forward_volume = dialog()->getEffectiveIntValue("SPU2/Output", "FastForwardVolume", standard_volume);
587+
slider->setValue(getNearestFastForwardMultiplierIndex(standard_volume, fast_forward_volume));
588+
}
589+
else
590+
{
591+
slider->setValue(dialog()->getEffectiveIntValue("SPU2/Output", key, 100));
592+
}
495593

496594
// remove bold font if it was previously overridden
497595
QFont font(label->font());
498596
font.setBold(false);
499597
label->setFont(font);
598+
599+
if (!fast_forward)
600+
updateFastForwardVolumeSetting();
500601
}
501602
else
502603
{
503-
slider->setValue(100);
604+
if (fast_forward)
605+
{
606+
QSignalBlocker sb(slider);
607+
slider->setValue(FAST_FORWARD_MULTIPLIER_INDEX_DEFAULT);
608+
updateFastForwardVolumeSetting();
609+
}
610+
else
611+
{
612+
slider->setValue(100);
613+
}
504614
}
505615
}
506616

pcsx2-qt/Settings/AudioSettingsWidget.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ private Q_SLOTS:
2828
void updateVolumeLabel();
2929
void onMinimalOutputLatencyChanged();
3030
void onStandardVolumeChanged(const int new_value);
31-
void onFastForwardVolumeChanged(const int new_value);
31+
void onFastForwardVolumeChanged();
3232
void onOutputMutedChanged(const int new_state);
33+
void resetVolumeAction(bool per_game, const char* key, QSlider* slider, QLabel* label, bool fast_forward);
3334

3435
void onExpansionSettingsClicked();
3536
void onStretchSettingsClicked();
@@ -38,6 +39,10 @@ private Q_SLOTS:
3839
AudioBackend getEffectiveBackend() const;
3940
AudioExpansionMode getEffectiveExpansionMode() const;
4041
u32 getEffectiveExpansionBlockSize() const;
42+
int getNearestFastForwardMultiplierIndex(int standard_volume, int fast_forward_volume) const;
43+
int getComputedFastForwardVolume(int standard_volume, int multiplier_index) const;
44+
QString getFastForwardVolumeLabel(int multiplier_index) const;
45+
void updateFastForwardVolumeSetting();
4146
void resetVolume(const bool fast_forward);
4247

4348
Ui::AudioSettingsWidget m_ui;

pcsx2-qt/Settings/AudioSettingsWidget.ui

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,16 @@
304304
<item>
305305
<widget class="QSlider" name="fastForwardVolume">
306306
<property name="maximum">
307-
<number>200</number>
307+
<number>5</number>
308+
</property>
309+
<property name="singleStep">
310+
<number>1</number>
311+
</property>
312+
<property name="pageStep">
313+
<number>1</number>
308314
</property>
309315
<property name="value">
310-
<number>100</number>
316+
<number>5</number>
311317
</property>
312318
<property name="orientation">
313319
<enum>Qt::Orientation::Horizontal</enum>
@@ -316,7 +322,7 @@
316322
<enum>QSlider::TickPosition::TicksBothSides</enum>
317323
</property>
318324
<property name="tickInterval">
319-
<number>10</number>
325+
<number>1</number>
320326
</property>
321327
</widget>
322328
</item>
@@ -329,7 +335,7 @@
329335
</sizepolicy>
330336
</property>
331337
<property name="text">
332-
<string>100%</string>
338+
<string>1x Standard (Default)</string>
333339
</property>
334340
<property name="alignment">
335341
<set>Qt::AlignmentFlag::AlignCenter</set>
@@ -339,7 +345,7 @@
339345
<item>
340346
<widget class="QToolButton" name="resetFastForwardVolume">
341347
<property name="toolTip">
342-
<string>Reset Fast Forward Volume</string>
348+
<string>Reset Fast Forward Volume Multiplier</string>
343349
</property>
344350
<property name="icon">
345351
<iconset theme="restart-line"/>

0 commit comments

Comments
 (0)