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
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace Ryujinx.Audio.Common
|
|||
_bufferAppendedCount = 0;
|
||||
_bufferRegisteredCount = 0;
|
||||
_bufferReleasedCount = 0;
|
||||
_volume = 1.0f;
|
||||
_volume = deviceSession.GetVolume();
|
||||
_state = AudioDeviceState.Stopped;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -25,6 +25,18 @@ namespace Ryujinx.Audio.Integration
|
|||
/// </summary>
|
||||
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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue