Merge pull request #4291 from german77/ImplementControllerRumble

input_common: First implementation of controller rumble
This commit is contained in:
David 2020-09-30 21:24:11 +10:00 committed by GitHub
commit 4d0ae1a17a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 14 deletions

View File

@ -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.

View File

@ -665,20 +665,31 @@ 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 {

View File

@ -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;

View File

@ -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);

View File

@ -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;