diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index 08b4cb37f..5a02ef11b 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -22,7 +22,7 @@ Plugins: ImageUpdate(data); */ void UpdateInputState(u32 port); -void UpdateVibration(u32 port, u32 value); +void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms); extern u16 kcode[4]; extern u32 vks[4]; @@ -46,10 +46,10 @@ struct MapleConfigMap : IMapleConfigMap this->player_num = player_num; } - void SetVibration(u32 value) + void SetVibration(float power, float inclination, u32 duration_ms) { int player_num = this->player_num == -1 ? dev->bus_id : this->player_num; - UpdateVibration(player_num, value); + UpdateVibration(player_num, power, inclination, duration_ms); } void GetInput(PlainJoystickState* pjs) diff --git a/core/hw/maple/maple_cfg.h b/core/hw/maple/maple_cfg.h index b487a2f76..4afcc99ce 100644 --- a/core/hw/maple/maple_cfg.h +++ b/core/hw/maple/maple_cfg.h @@ -52,7 +52,7 @@ struct PlainJoystickState struct IMapleConfigMap { - virtual void SetVibration(u32 value) = 0; + virtual void SetVibration(float power, float inclination, u32 duration_ms) = 0; virtual void GetInput(PlainJoystickState* pjs)=0; virtual void SetImage(void* img)=0; virtual ~IMapleConfigMap() {} diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 2778ea0e9..de190d03c 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -957,7 +957,7 @@ struct maple_microphone: maple_base struct maple_sega_purupuru : maple_base { - u16 AST, AST_ms; + u16 AST = 19, AST_ms = 5000; u32 VIBSET; virtual MapleDeviceType get_device_type() @@ -1053,8 +1053,32 @@ struct maple_sega_purupuru : maple_base case MDCF_SetCondition: VIBSET = *(u32*)&dma_buffer_in[4]; - //Do the rumble thing! - config->SetVibration(VIBSET); + { + //Do the rumble thing! + u8 POW_POS = (VIBSET >> 8) & 0x7; + u8 POW_NEG = (VIBSET >> 12) & 0x7; + u8 FREQ = (VIBSET >> 16) & 0xFF; + s16 INC = (VIBSET >> 24) & 0xFF; + if (VIBSET & 0x8000) // INH + INC = -INC; + else if (!(VIBSET & 0x0800)) // EXH + INC = 0; + bool CNT = VIBSET & 1; + + float power = min((POW_POS + POW_NEG) / 7.0, 1.0); + + u32 duration_ms; + if (FREQ > 0 && (!CNT || INC)) + duration_ms = min((int)(1000 * (INC ? abs(INC) * max(POW_POS, POW_NEG) : 1) / FREQ), (int)AST_ms); + else + duration_ms = AST_ms; + float inclination; + if (INC == 0 || power == 0) + inclination = 0.0; + else + inclination = FREQ / (1000.0 * INC * max(POW_POS, POW_NEG)); + config->SetVibration(power, inclination, duration_ms); + } return MDRS_DeviceReply; diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index dfc9a4c08..1e15cdb5a 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -207,3 +207,14 @@ void GamepadDevice::save_mapping() return; input_mapper->save(make_mapping_filename().c_str()); } + +void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms) +{ + int i = GamepadDevice::GetGamepadCount() - 1; + for ( ; i >= 0; i--) + { + std::shared_ptr gamepad = GamepadDevice::GetGamepad(i); + if (gamepad != NULL && gamepad->maple_port() == port && gamepad->is_rumble_enabled()) + gamepad->rumble(power, inclination, duration_ms); + } +} diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index e96ca8815..a001ea6c9 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -53,6 +53,10 @@ public: void save_mapping(); bool remappable() { return _remappable && input_mapper != NULL; } + virtual void rumble(float power, float inclination, u32 duration_ms) {} + virtual void update_rumble() {} + bool is_rumble_enabled() { return _rumble_enabled; } + static void Register(std::shared_ptr gamepad) { _gamepads_mutex.lock(); @@ -101,6 +105,8 @@ private: input_detected_cb _input_detected; bool _remappable; + bool _rumble_enabled = true; + static std::vector> _gamepads; static std::mutex _gamepads_mutex; }; diff --git a/core/linux-dist/evdev.cpp b/core/linux-dist/evdev.cpp index 5740cca23..d1001ac72 100644 --- a/core/linux-dist/evdev.cpp +++ b/core/linux-dist/evdev.cpp @@ -211,13 +211,5 @@ bool input_evdev_handle(u32 port) return true; } -void input_evdev_rumble(u32 port, u16 pow_strong, u16 pow_weak) -{ - std::shared_ptr dev = EvdevGamepadDevice::GetControllerForPort(port); - if (dev != NULL) - dev->Rumble(pow_strong, pow_weak); -} - - #endif // USE_EVDEV diff --git a/core/linux-dist/evdev.h b/core/linux-dist/evdev.h index 61b6de252..2fa28b9f9 100644 --- a/core/linux-dist/evdev.h +++ b/core/linux-dist/evdev.h @@ -4,4 +4,3 @@ extern void input_evdev_init(); extern void input_evdev_close(); extern bool input_evdev_handle(u32 port); -extern void input_evdev_rumble(u32 port, u16 pow_strong, u16 pow_weak); diff --git a/core/linux-dist/evdev_gamepad.h b/core/linux-dist/evdev_gamepad.h index 8e760a2c5..f3c5c4bd4 100644 --- a/core/linux-dist/evdev_gamepad.h +++ b/core/linux-dist/evdev_gamepad.h @@ -1,4 +1,5 @@ #include "../input/gamepad_device.h" +#include "oslib/oslib.h" #include "evdev.h" class EvdevGamepadDevice : public GamepadDevice @@ -60,36 +61,22 @@ public: close(_fd); } - // FIXME add to base class - void Rumble(u16 pow_strong, u16 pow_weak) + virtual void rumble(float power, float inclination, u32 duration_ms) override { - printf("RUMBLE: %u / %u (%d)\n", pow_strong, pow_weak, _rumble_effect_id); - struct ff_effect effect; - effect.type = FF_RUMBLE; - effect.id = _rumble_effect_id; - effect.u.rumble.strong_magnitude = pow_strong; - effect.u.rumble.weak_magnitude = pow_weak; - effect.replay.length = 0; - effect.replay.delay = 0; - if (ioctl(_fd, EVIOCSFF, &effect) == -1) - { - perror("evdev: Force feedback error"); - _rumble_effect_id = -2; - } - else - { - _rumble_effect_id = effect.id; + vib_inclination = inclination * power; + vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; - // Let's play the effect - input_event play; - play.type = EV_FF; - play.code = effect.id; - play.value = 1; - if (write(_fd, (const void*) &play, sizeof(play)) == -1) - { - perror("evdev: Force feedback error"); - _rumble_effect_id = -2; - } + do_rumble(power, duration_ms); + } + virtual void update_rumble() override + { + if (vib_inclination > 0) + { + int rem_time = (vib_stop_time - os_GetSeconds()) * 1000; + if (rem_time <= 0) + vib_inclination = 0; + else + do_rumble(vib_inclination * rem_time, rem_time); } } @@ -151,6 +138,7 @@ protected: private: void read_input() { + update_rumble(); input_event ie; while (read(_fd, &ie, sizeof(ie)) == sizeof(ie)) @@ -168,10 +156,47 @@ private: } } + void do_rumble(float power, u32 duration_ms) + { + // Remove previous effect + if (_rumble_effect_id != -1) + ioctl(_fd, EVIOCRMFF, _rumble_effect_id); + + // Upload new effect + struct ff_effect effect; + effect.type = FF_RUMBLE; + effect.id = -1; // Let the driver assign one + effect.direction = 0; + effect.replay.length = (u16)duration_ms; + effect.replay.delay = 0; + effect.u.rumble.strong_magnitude = (s16)(power * 32767); + effect.u.rumble.weak_magnitude = (s16)(power * 32767); + if (ioctl(_fd, EVIOCSFF, &effect) == -1) + { + perror("evdev: Force feedback error"); + _rumble_effect_id = -1; + } + else + { + _rumble_effect_id = effect.id; + + // Let's play the effect + input_event play; + play.type = EV_FF; + play.code = effect.id; + play.value = 1; + if (write(_fd, (const void*) &play, sizeof(play)) == -1) + { + perror("evdev: Force feedback error"); + } + } + } int _fd; std::string _devnode; - int _rumble_effect_id; + int _rumble_effect_id = -1; + float vib_inclination = 0; + double vib_stop_time = 0; static std::map> evdev_gamepads; }; diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 7471747de..eeedb540c 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -147,27 +147,6 @@ void UpdateInputState(u32 port) #endif } -void UpdateVibration(u32 port, u32 value) -{ - #if defined(USE_EVDEV) - u8 POW_POS = (value >> 8) & 0x3; - u8 POW_NEG = (value >> 12) & 0x3; - u8 FREQ = (value >> 16) & 0xFF; - - double pow = (POW_POS + POW_NEG) / 7.0; - double pow_l = pow * (0x3B - FREQ) / 17.0; - double pow_r = pow * (FREQ - 0x07) / 15.0; - - if (pow_l > 1.0) pow_l = 1.0; - if (pow_r > 1.0) pow_r = 1.0; - - u16 pow_strong = (u16)(65535 * pow_l); - u16 pow_weak = (u16)(65535 * pow_r); - - input_evdev_rumble(port, pow_strong, pow_weak); - #endif -} - void os_DoEvents() { #if defined(SUPPORT_X11) diff --git a/core/nacl/nacl.cpp b/core/nacl/nacl.cpp index f66c94ce8..eb81de6d1 100644 --- a/core/nacl/nacl.cpp +++ b/core/nacl/nacl.cpp @@ -263,10 +263,6 @@ void UpdateInputState(u32 port) { } -void UpdateVibration(u32 port, u32 value) { - -} - void os_CreateWindow() { } diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 62024dcb0..48c6562f3 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -236,27 +236,6 @@ void UpdateInputState(u32 port) #endif } -void UpdateVibration(u32 port, u32 value) -{ - u8 POW_POS = (value >> 8) & 0x3; - u8 POW_NEG = (value >> 12) & 0x3; - u8 FREQ = (value >> 16) & 0xFF; - - XINPUT_VIBRATION vib; - - double pow = (POW_POS + POW_NEG) / 7.0; - double pow_l = pow * (0x3B - FREQ) / 17.0; - double pow_r = pow * (FREQ - 0x07) / 15.0; - - if (pow_l > 1.0) pow_l = 1.0; - if (pow_r > 1.0) pow_r = 1.0; - - vib.wLeftMotorSpeed = (u16)(65535 * pow_l); - vib.wRightMotorSpeed = (u16)(65535 * pow_r); - - XInputSetState(port, &vib); -} - // Windows class name to register #define WINDOW_CLASS "nilDC" diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index ffa12ae15..d8d2ead3b 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -40,6 +40,8 @@ public: void ReadInput() { + update_rumble(); + XINPUT_STATE state; if (XInputGetState(_xinput_port, &state) == 0) @@ -99,6 +101,23 @@ public: last_right_thumb_y = 0; } } + virtual void rumble(float power, float inclination, u32 duration_ms) override + { + vib_inclination = inclination * power; + vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; + + do_rumble(power); + } + virtual void update_rumble() override + { + if (vib_inclination > 0) + { + int rem_time = (vib_stop_time - os_GetSeconds()) * 1000; + if (rem_time <= 0) + vib_inclination = 0; + do_rumble(vib_inclination * rem_time); + } + } void Open() { @@ -153,6 +172,16 @@ protected: } private: + void do_rumble(float power) + { + + XINPUT_VIBRATION vib; + + vib.wLeftMotorSpeed = (u16)(65535 * power); + vib.wRightMotorSpeed = (u16)(65535 * power); + + XInputSetState(_xinput_port, &vib); + } const int _xinput_port; u32 last_buttons_state = 0; @@ -162,6 +191,8 @@ private: s16 last_left_thumb_y = 0; s16 last_right_thumb_x = 0; s16 last_right_thumb_y = 0; + double vib_stop_time; + float vib_inclination; static std::vector> xinput_gamepads; }; diff --git a/shell/android-studio/reicast/src/main/jni/src/Android.cpp b/shell/android-studio/reicast/src/main/jni/src/Android.cpp index d0be66232..5165b8508 100644 --- a/shell/android-studio/reicast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/reicast/src/main/jni/src/Android.cpp @@ -175,11 +175,6 @@ void UpdateInputState(u32 Port) // @@@ Nothing here yet } -void UpdateVibration(u32 port, u32 value) -{ - -} - void *libPvr_GetRenderTarget() { return g_window; // the surface to render to diff --git a/shell/apple/emulator-ios/emulator/ios_main.mm b/shell/apple/emulator-ios/emulator/ios_main.mm index 8817fdb71..00cad0b12 100644 --- a/shell/apple/emulator-ios/emulator/ios_main.mm +++ b/shell/apple/emulator-ios/emulator/ios_main.mm @@ -113,10 +113,6 @@ void UpdateInputState(u32 port) { } -void UpdateVibration(u32 port, u32 value) { - -} - void get_mic_data(u8* ) { } diff --git a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm index 23b56b182..d5879729b 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm +++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm @@ -68,10 +68,6 @@ void UpdateInputState(u32 port) { } -void UpdateVibration(u32 port, u32 value) { - -} - void os_CreateWindow() { }