diff --git a/assets/Locales/Root.json b/assets/Locales/Root.json
index 757ddeec8..d9139904c 100644
--- a/assets/Locales/Root.json
+++ b/assets/Locales/Root.json
@@ -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": {
diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs
index ee8ab457d..f190996c1 100644
--- a/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs
+++ b/src/Ryujinx.Common/Configuration/Hid/Controller/RumbleConfigController.cs
@@ -16,5 +16,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
/// Enable Rumble
///
public bool EnableRumble { get; set; }
+
+ ///
+ /// Enable HD Rumble support
+ /// = 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;
}
diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs
index fe225111b..85ca5ffcb 100644
--- a/src/Ryujinx.Input/HLE/NpadController.cs
+++ b/src/Ryujinx.Input/HLE/NpadController.cs
@@ -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})");
}
}
}
diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
index af61b7b63..3574c3061 100644
--- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
+++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
@@ -221,6 +221,7 @@ namespace Ryujinx.Headless
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
+ UseHDRumble = true
},
};
}
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
index 0b451eacb..1fe98ee69 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 72;
+ public const int CurrentVersion = 73;
///
/// Version of the configuration file format
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
index 90a045a67..728321985 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
@@ -333,6 +333,7 @@ namespace Ryujinx.Ava.Systems.Configuration
EnableRumble = false,
StrongRumble = 1f,
WeakRumble = 1f,
+ UseHDRumble = true
};
}
}
diff --git a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs
index 0eeef45f5..2a076f525 100644
--- a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs
+++ b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs
@@ -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
{
diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs
index 51229af72..68559d6c1 100644
--- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs
@@ -789,6 +789,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
StrongRumble = 1f,
WeakRumble = 1f,
EnableRumble = false,
+ UseHDRumble = true
},
};
}
diff --git a/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs
index e2323f567..74e0cd289 100644
--- a/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/Input/RumbleInputViewModel.cs
@@ -9,5 +9,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
[ObservableProperty]
public partial float WeakRumble { get; set; }
+
+ [ObservableProperty]
+ public partial bool EnableHDRumble { get; set; }
}
}
diff --git a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml
index 98489aab0..49eb1a717 100644
--- a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml
+++ b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml
@@ -53,6 +53,15 @@
Margin="5,0"
Text="{Binding WeakRumble, StringFormat=\{0:0.00\}}" />
+
+
+
diff --git a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs
index 347d011d5..655bdf591 100644
--- a/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs
+++ b/src/Ryujinx/UI/Views/Input/RumbleInputView.axaml.cs
@@ -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();