UI - Add Volume Controls + Mute Toggle (F2) (#2871)

* Add the ability to toggle mute in the status bar.

* Add the ability to toggle mute in the status bar.

* Formatting fixes

* Add hotkey (F2) to mute

* Add default hotkey to config.json

* Add ability to change volume via slider.

* Fix Headless

* Fix SDL2 Problem : Credits to d3xMachina

* Remove unnecessary work

* Address gdk comments

* Toggling with Hotkey now properly restores volume to original level.

* Toggling with Hotkey now properly restores volume to original level.

* Update UI to show Volume % instead of Muted/Unmuted

* Clean up the volume ui a bit.

* Undo unintentionally committed code.

* Implement AudRen Support

* Restore intiial volume level in function definition.

* Finalize UI

* Finalize UI

* Use clamp for bounds check

* Use Math.Clamp for volume in soundio

* Address comments by gdkchan

* Address remaining comments

* Fix missing semicolon

* Address remaining gdkchan comment

* Fix comment

* Change /* to //

* Allow volume slider to change volume immediately.
Also force label text to cast to int to prevent decimals from showing in status bar

* Remove blank line

* Undo setting of volume level when "Cancel" is pressed.

* Fix allignment for settings window code
This commit is contained in:
sharmander 2021-12-23 11:33:56 -05:00 committed by GitHub
parent e7c2dc8ec3
commit cb43cc7e32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 411 additions and 94 deletions

View file

@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 32;
public const int CurrentVersion = 33;
public int Version { get; set; }
@ -179,6 +179,11 @@ namespace Ryujinx.Configuration
/// </summary>
public AudioBackend AudioBackend { get; set; }
/// <summary>
/// The audio volume
/// </summary>
public float AudioVolume { get; set; }
/// <summary>
/// The selected memory manager mode
/// </summary>

View file

@ -220,6 +220,11 @@ namespace Ryujinx.Configuration
/// </summary>
public ReactiveObject<AudioBackend> AudioBackend { get; private set; }
/// <summary>
/// The audio backend volume
/// </summary>
public ReactiveObject<float> AudioVolume { get; private set; }
/// <summary>
/// The selected memory manager mode
/// </summary>
@ -257,6 +262,8 @@ namespace Ryujinx.Configuration
ExpandRam.Event += static (sender, e) => LogValueChange(sender, e, nameof(ExpandRam));
IgnoreMissingServices = new ReactiveObject<bool>();
IgnoreMissingServices.Event += static (sender, e) => LogValueChange(sender, e, nameof(IgnoreMissingServices));
AudioVolume = new ReactiveObject<float>();
AudioVolume.Event += static (sender, e) => LogValueChange(sender, e, nameof(AudioVolume));
}
}
@ -460,6 +467,7 @@ namespace Ryujinx.Configuration
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
AudioBackend = System.AudioBackend,
AudioVolume = System.AudioVolume,
MemoryManagerMode = System.MemoryManagerMode,
ExpandRam = System.ExpandRam,
IgnoreMissingServices = System.IgnoreMissingServices,
@ -553,6 +561,7 @@ namespace Ryujinx.Configuration
Hid.Hotkeys.Value = new KeyboardHotkeys
{
ToggleVsync = Key.Tab,
ToggleMute = Key.F2,
Screenshot = Key.F8,
ShowUi = Key.F4,
Pause = Key.F5
@ -929,6 +938,24 @@ namespace Ryujinx.Configuration
configurationFileUpdated = true;
}
if (configurationFileFormat.Version < 33)
{
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 33.");
configurationFileFormat.Hotkeys = new KeyboardHotkeys
{
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
ShowUi = configurationFileFormat.Hotkeys.ShowUi,
Pause = configurationFileFormat.Hotkeys.Pause,
ToggleMute = Key.F2
};
configurationFileFormat.AudioVolume = 1;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
@ -960,6 +987,7 @@ namespace Ryujinx.Configuration
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
System.AudioBackend.Value = configurationFileFormat.AudioBackend;
System.AudioVolume.Value = configurationFileFormat.AudioVolume;
System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode;
System.ExpandRam.Value = configurationFileFormat.ExpandRam;
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
@ -132,6 +132,7 @@ namespace Ryujinx.Ui
[GUI] ProgressBar _progressBar;
[GUI] Box _viewBox;
[GUI] Label _vSyncStatus;
[GUI] Label _volumeStatus;
[GUI] Box _listStatusBox;
[GUI] Label _loadingStatusLabel;
[GUI] ProgressBar _loadingStatusBar;
@ -205,6 +206,7 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
if (ConfigurationState.Instance.Ui.StartFullscreen)
{
@ -305,6 +307,11 @@ namespace Ryujinx.Ui
}
}
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
{
_emulationContext?.SetVolume(e.NewValue);
}
private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
{
_fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen";
@ -562,7 +569,8 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.System.TimeZone,
ConfigurationState.Instance.System.MemoryManagerMode,
ConfigurationState.Instance.System.IgnoreMissingServices,
ConfigurationState.Instance.Graphics.AspectRatio);
ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume);
_emulationContext = new HLE.Switch(configuration);
}
@ -1108,11 +1116,12 @@ namespace Ryujinx.Ui
{
Application.Invoke(delegate
{
_gameStatus.Text = args.GameStatus;
_fifoStatus.Text = args.FifoStatus;
_gpuName.Text = args.GpuName;
_dockedMode.Text = args.DockedMode;
_aspectRatio.Text = args.AspectRatio;
_gameStatus.Text = args.GameStatus;
_fifoStatus.Text = args.FifoStatus;
_gpuName.Text = args.GpuName;
_dockedMode.Text = args.DockedMode;
_aspectRatio.Text = args.AspectRatio;
_volumeStatus.Text = GetVolumeLabelText(args.Volume);
if (args.VSyncEnabled)
{
@ -1173,6 +1182,28 @@ namespace Ryujinx.Ui
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
}
private string GetVolumeLabelText(float volume)
{
string icon = volume == 0 ? "🔇" : "🔊";
return $"{icon} {(int)(volume * 100)}%";
}
private void VolumeStatus_Clicked(object sender, ButtonReleaseEventArgs args)
{
if (_emulationContext != null)
{
if (_emulationContext.IsAudioMuted())
{
_emulationContext.SetVolume(ConfigurationState.Instance.System.AudioVolume);
}
else
{
_emulationContext.SetVolume(0);
}
}
}
private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args)
{
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;

View file

@ -294,35 +294,35 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="_pauseEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Pause emulation</property>
<property name="label" translatable="yes">Pause Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_resumeEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Resume emulation</property>
<property name="label" translatable="yes">Resume Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_stopEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Stop emulation of the current game and return to game selection</property>
<property name="label" translatable="yes">Stop Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
</object>
</child>
<object class="GtkMenuItem" id="_pauseEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Pause emulation</property>
<property name="label" translatable="yes">Pause Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_resumeEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Resume emulation</property>
<property name="label" translatable="yes">Resume Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_stopEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Stop emulation of the current game and return to game selection</property>
<property name="label" translatable="yes">Stop Emulation</property>
<property name="use_underline">True</property>
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
@ -647,14 +647,15 @@
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="button_release_event" handler="AspectRatio_Clicked" swapped="no"/>
<signal name="button_release_event" handler="VolumeStatus_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_aspectRatio">
<object class="GtkLabel" id="_volumeStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="label" translatable="yes"></property>
</object>
</child>
</object>
@ -676,12 +677,19 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="_gameStatus">
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<signal name="button_release_event" handler="AspectRatio_Clicked" swapped="no"/>
<child>
<object class="GtkLabel" id="_aspectRatio">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
@ -701,7 +709,7 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="_fifoStatus">
<object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
@ -725,6 +733,31 @@
<property name="position">9</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_fifoStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_gpuName">
<property name="visible">True</property>
@ -736,7 +769,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">10</property>
<property name="position">12</property>
</packing>
</child>
</object>

View file

@ -425,6 +425,7 @@ namespace Ryujinx.Ui
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
Device.EnableDeviceVsync,
Device.GetVolume(),
dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
@ -598,6 +599,19 @@ namespace Ryujinx.Ui
(Toplevel as MainWindow)?.TogglePause();
}
if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute) &&
!_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute))
{
if (Device.IsAudioMuted())
{
Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
}
else
{
Device.SetVolume(0);
}
}
_prevHotkeyState = currentHotkeyState;
}
@ -627,7 +641,8 @@ namespace Ryujinx.Ui
ToggleVSync = 1 << 0,
Screenshot = 1 << 1,
ShowUi = 1 << 2,
Pause = 1 << 3
Pause = 1 << 3,
ToggleMute = 1 << 4
}
private KeyboardHotkeyState GetHotkeyState()
@ -654,6 +669,11 @@ namespace Ryujinx.Ui
state |= KeyboardHotkeyState.Pause;
}
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute))
{
state |= KeyboardHotkeyState.ToggleMute;
}
return state;
}
}

View file

@ -5,15 +5,17 @@ namespace Ryujinx.Ui
public class StatusUpdatedEventArgs : EventArgs
{
public bool VSyncEnabled;
public float Volume;
public string DockedMode;
public string AspectRatio;
public string GameStatus;
public string FifoStatus;
public string GpuName;
public StatusUpdatedEventArgs(bool vSyncEnabled, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName)
{
VSyncEnabled = vSyncEnabled;
Volume = volume;
DockedMode = dockedMode;
AspectRatio = aspectRatio;
GameStatus = gameStatus;

View file

@ -30,7 +30,8 @@ namespace Ryujinx.Ui.Windows
private readonly TimeZoneContentManager _timeZoneContentManager;
private readonly HashSet<string> _validTzRegions;
private long _systemTimeOffset;
private long _systemTimeOffset;
private float _previousVolumeLevel;
#pragma warning disable CS0649, IDE0044
[GUI] CheckButton _errorLogToggle;
@ -65,6 +66,8 @@ namespace Ryujinx.Ui.Windows
[GUI] EntryCompletion _systemTimeZoneCompletion;
[GUI] Box _audioBackendBox;
[GUI] ComboBox _audioBackendSelect;
[GUI] Label _audioVolumeLabel;
[GUI] Scale _audioVolumeSlider;
[GUI] SpinButton _systemTimeYearSpin;
[GUI] SpinButton _systemTimeMonthSpin;
[GUI] SpinButton _systemTimeDaySpin;
@ -364,6 +367,20 @@ namespace Ryujinx.Ui.Windows
_audioBackendBox.Add(_audioBackendSelect);
_audioBackendSelect.Show();
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume;
_audioVolumeLabel = new Label("Volume: ");
_audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1);
_audioVolumeLabel.MarginStart = 10;
_audioVolumeSlider.ValuePos = PositionType.Right;
_audioVolumeSlider.WidthRequest = 200;
_audioVolumeSlider.Value = _previousVolumeLevel * 100;
_audioVolumeSlider.ValueChanged += VolumeSlider_OnChange;
_audioBackendBox.Add(_audioVolumeLabel);
_audioBackendBox.Add(_audioVolumeSlider);
_audioVolumeLabel.Show();
_audioVolumeSlider.Show();
bool openAlIsSupported = false;
bool soundIoIsSupported = false;
bool sdl2IsSupported = false;
@ -498,6 +515,9 @@ namespace Ryujinx.Ui.Windows
ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading;
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom;
ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f;
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value;
if (_audioBackendSelect.GetActiveIter(out TreeIter activeIter))
{
@ -651,6 +671,11 @@ namespace Ryujinx.Ui.Windows
controllerWindow.Show();
}
private void VolumeSlider_OnChange(object sender, EventArgs args)
{
ConfigurationState.Instance.System.AudioVolume.Value = (float)(_audioVolumeSlider.Value / 100);
}
private void SaveToggle_Activated(object sender, EventArgs args)
{
SaveSettings();
@ -664,6 +689,7 @@ namespace Ryujinx.Ui.Windows
private void CloseToggle_Activated(object sender, EventArgs args)
{
ConfigurationState.Instance.System.AudioVolume.Value = _previousVolumeLevel;
Dispose();
}
}