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:
parent
e7c2dc8ec3
commit
cb43cc7e32
35 changed files with 411 additions and 94 deletions
|
@ -52,7 +52,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
throw new ArgumentException($"{channelCount}");
|
throw new ArgumentException($"{channelCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
OpenALHardwareDeviceSession session = new OpenALHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
|
|
||||||
private object _lock = new object();
|
private object _lock = new object();
|
||||||
|
|
||||||
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_queuedBuffers = new Queue<OpenALAudioBuffer>();
|
_queuedBuffers = new Queue<OpenALAudioBuffer>();
|
||||||
|
@ -27,6 +27,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
||||||
_targetFormat = GetALFormat();
|
_targetFormat = GetALFormat();
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
_playedSampleCount = 0;
|
_playedSampleCount = 0;
|
||||||
|
SetVolume(requestedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ALFormat GetALFormat()
|
private ALFormat GetALFormat()
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
return _pauseEvent;
|
return _pauseEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
|
@ -68,7 +68,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SDL2 backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SDL2HardwareDeviceSession session = new SDL2HardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
private float _volume;
|
private float _volume;
|
||||||
private ushort _nativeSampleFormat;
|
private ushort _nativeSampleFormat;
|
||||||
|
|
||||||
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public SDL2HardwareDeviceSession(SDL2HardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
|
||||||
_sampleCount = uint.MaxValue;
|
_sampleCount = uint.MaxValue;
|
||||||
_started = false;
|
_started = false;
|
||||||
_volume = 1.0f;
|
_volume = requestedVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
private void EnsureAudioStreamSetup(AudioBuffer buffer)
|
||||||
|
@ -82,7 +82,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
|
|
||||||
if (frameCount == 0)
|
if (frameCount == 0)
|
||||||
{
|
{
|
||||||
// SDL2 left the responsability to the user to clear the buffer.
|
// SDL2 left the responsibility to the user to clear the buffer.
|
||||||
streamSpan.Fill(0);
|
streamSpan.Fill(0);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -92,11 +92,16 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||||
|
|
||||||
_ringBuffer.Read(samples, 0, samples.Length);
|
_ringBuffer.Read(samples, 0, samples.Length);
|
||||||
|
|
||||||
samples.AsSpan().CopyTo(streamSpan);
|
fixed (byte* p = samples)
|
||||||
streamSpan.Slice(samples.Length).Fill(0);
|
{
|
||||||
|
IntPtr pStreamSrc = (IntPtr)p;
|
||||||
|
|
||||||
// Apply volume to written data
|
// Zero the dest buffer
|
||||||
SDL_MixAudioFormat(stream, stream, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
|
streamSpan.Fill(0);
|
||||||
|
|
||||||
|
// Apply volume to written data
|
||||||
|
SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
|
||||||
|
}
|
||||||
|
|
||||||
ulong sampleCount = GetSampleCount(samples.Length);
|
ulong sampleCount = GetSampleCount(samples.Length);
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
return _pauseEvent;
|
return _pauseEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
|
@ -142,12 +142,14 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
sampleRate = Constants.TargetSampleRate;
|
sampleRate = Constants.TargetSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volume = Math.Clamp(volume, 0, 1);
|
||||||
|
|
||||||
if (direction != Direction.Output)
|
if (direction != Direction.Output)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
throw new NotImplementedException("Input direction is currently not implemented on SoundIO backend!");
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
SoundIoHardwareDeviceSession session = new SoundIoHardwareDeviceSession(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
|
|
||||||
_sessions.TryAdd(session, 0);
|
_sessions.TryAdd(session, 0);
|
||||||
|
|
||||||
|
|
|
@ -19,21 +19,21 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
private ManualResetEvent _updateRequiredEvent;
|
||||||
private int _disposeState;
|
private int _disposeState;
|
||||||
|
|
||||||
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public SoundIoHardwareDeviceSession(SoundIoHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_driver = driver;
|
_driver = driver;
|
||||||
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
|
||||||
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
|
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
|
||||||
_ringBuffer = new DynamicRingBuffer();
|
_ringBuffer = new DynamicRingBuffer();
|
||||||
|
|
||||||
SetupOutputStream();
|
SetupOutputStream(requestedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupOutputStream()
|
private void SetupOutputStream(float requestedVolume)
|
||||||
{
|
{
|
||||||
_outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
|
_outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
|
||||||
_outputStream.WriteCallback += Update;
|
_outputStream.WriteCallback += Update;
|
||||||
|
_outputStream.Volume = requestedVolume;
|
||||||
// TODO: Setup other callbacks (errors, ect).
|
// TODO: Setup other callbacks (errors, ect).
|
||||||
|
|
||||||
_outputStream.Open();
|
_outputStream.Open();
|
||||||
|
|
|
@ -68,7 +68,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
{
|
{
|
||||||
|
@ -80,6 +80,8 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||||
sampleRate = Constants.TargetSampleRate;
|
sampleRate = Constants.TargetSampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volume = Math.Clamp(volume, 0, 1);
|
||||||
|
|
||||||
if (!_realDriver.SupportsDirection(direction))
|
if (!_realDriver.SupportsDirection(direction))
|
||||||
{
|
{
|
||||||
if (direction == Direction.Input)
|
if (direction == Direction.Input)
|
||||||
|
@ -94,7 +96,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||||
|
|
||||||
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
||||||
|
|
||||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount);
|
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount, volume);
|
||||||
|
|
||||||
if (hardwareChannelCount == channelCount)
|
if (hardwareChannelCount == channelCount)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||||
_pauseEvent = new ManualResetEvent(true);
|
_pauseEvent = new ManualResetEvent(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (sampleRate == 0)
|
if (sampleRate == 0)
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||||
|
|
||||||
if (direction == Direction.Output)
|
if (direction == Direction.Output)
|
||||||
{
|
{
|
||||||
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount);
|
return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,9 +30,9 @@ namespace Ryujinx.Audio.Backends.Dummy
|
||||||
|
|
||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
|
|
||||||
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
public DummyHardwareDeviceSessionOutput(IHardwareDeviceDriver manager, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount, float requestedVolume) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
|
||||||
{
|
{
|
||||||
_volume = 1.0f;
|
_volume = requestedVolume;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ namespace Ryujinx.Audio.Common
|
||||||
_bufferAppendedCount = 0;
|
_bufferAppendedCount = 0;
|
||||||
_bufferRegisteredCount = 0;
|
_bufferRegisteredCount = 0;
|
||||||
_bufferReleasedCount = 0;
|
_bufferReleasedCount = 0;
|
||||||
_volume = 1.0f;
|
_volume = deviceSession.GetVolume();
|
||||||
_state = AudioDeviceState.Stopped;
|
_state = AudioDeviceState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ namespace Ryujinx.Audio.Integration
|
||||||
|
|
||||||
private byte[] _buffer;
|
private byte[] _buffer;
|
||||||
|
|
||||||
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate)
|
public HardwareDeviceImpl(IHardwareDeviceDriver deviceDriver, uint channelCount, uint sampleRate, float volume)
|
||||||
{
|
{
|
||||||
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount);
|
_session = deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, null, SampleFormat.PcmInt16, sampleRate, channelCount, volume);
|
||||||
_channelCount = channelCount;
|
_channelCount = channelCount;
|
||||||
_sampleRate = sampleRate;
|
_sampleRate = sampleRate;
|
||||||
_currentBufferTag = 0;
|
_currentBufferTag = 0;
|
||||||
|
@ -56,6 +56,16 @@ namespace Ryujinx.Audio.Integration
|
||||||
_currentBufferTag = _currentBufferTag % 4;
|
_currentBufferTag = _currentBufferTag % 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
_session.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
return _session.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
public uint GetChannelCount()
|
public uint GetChannelCount()
|
||||||
{
|
{
|
||||||
return _channelCount;
|
return _channelCount;
|
||||||
|
|
|
@ -25,6 +25,18 @@ namespace Ryujinx.Audio.Integration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IHardwareDevice : IDisposable
|
public interface IHardwareDevice : IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the volume level for this device.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume">The volume level to set.</param>
|
||||||
|
void SetVolume(float volume);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the volume level for this device.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The volume level of this device.</returns>
|
||||||
|
float GetVolume();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the supported sample rate of this device.
|
/// Get the supported sample rate of this device.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace Ryujinx.Audio.Integration
|
||||||
Output
|
Output
|
||||||
}
|
}
|
||||||
|
|
||||||
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount);
|
IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume = 1f);
|
||||||
|
|
||||||
ManualResetEvent GetUpdateRequiredEvent();
|
ManualResetEvent GetUpdateRequiredEvent();
|
||||||
ManualResetEvent GetPauseEvent();
|
ManualResetEvent GetPauseEvent();
|
||||||
|
|
|
@ -208,13 +208,14 @@ namespace Ryujinx.Audio.Output
|
||||||
SampleFormat sampleFormat,
|
SampleFormat sampleFormat,
|
||||||
ref AudioInputConfiguration parameter,
|
ref AudioInputConfiguration parameter,
|
||||||
ulong appletResourceUserId,
|
ulong appletResourceUserId,
|
||||||
uint processHandle)
|
uint processHandle,
|
||||||
|
float volume)
|
||||||
{
|
{
|
||||||
int sessionId = AcquireSessionId();
|
int sessionId = AcquireSessionId();
|
||||||
|
|
||||||
_sessionsBufferEvents[sessionId].Clear();
|
_sessionsBufferEvents[sessionId].Clear();
|
||||||
|
|
||||||
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount);
|
IHardwareDeviceSession deviceSession = _deviceDriver.OpenDeviceSession(IHardwareDeviceDriver.Direction.Output, memoryManager, sampleFormat, parameter.SampleRate, parameter.ChannelCount, volume);
|
||||||
|
|
||||||
AudioOutputSystem audioOut = new AudioOutputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
AudioOutputSystem audioOut = new AudioOutputSystem(this, _lock, deviceSession, _sessionsBufferEvents[sessionId]);
|
||||||
|
|
||||||
|
@ -247,6 +248,41 @@ namespace Ryujinx.Audio.Output
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the volume for all output devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="volume">The volume to set.</param>
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
if (_sessions != null)
|
||||||
|
{
|
||||||
|
foreach (AudioOutputSystem session in _sessions)
|
||||||
|
{
|
||||||
|
session?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the volume for all output devices.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A float indicating the volume level.</returns>
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (_sessions != null)
|
||||||
|
{
|
||||||
|
foreach (AudioOutputSystem session in _sessions)
|
||||||
|
{
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
return session.GetVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
|
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
|
||||||
|
|
|
@ -78,7 +78,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(IHardwareDeviceDriver deviceDriver)
|
public void Start(IHardwareDeviceDriver deviceDriver, float volume)
|
||||||
{
|
{
|
||||||
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
|
OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax];
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
for (int i = 0; i < OutputDevices.Length; i++)
|
for (int i = 0; i < OutputDevices.Length; i++)
|
||||||
{
|
{
|
||||||
// TODO: Don't hardcode sample rate.
|
// TODO: Don't hardcode sample rate.
|
||||||
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate);
|
OutputDevices[i] = new HardwareDeviceImpl(deviceDriver, channelCount, Constants.TargetSampleRate, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
_mailbox = new Mailbox<MailboxMessage>();
|
_mailbox = new Mailbox<MailboxMessage>();
|
||||||
|
@ -245,6 +245,33 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
_mailbox.SendResponse(MailboxMessage.Stop);
|
_mailbox.SendResponse(MailboxMessage.Stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (OutputDevices != null)
|
||||||
|
{
|
||||||
|
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||||
|
{
|
||||||
|
if (outputDevice != null)
|
||||||
|
{
|
||||||
|
return outputDevice.GetVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
if (OutputDevices != null)
|
||||||
|
{
|
||||||
|
foreach (IHardwareDevice outputDevice in OutputDevices)
|
||||||
|
{
|
||||||
|
outputDevice?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
|
@ -186,12 +186,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
/// Start the <see cref="AudioProcessor"/> and worker thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void StartLocked()
|
private void StartLocked(float volume)
|
||||||
{
|
{
|
||||||
_isRunning = true;
|
_isRunning = true;
|
||||||
|
|
||||||
// TODO: virtual device mapping (IAudioDevice)
|
// TODO: virtual device mapping (IAudioDevice)
|
||||||
Processor.Start(_deviceDriver);
|
Processor.Start(_deviceDriver, volume);
|
||||||
|
|
||||||
_workerThread = new Thread(SendCommands)
|
_workerThread = new Thread(SendCommands)
|
||||||
{
|
{
|
||||||
|
@ -263,7 +263,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// Register a new <see cref="AudioRenderSystem"/>.
|
/// Register a new <see cref="AudioRenderSystem"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
|
||||||
private void Register(AudioRenderSystem renderer)
|
private void Register(AudioRenderSystem renderer, float volume)
|
||||||
{
|
{
|
||||||
lock (_sessionLock)
|
lock (_sessionLock)
|
||||||
{
|
{
|
||||||
|
@ -274,7 +274,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
if (!_isRunning)
|
if (!_isRunning)
|
||||||
{
|
{
|
||||||
StartLocked();
|
StartLocked(volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
/// <param name="workBufferSize">The guest work buffer size.</param>
|
/// <param name="workBufferSize">The guest work buffer size.</param>
|
||||||
/// <param name="processHandle">The process handle of the application.</param>
|
/// <param name="processHandle">The process handle of the application.</param>
|
||||||
/// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
|
/// <returns>A <see cref="ResultCode"/> reporting an error or a success.</returns>
|
||||||
public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, IVirtualMemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle)
|
public ResultCode OpenAudioRenderer(out AudioRenderSystem renderer, IVirtualMemoryManager memoryManager, ref AudioRendererConfiguration parameter, ulong appletResourceUserId, ulong workBufferAddress, ulong workBufferSize, uint processHandle, float volume)
|
||||||
{
|
{
|
||||||
int sessionId = AcquireSessionId();
|
int sessionId = AcquireSessionId();
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
{
|
{
|
||||||
renderer = audioRenderer;
|
renderer = audioRenderer;
|
||||||
|
|
||||||
Register(renderer);
|
Register(renderer, volume);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -338,6 +338,21 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
if (Processor != null)
|
||||||
|
{
|
||||||
|
return Processor.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
Processor?.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
|
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
|
||||||
|
|
|
@ -76,6 +76,17 @@ namespace Ryujinx.Audio.Renderer.Utils
|
||||||
_stream.Flush();
|
_stream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
// Do nothing, volume is not used for FileHardwareDevice at the moment.
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
// FileHardwareDevice does not incorporate volume.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public uint GetChannelCount()
|
public uint GetChannelCount()
|
||||||
{
|
{
|
||||||
return _channelCount;
|
return _channelCount;
|
||||||
|
|
|
@ -37,6 +37,17 @@ namespace Ryujinx.Audio.Renderer.Utils
|
||||||
_secondaryDevice?.AppendBuffer(data, channelCount);
|
_secondaryDevice?.AppendBuffer(data, channelCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
_baseDevice.SetVolume(volume);
|
||||||
|
_secondaryDevice.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
return _baseDevice.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
public uint GetChannelCount()
|
public uint GetChannelCount()
|
||||||
{
|
{
|
||||||
return _baseDevice.GetChannelCount();
|
return _baseDevice.GetChannelCount();
|
||||||
|
|
|
@ -6,5 +6,6 @@
|
||||||
public Key Screenshot { get; set; }
|
public Key Screenshot { get; set; }
|
||||||
public Key ShowUi { get; set; }
|
public Key ShowUi { get; set; }
|
||||||
public Key Pause { get; set; }
|
public Key Pause { get; set; }
|
||||||
|
public Key ToggleMute { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,11 @@ namespace Ryujinx.HLE
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AspectRatio AspectRatio { get; set; }
|
public AspectRatio AspectRatio { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The audio volume level.
|
||||||
|
/// </summary>
|
||||||
|
public float AudioVolume { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An action called when HLE force a refresh of output after docked mode changed.
|
/// An action called when HLE force a refresh of output after docked mode changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -164,7 +169,8 @@ namespace Ryujinx.HLE
|
||||||
string timeZone,
|
string timeZone,
|
||||||
MemoryManagerMode memoryManagerMode,
|
MemoryManagerMode memoryManagerMode,
|
||||||
bool ignoreMissingServices,
|
bool ignoreMissingServices,
|
||||||
AspectRatio aspectRatio)
|
AspectRatio aspectRatio,
|
||||||
|
float audioVolume)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
VirtualFileSystem = virtualFileSystem;
|
||||||
LibHacHorizonManager = libHacHorizonManager;
|
LibHacHorizonManager = libHacHorizonManager;
|
||||||
|
@ -187,6 +193,7 @@ namespace Ryujinx.HLE
|
||||||
MemoryManagerMode = memoryManagerMode;
|
MemoryManagerMode = memoryManagerMode;
|
||||||
IgnoreMissingServices = ignoreMissingServices;
|
IgnoreMissingServices = ignoreMissingServices;
|
||||||
AspectRatio = aspectRatio;
|
AspectRatio = aspectRatio;
|
||||||
|
AudioVolume = audioVolume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -243,6 +243,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
AudioOutputManager = new AudioOutputManager();
|
AudioOutputManager = new AudioOutputManager();
|
||||||
AudioInputManager = new AudioInputManager();
|
AudioInputManager = new AudioInputManager();
|
||||||
AudioRendererManager = new AudioRendererManager();
|
AudioRendererManager = new AudioRendererManager();
|
||||||
|
AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
|
||||||
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
|
||||||
|
|
||||||
IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
|
IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
|
||||||
|
@ -255,6 +256,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
|
AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
|
||||||
|
AudioOutputManager.SetVolume(Device.Configuration.AudioVolume);
|
||||||
|
|
||||||
IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
|
IWritableEvent[] audioInputRegisterBufferEvents = new IWritableEvent[Constants.AudioInSessionCountMax];
|
||||||
|
|
||||||
|
@ -326,6 +328,17 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
AudioOutputManager.SetVolume(volume);
|
||||||
|
AudioRendererManager.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
return AudioOutputManager.GetVolume() == 0 ? AudioRendererManager.GetVolume() : AudioOutputManager.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
public void ReturnFocus()
|
public void ReturnFocus()
|
||||||
{
|
{
|
||||||
AppletState.SetFocus(true);
|
AppletState.SetFocus(true);
|
||||||
|
|
|
@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
return _impl.ListAudioOuts();
|
return _impl.ListAudioOuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle)
|
public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle, float volume)
|
||||||
{
|
{
|
||||||
var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
|
var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
|
||||||
|
|
||||||
ResultCode result = (ResultCode)_impl.OpenAudioOut(out outputDeviceName, out outputConfiguration, out AudioOutputSystem outSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle);
|
ResultCode result = (ResultCode)_impl.OpenAudioOut(out outputDeviceName, out outputConfiguration, out AudioOutputSystem outSystem, memoryManager, inputDeviceName, SampleFormat.PcmInt16, ref parameter, appletResourceUserId, processHandle, volume);
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
|
|
||||||
string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
|
string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
|
||||||
|
|
||||||
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
|
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle, context.Device.Configuration.AudioVolume);
|
||||||
|
|
||||||
if (resultCode == ResultCode.Success)
|
if (resultCode == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
|
|
||||||
string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
|
string inputDeviceName = MemoryHelper.ReadAsciiString(context.Memory, deviceNameInputPosition, (long)deviceNameInputSize);
|
||||||
|
|
||||||
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle);
|
ResultCode resultCode = _impl.OpenAudioOut(context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, inputDeviceName, ref inputConfiguration, appletResourceUserId, processHandle, context.Device.Configuration.AudioVolume);
|
||||||
|
|
||||||
if (resultCode == ResultCode.Success)
|
if (resultCode == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
{
|
{
|
||||||
var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
|
var memoryManager = context.Process.HandleTable.GetKProcess((int)processHandle).CpuMemory;
|
||||||
|
|
||||||
ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, memoryManager, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle);
|
ResultCode result = (ResultCode)_impl.OpenAudioRenderer(out AudioRenderSystem renderer, memoryManager, ref parameter, appletResourceUserId, workBufferTransferMemory.Address, workBufferTransferMemory.Size, processHandle, context.Device.Configuration.AudioVolume);
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
if (result == ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
||||||
{
|
{
|
||||||
public string[] ListAudioOuts();
|
public string[] ListAudioOuts();
|
||||||
|
|
||||||
public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle);
|
public ResultCode OpenAudioOut(ServiceCtx context, out string outputDeviceName, out AudioOutputConfiguration outputConfiguration, out IAudioOut obj, string inputDeviceName, ref AudioInputConfiguration parameter, ulong appletResourceUserId, uint processHandle, float volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,21 @@ namespace Ryujinx.HLE
|
||||||
Gpu.Window.Present(swapBuffersCallback);
|
Gpu.Window.Present(swapBuffersCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float volume)
|
||||||
|
{
|
||||||
|
System.SetVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
return System.GetVolume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAudioMuted()
|
||||||
|
{
|
||||||
|
return System.GetVolume() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void DisposeGpu()
|
public void DisposeGpu()
|
||||||
{
|
{
|
||||||
Gpu.Dispose();
|
Gpu.Dispose();
|
||||||
|
|
|
@ -109,6 +109,9 @@ namespace Ryujinx.Headless.SDL2
|
||||||
[Option("memory-manager-mode", Required = false, Default = MemoryManagerMode.HostMappedUnsafe, HelpText = "The selected memory manager mode.")]
|
[Option("memory-manager-mode", Required = false, Default = MemoryManagerMode.HostMappedUnsafe, HelpText = "The selected memory manager mode.")]
|
||||||
public MemoryManagerMode MemoryManagerMode { get; set; }
|
public MemoryManagerMode MemoryManagerMode { get; set; }
|
||||||
|
|
||||||
|
[Option("audio-volume", Required = false, Default = 1.0f, HelpText ="The audio level (0 to 1).")]
|
||||||
|
public float AudioVolume { get; set; }
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
|
|
||||||
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")]
|
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")]
|
||||||
|
|
|
@ -465,7 +465,8 @@ namespace Ryujinx.Headless.SDL2
|
||||||
options.SystemTimeZone,
|
options.SystemTimeZone,
|
||||||
options.MemoryManagerMode,
|
options.MemoryManagerMode,
|
||||||
(bool)options.IgnoreMissingServices,
|
(bool)options.IgnoreMissingServices,
|
||||||
options.AspectRatio);
|
options.AspectRatio,
|
||||||
|
options.AudioVolume);
|
||||||
|
|
||||||
return new Switch(configuration);
|
return new Switch(configuration);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 32;
|
public const int CurrentVersion = 33;
|
||||||
|
|
||||||
public int Version { get; set; }
|
public int Version { get; set; }
|
||||||
|
|
||||||
|
@ -179,6 +179,11 @@ namespace Ryujinx.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AudioBackend AudioBackend { get; set; }
|
public AudioBackend AudioBackend { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The audio volume
|
||||||
|
/// </summary>
|
||||||
|
public float AudioVolume { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The selected memory manager mode
|
/// The selected memory manager mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -220,6 +220,11 @@ namespace Ryujinx.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<AudioBackend> AudioBackend { get; private set; }
|
public ReactiveObject<AudioBackend> AudioBackend { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The audio backend volume
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<float> AudioVolume { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The selected memory manager mode
|
/// The selected memory manager mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -257,6 +262,8 @@ namespace Ryujinx.Configuration
|
||||||
ExpandRam.Event += static (sender, e) => LogValueChange(sender, e, nameof(ExpandRam));
|
ExpandRam.Event += static (sender, e) => LogValueChange(sender, e, nameof(ExpandRam));
|
||||||
IgnoreMissingServices = new ReactiveObject<bool>();
|
IgnoreMissingServices = new ReactiveObject<bool>();
|
||||||
IgnoreMissingServices.Event += static (sender, e) => LogValueChange(sender, e, nameof(IgnoreMissingServices));
|
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,
|
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
|
||||||
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
|
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
|
||||||
AudioBackend = System.AudioBackend,
|
AudioBackend = System.AudioBackend,
|
||||||
|
AudioVolume = System.AudioVolume,
|
||||||
MemoryManagerMode = System.MemoryManagerMode,
|
MemoryManagerMode = System.MemoryManagerMode,
|
||||||
ExpandRam = System.ExpandRam,
|
ExpandRam = System.ExpandRam,
|
||||||
IgnoreMissingServices = System.IgnoreMissingServices,
|
IgnoreMissingServices = System.IgnoreMissingServices,
|
||||||
|
@ -553,6 +561,7 @@ namespace Ryujinx.Configuration
|
||||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.Tab,
|
ToggleVsync = Key.Tab,
|
||||||
|
ToggleMute = Key.F2,
|
||||||
Screenshot = Key.F8,
|
Screenshot = Key.F8,
|
||||||
ShowUi = Key.F4,
|
ShowUi = Key.F4,
|
||||||
Pause = Key.F5
|
Pause = Key.F5
|
||||||
|
@ -929,6 +938,24 @@ namespace Ryujinx.Configuration
|
||||||
configurationFileUpdated = true;
|
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;
|
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||||
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
||||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||||
|
@ -960,6 +987,7 @@ namespace Ryujinx.Configuration
|
||||||
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
|
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
|
||||||
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
|
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
|
||||||
System.AudioBackend.Value = configurationFileFormat.AudioBackend;
|
System.AudioBackend.Value = configurationFileFormat.AudioBackend;
|
||||||
|
System.AudioVolume.Value = configurationFileFormat.AudioVolume;
|
||||||
System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode;
|
System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode;
|
||||||
System.ExpandRam.Value = configurationFileFormat.ExpandRam;
|
System.ExpandRam.Value = configurationFileFormat.ExpandRam;
|
||||||
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
|
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
@ -132,6 +132,7 @@ namespace Ryujinx.Ui
|
||||||
[GUI] ProgressBar _progressBar;
|
[GUI] ProgressBar _progressBar;
|
||||||
[GUI] Box _viewBox;
|
[GUI] Box _viewBox;
|
||||||
[GUI] Label _vSyncStatus;
|
[GUI] Label _vSyncStatus;
|
||||||
|
[GUI] Label _volumeStatus;
|
||||||
[GUI] Box _listStatusBox;
|
[GUI] Box _listStatusBox;
|
||||||
[GUI] Label _loadingStatusLabel;
|
[GUI] Label _loadingStatusLabel;
|
||||||
[GUI] ProgressBar _loadingStatusBar;
|
[GUI] ProgressBar _loadingStatusBar;
|
||||||
|
@ -205,6 +206,7 @@ namespace Ryujinx.Ui
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
|
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||||
|
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||||
|
|
||||||
if (ConfigurationState.Instance.Ui.StartFullscreen)
|
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)
|
private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
|
||||||
{
|
{
|
||||||
_fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen";
|
_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.TimeZone,
|
||||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio);
|
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||||
|
ConfigurationState.Instance.System.AudioVolume);
|
||||||
|
|
||||||
_emulationContext = new HLE.Switch(configuration);
|
_emulationContext = new HLE.Switch(configuration);
|
||||||
}
|
}
|
||||||
|
@ -1108,11 +1116,12 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
Application.Invoke(delegate
|
Application.Invoke(delegate
|
||||||
{
|
{
|
||||||
_gameStatus.Text = args.GameStatus;
|
_gameStatus.Text = args.GameStatus;
|
||||||
_fifoStatus.Text = args.FifoStatus;
|
_fifoStatus.Text = args.FifoStatus;
|
||||||
_gpuName.Text = args.GpuName;
|
_gpuName.Text = args.GpuName;
|
||||||
_dockedMode.Text = args.DockedMode;
|
_dockedMode.Text = args.DockedMode;
|
||||||
_aspectRatio.Text = args.AspectRatio;
|
_aspectRatio.Text = args.AspectRatio;
|
||||||
|
_volumeStatus.Text = GetVolumeLabelText(args.Volume);
|
||||||
|
|
||||||
if (args.VSyncEnabled)
|
if (args.VSyncEnabled)
|
||||||
{
|
{
|
||||||
|
@ -1173,6 +1182,28 @@ namespace Ryujinx.Ui
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
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)
|
private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args)
|
||||||
{
|
{
|
||||||
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
|
||||||
|
|
|
@ -294,35 +294,35 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="_pauseEmulation">
|
<object class="GtkMenuItem" id="_pauseEmulation">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Pause emulation</property>
|
<property name="tooltip_text" translatable="yes">Pause emulation</property>
|
||||||
<property name="label" translatable="yes">Pause Emulation</property>
|
<property name="label" translatable="yes">Pause Emulation</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
|
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="_resumeEmulation">
|
<object class="GtkMenuItem" id="_resumeEmulation">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Resume emulation</property>
|
<property name="tooltip_text" translatable="yes">Resume emulation</property>
|
||||||
<property name="label" translatable="yes">Resume Emulation</property>
|
<property name="label" translatable="yes">Resume Emulation</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
|
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="_stopEmulation">
|
<object class="GtkMenuItem" id="_stopEmulation">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</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="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="label" translatable="yes">Stop Emulation</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
|
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem">
|
<object class="GtkSeparatorMenuItem">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -647,14 +647,15 @@
|
||||||
<object class="GtkEventBox">
|
<object class="GtkEventBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</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>
|
<child>
|
||||||
<object class="GtkLabel" id="_aspectRatio">
|
<object class="GtkLabel" id="_volumeStatus">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="margin_left">5</property>
|
<property name="margin_left">5</property>
|
||||||
<property name="margin_right">5</property>
|
<property name="margin_right">5</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@ -676,12 +677,19 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="_gameStatus">
|
<object class="GtkEventBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="halign">start</property>
|
<signal name="button_release_event" handler="AspectRatio_Clicked" swapped="no"/>
|
||||||
<property name="margin_left">5</property>
|
<child>
|
||||||
<property name="margin_right">5</property>
|
<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>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
@ -701,7 +709,7 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="_fifoStatus">
|
<object class="GtkLabel" id="_gameStatus">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
|
@ -725,6 +733,31 @@
|
||||||
<property name="position">9</property>
|
<property name="position">9</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</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>
|
<child>
|
||||||
<object class="GtkLabel" id="_gpuName">
|
<object class="GtkLabel" id="_gpuName">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -736,7 +769,7 @@
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">10</property>
|
<property name="position">12</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
@ -425,6 +425,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||||
Device.EnableDeviceVsync,
|
Device.EnableDeviceVsync,
|
||||||
|
Device.GetVolume(),
|
||||||
dockedMode,
|
dockedMode,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||||
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||||
|
@ -598,6 +599,19 @@ namespace Ryujinx.Ui
|
||||||
(Toplevel as MainWindow)?.TogglePause();
|
(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;
|
_prevHotkeyState = currentHotkeyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +641,8 @@ namespace Ryujinx.Ui
|
||||||
ToggleVSync = 1 << 0,
|
ToggleVSync = 1 << 0,
|
||||||
Screenshot = 1 << 1,
|
Screenshot = 1 << 1,
|
||||||
ShowUi = 1 << 2,
|
ShowUi = 1 << 2,
|
||||||
Pause = 1 << 3
|
Pause = 1 << 3,
|
||||||
|
ToggleMute = 1 << 4
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeyboardHotkeyState GetHotkeyState()
|
private KeyboardHotkeyState GetHotkeyState()
|
||||||
|
@ -654,6 +669,11 @@ namespace Ryujinx.Ui
|
||||||
state |= KeyboardHotkeyState.Pause;
|
state |= KeyboardHotkeyState.Pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute))
|
||||||
|
{
|
||||||
|
state |= KeyboardHotkeyState.ToggleMute;
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,17 @@ namespace Ryujinx.Ui
|
||||||
public class StatusUpdatedEventArgs : EventArgs
|
public class StatusUpdatedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public bool VSyncEnabled;
|
public bool VSyncEnabled;
|
||||||
|
public float Volume;
|
||||||
public string DockedMode;
|
public string DockedMode;
|
||||||
public string AspectRatio;
|
public string AspectRatio;
|
||||||
public string GameStatus;
|
public string GameStatus;
|
||||||
public string FifoStatus;
|
public string FifoStatus;
|
||||||
public string GpuName;
|
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;
|
VSyncEnabled = vSyncEnabled;
|
||||||
|
Volume = volume;
|
||||||
DockedMode = dockedMode;
|
DockedMode = dockedMode;
|
||||||
AspectRatio = aspectRatio;
|
AspectRatio = aspectRatio;
|
||||||
GameStatus = gameStatus;
|
GameStatus = gameStatus;
|
||||||
|
|
|
@ -30,7 +30,8 @@ namespace Ryujinx.Ui.Windows
|
||||||
private readonly TimeZoneContentManager _timeZoneContentManager;
|
private readonly TimeZoneContentManager _timeZoneContentManager;
|
||||||
private readonly HashSet<string> _validTzRegions;
|
private readonly HashSet<string> _validTzRegions;
|
||||||
|
|
||||||
private long _systemTimeOffset;
|
private long _systemTimeOffset;
|
||||||
|
private float _previousVolumeLevel;
|
||||||
|
|
||||||
#pragma warning disable CS0649, IDE0044
|
#pragma warning disable CS0649, IDE0044
|
||||||
[GUI] CheckButton _errorLogToggle;
|
[GUI] CheckButton _errorLogToggle;
|
||||||
|
@ -65,6 +66,8 @@ namespace Ryujinx.Ui.Windows
|
||||||
[GUI] EntryCompletion _systemTimeZoneCompletion;
|
[GUI] EntryCompletion _systemTimeZoneCompletion;
|
||||||
[GUI] Box _audioBackendBox;
|
[GUI] Box _audioBackendBox;
|
||||||
[GUI] ComboBox _audioBackendSelect;
|
[GUI] ComboBox _audioBackendSelect;
|
||||||
|
[GUI] Label _audioVolumeLabel;
|
||||||
|
[GUI] Scale _audioVolumeSlider;
|
||||||
[GUI] SpinButton _systemTimeYearSpin;
|
[GUI] SpinButton _systemTimeYearSpin;
|
||||||
[GUI] SpinButton _systemTimeMonthSpin;
|
[GUI] SpinButton _systemTimeMonthSpin;
|
||||||
[GUI] SpinButton _systemTimeDaySpin;
|
[GUI] SpinButton _systemTimeDaySpin;
|
||||||
|
@ -364,6 +367,20 @@ namespace Ryujinx.Ui.Windows
|
||||||
_audioBackendBox.Add(_audioBackendSelect);
|
_audioBackendBox.Add(_audioBackendSelect);
|
||||||
_audioBackendSelect.Show();
|
_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 openAlIsSupported = false;
|
||||||
bool soundIoIsSupported = false;
|
bool soundIoIsSupported = false;
|
||||||
bool sdl2IsSupported = false;
|
bool sdl2IsSupported = false;
|
||||||
|
@ -498,6 +515,9 @@ namespace Ryujinx.Ui.Windows
|
||||||
ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading;
|
ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading;
|
||||||
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
|
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
|
||||||
ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom;
|
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))
|
if (_audioBackendSelect.GetActiveIter(out TreeIter activeIter))
|
||||||
{
|
{
|
||||||
|
@ -651,6 +671,11 @@ namespace Ryujinx.Ui.Windows
|
||||||
controllerWindow.Show();
|
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)
|
private void SaveToggle_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
|
@ -664,6 +689,7 @@ namespace Ryujinx.Ui.Windows
|
||||||
|
|
||||||
private void CloseToggle_Activated(object sender, EventArgs args)
|
private void CloseToggle_Activated(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
|
ConfigurationState.Instance.System.AudioVolume.Value = _previousVolumeLevel;
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue