First implementation of controller rumble
This commit is contained in:
parent
a8be822e8e
commit
ab88c2f611
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.
|
||||||
|
|
|
@ -609,20 +609,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 {
|
||||||
|
|
|
@ -121,7 +121,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;
|
||||||
|
|
|
@ -802,18 +802,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) {
|
||||||
|
@ -831,8 +831,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…
Reference in a new issue