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
2133AudioSettingsWidget::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
129150AudioSettingsWidget::~AudioSettingsWidget () = default ;
@@ -293,7 +314,7 @@ void AudioSettingsWidget::updateLatencyLabel()
293314void 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
299320void 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+
339422void 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
0 commit comments