aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs4
-rw-r--r--Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs3
-rw-r--r--Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs4
-rw-r--r--Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs19
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs6
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs8
-rw-r--r--Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs6
-rw-r--r--Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs4
-rw-r--r--Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs4
-rw-r--r--Ryujinx.Audio/Common/AudioDeviceSession.cs2
-rw-r--r--Ryujinx.Audio/Integration/HardwareDeviceImpl.cs14
-rw-r--r--Ryujinx.Audio/Integration/IHardwareDevice.cs12
-rw-r--r--Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs2
-rw-r--r--Ryujinx.Audio/Output/AudioOutputManager.cs40
-rw-r--r--Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs31
-rw-r--r--Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs27
-rw-r--r--Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs11
-rw-r--r--Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs11
-rw-r--r--Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs1
-rw-r--r--Ryujinx.HLE/HLEConfiguration.cs9
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs13
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs2
-rw-r--r--Ryujinx.HLE/Switch.cs15
-rw-r--r--Ryujinx.Headless.SDL2/Options.cs3
-rw-r--r--Ryujinx.Headless.SDL2/Program.cs3
-rw-r--r--Ryujinx/Configuration/ConfigurationFileFormat.cs7
-rw-r--r--Ryujinx/Configuration/ConfigurationState.cs28
-rw-r--r--Ryujinx/Ui/MainWindow.cs45
-rw-r--r--Ryujinx/Ui/MainWindow.glade105
-rw-r--r--Ryujinx/Ui/RendererWidgetBase.cs22
-rw-r--r--Ryujinx/Ui/StatusUpdatedEventArgs.cs4
-rw-r--r--Ryujinx/Ui/Windows/SettingsWindow.cs28
35 files changed, 410 insertions, 93 deletions
diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
index 453208a1..0c793f24 100644
--- a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
+++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs
@@ -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)
{
@@ -73,7 +73,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
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);
diff --git a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs
index f0c0f498..ac3319e0 100644
--- a/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs
+++ b/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs
@@ -19,7 +19,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
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;
_queuedBuffers = new Queue<OpenALAudioBuffer>();
@@ -27,6 +27,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
_targetFormat = GetALFormat();
_isActive = false;
_playedSampleCount = 0;
+ SetVolume(requestedVolume);
}
private ALFormat GetALFormat()
diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs
index 77545b57..54548c11 100644
--- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs
+++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.Audio.Backends.SDL2
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)
{
@@ -68,7 +68,7 @@ namespace Ryujinx.Audio.Backends.SDL2
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);
diff --git a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs
index 843de01a..33e1632d 100644
--- a/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs
+++ b/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs
@@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Backends.SDL2
private float _volume;
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;
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
@@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Backends.SDL2
_nativeSampleFormat = SDL2HardwareDeviceDriver.GetSDL2Format(RequestedSampleFormat);
_sampleCount = uint.MaxValue;
_started = false;
- _volume = 1.0f;
+ _volume = requestedVolume;
}
private void EnsureAudioStreamSetup(AudioBuffer buffer)
@@ -82,7 +82,7 @@ namespace Ryujinx.Audio.Backends.SDL2
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);
return;
@@ -92,11 +92,16 @@ namespace Ryujinx.Audio.Backends.SDL2
_ringBuffer.Read(samples, 0, samples.Length);
- samples.AsSpan().CopyTo(streamSpan);
- streamSpan.Slice(samples.Length).Fill(0);
+ fixed (byte* p = samples)
+ {
+ IntPtr pStreamSrc = (IntPtr)p;
+
+ // Zero the dest buffer
+ streamSpan.Fill(0);
- // Apply volume to written data
- SDL_MixAudioFormat(stream, stream, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
+ // Apply volume to written data
+ SDL_MixAudioFormat(stream, pStreamSrc, _nativeSampleFormat, (uint)samples.Length, (int)(_volume * SDL_MIX_MAXVOLUME));
+ }
ulong sampleCount = GetSampleCount(samples.Length);
diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs
index cde5b3d4..02a9a228 100644
--- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceDriver.cs
@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
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)
{
@@ -142,12 +142,14 @@ namespace Ryujinx.Audio.Backends.SoundIo
sampleRate = Constants.TargetSampleRate;
}
+ volume = Math.Clamp(volume, 0, 1);
+
if (direction != Direction.Output)
{
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);
diff --git a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs
index ee2eeb77..1e8c814e 100644
--- a/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/SoundIoHardwareDeviceSession.cs
@@ -19,21 +19,21 @@ namespace Ryujinx.Audio.Backends.SoundIo
private ManualResetEvent _updateRequiredEvent;
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;
_updateRequiredEvent = _driver.GetUpdateRequiredEvent();
_queuedBuffers = new ConcurrentQueue<SoundIoAudioBuffer>();
_ringBuffer = new DynamicRingBuffer();
- SetupOutputStream();
+ SetupOutputStream(requestedVolume);
}
- private void SetupOutputStream()
+ private void SetupOutputStream(float requestedVolume)
{
_outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
_outputStream.WriteCallback += Update;
-
+ _outputStream.Volume = requestedVolume;
// TODO: Setup other callbacks (errors, ect).
_outputStream.Open();
diff --git a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs
index 0ae6a620..d9e170c3 100644
--- a/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs
+++ b/Ryujinx.Audio/Backends/CompatLayer/CompatLayerHardwareDeviceDriver.cs
@@ -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)
{
@@ -80,6 +80,8 @@ namespace Ryujinx.Audio.Backends.CompatLayer
sampleRate = Constants.TargetSampleRate;
}
+ volume = Math.Clamp(volume, 0, 1);
+
if (!_realDriver.SupportsDirection(direction))
{
if (direction == Direction.Input)
@@ -94,7 +96,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
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)
{
diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs
index d729d3f6..f9783ee5 100644
--- a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs
+++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceDriver.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.Audio.Backends.Dummy
_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)
{
@@ -49,7 +49,7 @@ namespace Ryujinx.Audio.Backends.Dummy
if (direction == Direction.Output)
{
- return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount);
+ return new DummyHardwareDeviceSessionOutput(this, memoryManager, sampleFormat, sampleRate, channelCount, volume);
}
else
{
diff --git a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs
index 7cc19997..1e6a198a 100644
--- a/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs
+++ b/Ryujinx.Audio/Backends/Dummy/DummyHardwareDeviceSessionOutput.cs
@@ -30,9 +30,9 @@ namespace Ryujinx.Audio.Backends.Dummy
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;
}
diff --git a/Ryujinx.Audio/Common/AudioDeviceSession.cs b/Ryujinx.Audio/Common/AudioDeviceSession.cs
index fbdb5a75..511f3905 100644
--- a/Ryujinx.Audio/Common/AudioDeviceSession.cs
+++ b/Ryujinx.Audio/Common/AudioDeviceSession.cs
@@ -106,7 +106,7 @@ namespace Ryujinx.Audio.Common
_bufferAppendedCount = 0;
_bufferRegisteredCount = 0;
_bufferReleasedCount = 0;
- _volume = 1.0f;
+ _volume = deviceSession.GetVolume();
_state = AudioDeviceState.Stopped;
}
diff --git a/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs b/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs
index d489b008..d51aa3fb 100644
--- a/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs
+++ b/Ryujinx.Audio/Integration/HardwareDeviceImpl.cs
@@ -30,9 +30,9 @@ namespace Ryujinx.Audio.Integration
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;
_sampleRate = sampleRate;
_currentBufferTag = 0;
@@ -56,6 +56,16 @@ namespace Ryujinx.Audio.Integration
_currentBufferTag = _currentBufferTag % 4;
}
+ public void SetVolume(float volume)
+ {
+ _session.SetVolume(volume);
+ }
+
+ public float GetVolume()
+ {
+ return _session.GetVolume();
+ }
+
public uint GetChannelCount()
{
return _channelCount;
diff --git a/Ryujinx.Audio/Integration/IHardwareDevice.cs b/Ryujinx.Audio/Integration/IHardwareDevice.cs
index 0f67b2c8..1eeb5ca4 100644
--- a/Ryujinx.Audio/Integration/IHardwareDevice.cs
+++ b/Ryujinx.Audio/Integration/IHardwareDevice.cs
@@ -26,6 +26,18 @@ namespace Ryujinx.Audio.Integration
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>
/// Get the supported sample rate of this device.
/// </summary>
/// <returns>The supported sample rate of this device.</returns>
diff --git a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs
index 1a53fa9b..c1869ce1 100644
--- a/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs
+++ b/Ryujinx.Audio/Integration/IHardwareDeviceDriver.cs
@@ -33,7 +33,7 @@ namespace Ryujinx.Audio.Integration
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 GetPauseEvent();
diff --git a/Ryujinx.Audio/Output/AudioOutputManager.cs b/Ryujinx.Audio/Output/AudioOutputManager.cs
index 852632fa..dd115295 100644
--- a/Ryujinx.Audio/Output/AudioOutputManager.cs
+++ b/Ryujinx.Audio/Output/AudioOutputManager.cs
@@ -208,13 +208,14 @@ namespace Ryujinx.Audio.Output
SampleFormat sampleFormat,
ref AudioInputConfiguration parameter,
ulong appletResourceUserId,
- uint processHandle)
+ uint processHandle,
+ float volume)
{
int sessionId = AcquireSessionId();
_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]);
@@ -247,6 +248,41 @@ namespace Ryujinx.Audio.Output
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()
{
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
diff --git a/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs
index e15165b9..303de9bb 100644
--- a/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs
+++ b/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs
@@ -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];
@@ -89,7 +89,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
for (int i = 0; i < OutputDevices.Length; i++)
{
// 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>();
@@ -245,6 +245,33 @@ namespace Ryujinx.Audio.Renderer.Dsp
_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()
{
Dispose(true);
diff --git a/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
index 7518c447..d20c3c03 100644
--- a/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
+++ b/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs
@@ -186,12 +186,12 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary>
/// Start the <see cref="AudioProcessor"/> and worker thread.
/// </summary>
- private void StartLocked()
+ private void StartLocked(float volume)
{
_isRunning = true;
// TODO: virtual device mapping (IAudioDevice)
- Processor.Start(_deviceDriver);
+ Processor.Start(_deviceDriver, volume);
_workerThread = new Thread(SendCommands)
{
@@ -263,7 +263,7 @@ namespace Ryujinx.Audio.Renderer.Server
/// Register a new <see cref="AudioRenderSystem"/>.
/// </summary>
/// <param name="renderer">The <see cref="AudioRenderSystem"/> to register.</param>
- private void Register(AudioRenderSystem renderer)
+ private void Register(AudioRenderSystem renderer, float volume)
{
lock (_sessionLock)
{
@@ -274,7 +274,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
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="processHandle">The process handle of the application.</param>
/// <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();
@@ -326,7 +326,7 @@ namespace Ryujinx.Audio.Renderer.Server
{
renderer = audioRenderer;
- Register(renderer);
+ Register(renderer, volume);
}
else
{
@@ -338,6 +338,21 @@ namespace Ryujinx.Audio.Renderer.Server
return result;
}
+ public float GetVolume()
+ {
+ if (Processor != null)
+ {
+ return Processor.GetVolume();
+ }
+
+ return 0f;
+ }
+
+ public void SetVolume(float volume)
+ {
+ Processor?.SetVolume(volume);
+ }
+
public void Dispose()
{
if (Interlocked.CompareExchange(ref _disposeState, 1, 0) == 0)
diff --git a/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs
index 2008bafc..8d717f6a 100644
--- a/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs
+++ b/Ryujinx.Audio/Renderer/Utils/FileHardwareDevice.cs
@@ -76,6 +76,17 @@ namespace Ryujinx.Audio.Renderer.Utils
_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()
{
return _channelCount;
diff --git a/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs b/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs
index c5411ac0..e6be07c0 100644
--- a/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs
+++ b/Ryujinx.Audio/Renderer/Utils/SplitterHardwareDevice.cs
@@ -37,6 +37,17 @@ namespace Ryujinx.Audio.Renderer.Utils
_secondaryDevice?.AppendBuffer(data, channelCount);
}
+ public void SetVolume(float volume)
+ {
+ _baseDevice.SetVolume(volume);
+ _secondaryDevice.SetVolume(volume);
+ }
+
+ public float GetVolume()
+ {
+ return _baseDevice.GetVolume();
+ }
+
public uint GetChannelCount()
{
return _baseDevice.GetChannelCount();
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
index dd0d7c21..d8183abc 100644
--- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -6,5 +6,6 @@
public Key Screenshot { get; set; }
public Key ShowUi { get; set; }
public Key Pause { get; set; }
+ public Key ToggleMute { get; set; }
}
}
diff --git a/Ryujinx.HLE/HLEConfiguration.cs b/Ryujinx.HLE/HLEConfiguration.cs
index 6dfa6a26..bba21435 100644
--- a/Ryujinx.HLE/HLEConfiguration.cs
+++ b/Ryujinx.HLE/HLEConfiguration.cs
@@ -140,6 +140,11 @@ namespace Ryujinx.HLE
public AspectRatio AspectRatio { get; set; }
/// <summary>
+ /// The audio volume level.
+ /// </summary>
+ public float AudioVolume { get; set; }
+
+ /// <summary>
/// An action called when HLE force a refresh of output after docked mode changed.
/// </summary>
public Action RefreshInputConfig { internal get; set; }
@@ -164,7 +169,8 @@ namespace Ryujinx.HLE
string timeZone,
MemoryManagerMode memoryManagerMode,
bool ignoreMissingServices,
- AspectRatio aspectRatio)
+ AspectRatio aspectRatio,
+ float audioVolume)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
@@ -187,6 +193,7 @@ namespace Ryujinx.HLE
MemoryManagerMode = memoryManagerMode;
IgnoreMissingServices = ignoreMissingServices;
AspectRatio = aspectRatio;
+ AudioVolume = audioVolume;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 74bdb647..b21bf3e3 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -243,6 +243,7 @@ namespace Ryujinx.HLE.HOS
AudioOutputManager = new AudioOutputManager();
AudioInputManager = new AudioInputManager();
AudioRendererManager = new AudioRendererManager();
+ AudioRendererManager.SetVolume(Device.Configuration.AudioVolume);
AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry();
IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax];
@@ -255,6 +256,7 @@ namespace Ryujinx.HLE.HOS
}
AudioOutputManager.Initialize(Device.AudioDeviceDriver, audioOutputRegisterBufferEvents);
+ AudioOutputManager.SetVolume(Device.Configuration.AudioVolume);
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()
{
AppletState.SetFocus(true);
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
index 29490553..7b289196 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManager.cs
@@ -20,11 +20,11 @@ namespace Ryujinx.HLE.HOS.Services.Audio
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;
- 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)
{
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
index 3040696e..8a987dba 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioOutManagerServer.cs
@@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
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)
{
@@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
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)
{
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
index 5aff3475..62bd0ba6 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRendererManager.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
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)
{
diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
index 0b164019..70e60d2e 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioOutManager.cs
@@ -7,6 +7,6 @@ namespace Ryujinx.HLE.HOS.Services.Audio
{
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);
}
}
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index eeb0e7b9..29d2ddd4 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -146,6 +146,21 @@ namespace Ryujinx.HLE
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()
{
Gpu.Dispose();
diff --git a/Ryujinx.Headless.SDL2/Options.cs b/Ryujinx.Headless.SDL2/Options.cs
index 179246c9..2c635a59 100644
--- a/Ryujinx.Headless.SDL2/Options.cs
+++ b/Ryujinx.Headless.SDL2/Options.cs
@@ -109,6 +109,9 @@ namespace Ryujinx.Headless.SDL2
[Option("memory-manager-mode", Required = false, Default = MemoryManagerMode.HostMappedUnsafe, HelpText = "The selected memory manager mode.")]
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
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")]
diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs
index 39dbddcd..eddf9351 100644
--- a/Ryujinx.Headless.SDL2/Program.cs
+++ b/Ryujinx.Headless.SDL2/Program.cs
@@ -465,7 +465,8 @@ namespace Ryujinx.Headless.SDL2
options.SystemTimeZone,
options.MemoryManagerMode,
(bool)options.IgnoreMissingServices,
- options.AspectRatio);
+ options.AspectRatio,
+ options.AudioVolume);
return new Switch(configuration);
}
diff --git a/Ryujinx/Configuration/ConfigurationFileFormat.cs b/Ryujinx/Configuration/ConfigurationFileFormat.cs
index dda86ff5..648004d5 100644
--- a/Ryujinx/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx/Configuration/ConfigurationFileFormat.cs
@@ -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; }
@@ -180,6 +180,11 @@ namespace Ryujinx.Configuration
public AudioBackend AudioBackend { get; set; }
/// <summary>
+ /// The audio volume
+ /// </summary>
+ public float AudioVolume { get; set; }
+
+ /// <summary>
/// The selected memory manager mode
/// </summary>
public MemoryManagerMode MemoryManagerMode { get; set; }
diff --git a/Ryujinx/Configuration/ConfigurationState.cs b/Ryujinx/Configuration/ConfigurationState.cs
index 9cf3f650..02136591 100644
--- a/Ryujinx/Configuration/ConfigurationState.cs
+++ b/Ryujinx/Configuration/ConfigurationState.cs
@@ -221,6 +221,11 @@ namespace Ryujinx.Configuration
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>
public ReactiveObject<MemoryManagerMode> MemoryManagerMode { get; private set; }
@@ -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;
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index c399400a..51a09a31 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -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;
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index 66b307fb..a9ab43e1 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -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,6 +677,38 @@
</packing>
</child>
<child>
+ <object class="GtkEventBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ <property name="fill">True</property>
+ <property name="position">6</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">7</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -686,7 +719,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">6</property>
+ <property name="position">8</property>
</packing>
</child>
<child>
@@ -697,7 +730,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">7</property>
+ <property name="position">9</property>
</packing>
</child>
<child>
@@ -711,7 +744,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">8</property>
+ <property name="position">10</property>
</packing>
</child>
<child>
@@ -722,7 +755,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">9</property>
+ <property name="position">11</property>
</packing>
</child>
<child>
@@ -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>
diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs
index c25e2163..8830a4f5 100644
--- a/Ryujinx/Ui/RendererWidgetBase.cs
+++ b/Ryujinx/Ui/RendererWidgetBase.cs
@@ -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;
}
}
diff --git a/Ryujinx/Ui/StatusUpdatedEventArgs.cs b/Ryujinx/Ui/StatusUpdatedEventArgs.cs
index c9fba77b..df83efa4 100644
--- a/Ryujinx/Ui/StatusUpdatedEventArgs.cs
+++ b/Ryujinx/Ui/StatusUpdatedEventArgs.cs
@@ -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;
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index da2301f0..c5aeebda 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -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();
}
}