From ab88c2f6112edba35bfa91ee8864e760728d16e8 Mon Sep 17 00:00:00 2001
From: german <german@thesoftwareartisans.com>
Date: Fri, 10 Jul 2020 21:20:50 -0500
Subject: First implementation of controller rumble

---
 src/input_common/sdl/sdl_impl.cpp | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

(limited to 'src/input_common/sdl/sdl_impl.cpp')

diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index a9e676f4b2..27a96c18b7 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <array>
 #include <atomic>
+#include <chrono>
 #include <cmath>
 #include <functional>
 #include <mutex>
@@ -78,6 +79,33 @@ public:
         return state.axes.at(axis) / (32767.0f * range);
     }
 
+    bool RumblePlay(f32 amp_low, f32 amp_high, int time) {
+        const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
+        const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
+        // Lower drastically the number of state changes
+        if (raw_amp_low >> 11 == last_state_rumble_low >> 11 &&
+            raw_amp_high >> 11 == last_state_rumble_high >> 11) {
+            if (raw_amp_low + raw_amp_high != 0 ||
+                last_state_rumble_low + last_state_rumble_high == 0) {
+                return false;
+            }
+        }
+        // Don't change state if last vibration was < 20ms
+        const auto now = std::chrono::system_clock::now();
+        if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
+            std::chrono::milliseconds(20)) {
+            return raw_amp_low + raw_amp_high == 0;
+        }
+
+        last_vibration = now;
+        last_state_rumble_low = raw_amp_low;
+        last_state_rumble_high = raw_amp_high;
+        if (sdl_joystick) {
+            SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
+        }
+        return false;
+    }
+
     std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
         float x = GetAxis(axis_x, range);
         float y = GetAxis(axis_y, range);
@@ -139,6 +167,9 @@ private:
     } state;
     std::string guid;
     int port;
+    u16 last_state_rumble_high;
+    u16 last_state_rumble_low;
+    std::chrono::time_point<std::chrono::system_clock> last_vibration;
     std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
     std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
     mutable std::mutex mutex;
@@ -207,7 +238,7 @@ void SDLState::InitJoystick(int joystick_index) {
         sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
     }
     if (!sdl_joystick) {
-        LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
+        LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
         return;
     }
     const std::string guid = GetGUID(sdl_joystick);
@@ -303,6 +334,12 @@ public:
         return joystick->GetButton(button);
     }
 
+    bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
+        const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
+        const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
+        return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
+    }
+
 private:
     std::shared_ptr<SDLJoystick> joystick;
     int button;
-- 
cgit v1.2.3-70-g09d2