forked from eden-emu/eden
		
	Merge pull request #4291 from german77/ImplementControllerRumble
input_common: First implementation of controller rumble
This commit is contained in:
		
						commit
						4d0ae1a17a
					
				
					 5 changed files with 63 additions and 14 deletions
				
			
		|  | @ -33,6 +33,9 @@ public: | ||||||
|     virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { |     virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
|  |     virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// An abstract class template for a factory that can create input devices.
 | /// An abstract class template for a factory that can create input devices.
 | ||||||
|  |  | ||||||
|  | @ -665,21 +665,32 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | void Controller_NPad::VibrateController(const std::vector<u32>& controllers, | ||||||
|                                         const std::vector<Vibration>& vibrations) { |                                         const std::vector<Vibration>& vibrations) { | ||||||
|     LOG_DEBUG(Service_HID, "(STUBBED) called"); |     LOG_TRACE(Service_HID, "called"); | ||||||
| 
 | 
 | ||||||
|     if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { |     if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     for (std::size_t i = 0; i < controller_ids.size(); i++) { |     bool success = true; | ||||||
|         std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); |     for (std::size_t i = 0; i < controllers.size(); ++i) { | ||||||
|         if (connected_controllers[controller_pos].is_connected) { |         if (!connected_controllers[i].is_connected) { | ||||||
|             // TODO(ogniK): Vibrate the physical controller
 |             continue; | ||||||
|  |         } | ||||||
|  |         using namespace Settings::NativeButton; | ||||||
|  |         const auto& button_state = buttons[i]; | ||||||
|  |         if (button_state[A - BUTTON_HID_BEGIN]) { | ||||||
|  |             if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( | ||||||
|  |                     vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, | ||||||
|  |                     vibrations[0].freq_low)) { | ||||||
|  |                 success = false; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |     if (success) { | ||||||
|         last_processed_vibration = vibrations.back(); |         last_processed_vibration = vibrations.back(); | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | ||||||
|     return last_processed_vibration; |     return last_processed_vibration; | ||||||
|  |  | ||||||
|  | @ -125,7 +125,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); |     void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); | ||||||
| 
 | 
 | ||||||
|     void VibrateController(const std::vector<u32>& controller_ids, |     void VibrateController(const std::vector<u32>& controllers, | ||||||
|                            const std::vector<Vibration>& vibrations); |                            const std::vector<Vibration>& vibrations); | ||||||
| 
 | 
 | ||||||
|     Vibration GetLastVibration() const; |     Vibration GetLastVibration() const; | ||||||
|  |  | ||||||
|  | @ -817,18 +817,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { | ||||||
| 
 | 
 | ||||||
| void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { | void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp{ctx}; |     IPC::RequestParser rp{ctx}; | ||||||
|     const auto controller_id{rp.Pop<u32>()}; |     const auto controller{rp.Pop<u32>()}; | ||||||
|     const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; |     const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; | ||||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; |     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, |     LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, | ||||||
|               applet_resource_user_id); |               applet_resource_user_id); | ||||||
| 
 | 
 | ||||||
|     IPC::ResponseBuilder rb{ctx, 2}; |     IPC::ResponseBuilder rb{ctx, 2}; | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
| 
 | 
 | ||||||
|     applet_resource->GetController<Controller_NPad>(HidController::NPad) |     applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||||||
|         .VibrateController({controller_id}, {vibration_values}); |         .VibrateController({controller}, {vibration_values}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | ||||||
|  | @ -846,8 +846,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | ||||||
| 
 | 
 | ||||||
|     std::memcpy(controller_list.data(), controllers.data(), controllers.size()); |     std::memcpy(controller_list.data(), controllers.data(), controllers.size()); | ||||||
|     std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); |     std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); | ||||||
|     std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(), |  | ||||||
|                    [](u32 controller_id) { return controller_id - 3; }); |  | ||||||
| 
 | 
 | ||||||
|     applet_resource->GetController<Controller_NPad>(HidController::NPad) |     applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||||||
|         .VibrateController(controller_list, vibration_list); |         .VibrateController(controller_list, vibration_list); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <chrono> | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | @ -78,6 +79,33 @@ public: | ||||||
|         return state.axes.at(axis) / (32767.0f * range); |         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 { |     std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const { | ||||||
|         float x = GetAxis(axis_x, range); |         float x = GetAxis(axis_x, range); | ||||||
|         float y = GetAxis(axis_y, range); |         float y = GetAxis(axis_y, range); | ||||||
|  | @ -139,6 +167,9 @@ private: | ||||||
|     } state; |     } state; | ||||||
|     std::string guid; |     std::string guid; | ||||||
|     int port; |     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_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; | ||||||
|     std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; |     std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; | ||||||
|     mutable std::mutex mutex; |     mutable std::mutex mutex; | ||||||
|  | @ -207,7 +238,7 @@ void SDLState::InitJoystick(int joystick_index) { | ||||||
|         sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); |         sdl_gamecontroller = SDL_GameControllerOpen(joystick_index); | ||||||
|     } |     } | ||||||
|     if (!sdl_joystick) { |     if (!sdl_joystick) { | ||||||
|         LOG_ERROR(Input, "failed to open joystick {}", joystick_index); |         LOG_ERROR(Input, "Failed to open joystick {}", joystick_index); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     const std::string guid = GetGUID(sdl_joystick); |     const std::string guid = GetGUID(sdl_joystick); | ||||||
|  | @ -303,6 +334,12 @@ public: | ||||||
|         return joystick->GetButton(button); |         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: | private: | ||||||
|     std::shared_ptr<SDLJoystick> joystick; |     std::shared_ptr<SDLJoystick> joystick; | ||||||
|     int button; |     int button; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 David
						David