mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-23 04:52:08 +00:00
added HD Rumble toggle to UI, simplified rumble loop
This commit is contained in:
parent
8e9c73cd08
commit
1d9c13fa2b
12 changed files with 108 additions and 70 deletions
|
|
@ -12250,6 +12250,56 @@
|
|||
"zh_TW": "弱震動調節:"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "ControllerSettingsRumbleUseHDRumble",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Enable HD Rumble",
|
||||
"es_ES": "Activa vibración HD",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "HDRumbleTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Sends more data to the controller for better rumble.\n\nCurrently only supports first-party Nintendo Switch controllers.\n\nLeave ON if you're using JoyCons or a Pro Controller.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "DialogMessageSaveNotAvailableMessage",
|
||||
"Translations": {
|
||||
|
|
|
|||
|
|
@ -16,5 +16,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
|
|||
/// Enable Rumble
|
||||
/// </summary>
|
||||
public bool EnableRumble { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable HD Rumble support
|
||||
/// </summary
|
||||
public bool UseHDRumble { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Ryujinx.HLE.HOS.Services.Hid;
|
|||
using SDL;
|
||||
using static SDL.SDL3;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Input.SDL3
|
||||
{
|
||||
|
|
@ -13,9 +12,8 @@ namespace Ryujinx.Input.SDL3
|
|||
public unsafe class NpadHdRumble : IDisposable
|
||||
{
|
||||
private readonly SDL_hid_device* _hidHandle;
|
||||
|
||||
|
||||
private int _globalCount;
|
||||
private byte[] _lastRumbleData = new byte[10];
|
||||
private ulong _lastWriteTicks;
|
||||
|
||||
private NpadHdRumble(SDL_hid_device* hidHandle)
|
||||
|
|
@ -69,18 +67,18 @@ namespace Ryujinx.Input.SDL3
|
|||
|
||||
fixed (byte* ptr = buf)
|
||||
{
|
||||
if (SendHDRumble(ptr, (nuint)buf.Length) < 0)
|
||||
if (SendHDRumble(ptr, (nuint)buf.Length) >= 0)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(SDL_GetError()))
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
|
||||
SDL_ClearError();
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(SDL_GetError()))
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Hid, SDL_GetError());
|
||||
SDL_ClearError();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int EncodeLowFreq(float lowFreq)
|
||||
|
|
@ -155,67 +153,31 @@ namespace Ryujinx.Input.SDL3
|
|||
EncodeHighAmp(right.AmplitudeHigh));
|
||||
}
|
||||
|
||||
public int SendHDRumble(byte* data, nuint length)
|
||||
private int SendHDRumble(byte* data, nuint length)
|
||||
{
|
||||
int result = 0;
|
||||
ulong currentTicks = SDL_GetTicks();
|
||||
|
||||
|
||||
// Ditch rumble if we haven't hit the poll-rate yet.
|
||||
// TODO: figure out a better way to do this
|
||||
// while the polling check makes the rumble accurate, it also causes it to miss signals.
|
||||
// While the polling check makes the rumble accurate, it also causes it to miss signals.
|
||||
if ((currentTicks - _lastWriteTicks) < 8) // https://docs.handheldlegend.com/s/progcc-3/doc/lag-comparison-aAR1mV3JLX
|
||||
{
|
||||
result = 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool match = true;
|
||||
int totalFreq = 0;
|
||||
int totalAmp = 0;
|
||||
|
||||
byte* head = data;
|
||||
for (int i = 0; i < (int) length; i++)
|
||||
{
|
||||
if (*data != _lastRumbleData[i])
|
||||
{
|
||||
match = false;
|
||||
}
|
||||
|
||||
if (i < 2)
|
||||
{
|
||||
data++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mario Kart 8 Deluxe sends rumble packets where the amplitude is zero, but the frequency isn't.
|
||||
// It's likely that the hardware accounts for this, but on the off-chance it doesn't, we did.
|
||||
if (i == 2 || i == 4 || i == 6 || i == 8) // frequency
|
||||
{
|
||||
totalFreq += *data;
|
||||
}
|
||||
else if (i == 3 || i == 5 || i == 7 || i == 9) // amplitude
|
||||
{
|
||||
totalAmp += *data;
|
||||
}
|
||||
|
||||
data++;
|
||||
}
|
||||
data = head;
|
||||
|
||||
if (!match || (totalFreq == 0 || totalAmp == 0))
|
||||
SDL_LockJoysticks();
|
||||
{
|
||||
// Fun fact: Mario Kart 8 Deluxe sends rumble packets
|
||||
// where the amplitude is zero, but the frequency isn't.
|
||||
result = SDL_hid_write(_hidHandle, data, length);
|
||||
if (result >= 0)
|
||||
{
|
||||
_lastWriteTicks = currentTicks;
|
||||
for (int i = 0; i < (int)length; i++)
|
||||
{
|
||||
_lastRumbleData[i] = *data;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockJoysticks();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -562,28 +562,29 @@ namespace Ryujinx.Input.HLE
|
|||
|
||||
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
||||
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
||||
|
||||
|
||||
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
|
||||
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
|
||||
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
|
||||
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
|
||||
|
||||
if (_gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
|
||||
if (!controllerConfig.Rumble.UseHDRumble || _gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
|
||||
{
|
||||
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
|
||||
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
|
||||
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15)));
|
||||
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85)));
|
||||
_gamepad?.Rumble(low, high, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
|
||||
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
|
||||
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
|
||||
$"L.low.freq={leftVibrationValue.FrequencyLow}, " +
|
||||
$"L.high.freq={leftVibrationValue.FrequencyHigh}, " +
|
||||
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
|
||||
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
|
||||
$"R.low.freq={rightVibrationValue.FrequencyLow}, " +
|
||||
$"R.high.freq={rightVibrationValue.FrequencyHigh}");
|
||||
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
|
||||
// Value=value/multiplier * multiplier (result)
|
||||
$"L.low.amp={leftVibrationValue.AmplitudeLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeLow}), " +
|
||||
$"L.high.amp={leftVibrationValue.AmplitudeHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.AmplitudeHigh}), " +
|
||||
$"L.low.freq={leftVibrationValue.FrequencyLow / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyLow}), " +
|
||||
$"L.high.freq={leftVibrationValue.FrequencyHigh / controllerConfig.Rumble.WeakRumble} * {controllerConfig.Rumble.WeakRumble} ({leftVibrationValue.FrequencyHigh}), " +
|
||||
$"R.low.amp={rightVibrationValue.AmplitudeLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeLow}), " +
|
||||
$"R.high.amp={rightVibrationValue.AmplitudeHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.AmplitudeHigh}), " +
|
||||
$"R.low.freq={rightVibrationValue.FrequencyLow / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyLow}), " +
|
||||
$"R.high.freq={rightVibrationValue.FrequencyHigh / controllerConfig.Rumble.StrongRumble} * {controllerConfig.Rumble.StrongRumble} ({rightVibrationValue.FrequencyHigh})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ namespace Ryujinx.Headless
|
|||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
EnableRumble = false,
|
||||
UseHDRumble = true
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 72;
|
||||
public const int CurrentVersion = 73;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||
EnableRumble = false,
|
||||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
UseHDRumble = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
|
||||
public float WeakRumble { get; set; }
|
||||
public float StrongRumble { get; set; }
|
||||
public bool UseHDRumble { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
|
|
@ -236,6 +237,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
EnableRumble = controllerInput.Rumble.EnableRumble;
|
||||
WeakRumble = controllerInput.Rumble.WeakRumble;
|
||||
StrongRumble = controllerInput.Rumble.StrongRumble;
|
||||
UseHDRumble = controllerInput.Rumble.UseHDRumble;
|
||||
}
|
||||
|
||||
if (controllerInput.Led != null)
|
||||
|
|
@ -307,6 +309,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||
EnableRumble = EnableRumble,
|
||||
WeakRumble = WeakRumble,
|
||||
StrongRumble = StrongRumble,
|
||||
UseHDRumble = UseHDRumble,
|
||||
},
|
||||
Led = new LedConfigController
|
||||
{
|
||||
|
|
|
|||
|
|
@ -789,6 +789,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
StrongRumble = 1f,
|
||||
WeakRumble = 1f,
|
||||
EnableRumble = false,
|
||||
UseHDRumble = true
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,5 +9,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
|
||||
[ObservableProperty]
|
||||
public partial float WeakRumble { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool EnableHDRumble { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,15 @@
|
|||
Margin="5,0"
|
||||
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
|
||||
</StackPanel>
|
||||
<CheckBox
|
||||
Margin="5"
|
||||
IsChecked="{Binding EnableHDRumble}">
|
||||
<TextBlock
|
||||
Margin="0,3,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale ControllerSettingsRumbleUseHDRumble}"
|
||||
ToolTip.Tip="{ext:Locale HDRumbleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||
{
|
||||
StrongRumble = config.StrongRumble,
|
||||
WeakRumble = config.WeakRumble,
|
||||
EnableHDRumble = config.UseHDRumble
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
|
|
@ -45,6 +46,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||
GamepadInputConfig config = viewModel.Config;
|
||||
config.StrongRumble = content.ViewModel.StrongRumble;
|
||||
config.WeakRumble = content.ViewModel.WeakRumble;
|
||||
config.UseHDRumble = content.ViewModel.EnableHDRumble;
|
||||
};
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
|
|
|
|||
Loading…
Reference in a new issue