android: do all rumble/haptic on background thread

Haptic effects for the virtual gamepad must be done on a background
thread. Not sure about gamepad rumble but it shouldn't hurt.
Reuse haptic duration for haptic power (0-100).
Fix crash when power is zero (createOneShot needs power >= 1)
This commit is contained in:
Flyinghead 2024-08-15 17:25:02 +02:00
parent aafa9f0bf0
commit f1fd8b12e2
4 changed files with 45 additions and 33 deletions

View File

@ -1441,7 +1441,7 @@ static void gamepadSettingsPopup(const std::shared_ptr<GamepadDevice>& gamepad)
if (gamepad->is_virtual_gamepad())
{
header("Haptic");
OptionSlider("Power", config::VirtualGamepadVibration, 0, 60, "Haptic feedback power");
OptionSlider("Power", config::VirtualGamepadVibration, 0, 100, "Haptic feedback power", "%d%%");
}
else if (gamepad->is_rumble_enabled())
{

View File

@ -22,7 +22,7 @@ public class Emulator extends Application {
public static final int MDT_Microphone = 2;
public static final int MDT_None = 8;
public static int vibrationDuration = 20;
public static int vibrationPower = 80;
public static int[] maple_devices = {
MDT_None,
@ -42,7 +42,7 @@ public class Emulator extends Application {
*
*/
public void getConfigurationPrefs() {
Emulator.vibrationDuration = JNIdc.getVirtualGamepadVibration();
Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration();
JNIdc.getControllers(maple_devices, maple_expansion_devices);
}
@ -54,7 +54,7 @@ public class Emulator extends Application {
{
Log.i("flycast", "SaveAndroidSettings: saving preferences");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Emulator.vibrationDuration = JNIdc.getVirtualGamepadVibration();
Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration();
JNIdc.getControllers(maple_devices, maple_expansion_devices);
prefs.edit()

View File

@ -105,12 +105,31 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
}
}
private void vibrate(Vibrator vibrator, long duration_ms, float power)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int ipow = Math.min((int)(power * 255), 255);
if (ipow >= 1)
vibrator.vibrate(VibrationEffect.createOneShot(duration_ms, ipow));
else
vibrator.cancel();
}
else
vibrator.vibrate(duration_ms);
}
// Called from native code
// returns false if the device has no vibrator
private boolean rumble(int i, float power, float inclination, int duration_ms)
{
Vibrator vibrator = getVibrator(i);
if (vibrator == null)
return false;
if (i == VIRTUAL_GAMEPAD_ID) {
if (Emulator.vibrationPower == 0)
return true;
power *= Emulator.vibrationPower / 100.f;
}
VibrationParams params;
synchronized (this) {
@ -120,25 +139,15 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
vibParams.put(i, params);
}
}
if (power == 0) {
if (params.power != 0)
vibrator.cancel();
} else {
if (inclination > 0) {
params.inclination = inclination * power;
params.stopTime = System.currentTimeMillis() + duration_ms;
}
else {
params.inclination = 0;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
vibrator.vibrate(VibrationEffect.createOneShot(duration_ms, (int)(power * 255)));
else
vibrator.vibrate(duration_ms);
if (power != 0) {
params.stopTime = System.currentTimeMillis() + duration_ms;
if (inclination > 0)
VibratorThread.getInstance().setVibrating();
params.inclination = inclination * power;
else
params.inclination = 0;
}
params.power = power;
VibratorThread.getInstance().setVibrating();
return true;
}
@ -164,19 +173,19 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
synchronized (this) {
params = vibParams.get(i);
}
if (vibrator == null || params == null || params.power == 0 || params.inclination == 0)
if (vibrator == null || params == null)
return false;
long remTime = params.stopTime - System.currentTimeMillis();
if (remTime <= 0) {
if (remTime <= 0 || params.power == 0) {
params.power = 0;
params.inclination = 0;
vibrator.cancel();
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
vibrator.vibrate(VibrationEffect.createOneShot(remTime, (int)(params.inclination * remTime * 255)));
if (params.inclination > 0)
vibrate(vibrator, remTime, params.inclination * remTime);
else
vibrator.vibrate(remTime);
vibrate(vibrator, remTime, params.power);
return true;
}

View File

@ -36,6 +36,7 @@ public class VibratorThread extends Thread {
private VibrationEffect clickEffect = null;
int clickDuration = 0;
private static VibratorThread INSTANCE = null;
private static final int LEGACY_VIBRATION_DURATION = 20; // ms
public static VibratorThread getInstance() {
synchronized (VibratorThread.class) {
@ -52,7 +53,11 @@ public class VibratorThread extends Thread {
private Vibrator getVibrator(int i)
{
if (i == InputDeviceManager.VIRTUAL_GAMEPAD_ID) {
return (Vibrator) Emulator.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
if (Emulator.vibrationPower > 0)
return (Vibrator) Emulator.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
else
// vibration disabled
return null;
}
else {
InputDevice device = InputDevice.getDevice(i);
@ -111,7 +116,7 @@ public class VibratorThread extends Thread {
}
public void click() {
if (Emulator.vibrationDuration > 0) {
if (Emulator.vibrationPower > 0) {
synchronized (this) {
click = true;
notify();
@ -126,17 +131,16 @@ public class VibratorThread extends Thread {
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
if (clickEffect == null || clickDuration != Emulator.vibrationDuration)
if (clickEffect == null)
{
clickDuration = Emulator.vibrationDuration;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
clickEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
else
clickEffect = VibrationEffect.createOneShot(clickDuration, VibrationEffect.DEFAULT_AMPLITUDE);
clickEffect = VibrationEffect.createOneShot(LEGACY_VIBRATION_DURATION, VibrationEffect.DEFAULT_AMPLITUDE);
}
vibrator.vibrate(clickEffect);
} else {
vibrator.vibrate(Emulator.vibrationDuration);
vibrator.vibrate(LEGACY_VIBRATION_DURATION);
}
}
@ -144,8 +148,7 @@ public class VibratorThread extends Thread {
{
// FIXME possible race condition
synchronized (this) {
if (nextRumbleUpdate == 0)
nextRumbleUpdate = System.currentTimeMillis() + 16667;
nextRumbleUpdate = 1;
notify();
}
}