Skip to content

Commit

Permalink
Switch to more efficient rumble haptics
Browse files Browse the repository at this point in the history
  • Loading branch information
streetpea committed Feb 4, 2025
1 parent 421a053 commit 82a0b2d
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 51 deletions.
2 changes: 1 addition & 1 deletion gui/include/controllermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Controller : public QObject
void SetDualSenseRumble(uint8_t left, uint8_t right, uint8_t strength);
void SetTriggerEffects(uint8_t type_left, const uint8_t *data_left, uint8_t type_right, const uint8_t *data_right, uint8_t trigger_intensity);
void SetDualsenseMic(bool on);
void SetHapticRumble(uint16_t left, uint16_t right, int ms);
void SetHapticRumble(uint16_t left, uint16_t right);
void StartUpdatingMapping();
void IsUpdatingMappingButton(bool is_updating_mapping_button);
void EnableAnalogStickMapping(bool enabled);
Expand Down
8 changes: 6 additions & 2 deletions gui/include/streamsession.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,10 @@ class StreamSession : public QObject
bool vertical_sdeck;
#endif
QQueue<uint8_t> ds_rumble_haptics;
bool ds_rumble_haptics_connected;
QQueue<uint16_t> reg_rumble_haptics;
bool rumble_haptics_connected;
bool ds_rumble_haptics_on;
bool reg_rumble_haptics_on;
float PS_TOUCHPAD_MAX_X, PS_TOUCHPAD_MAX_Y;
ChiakiControllerState keyboard_state;
ChiakiControllerState touch_state;
Expand Down Expand Up @@ -281,7 +283,8 @@ class StreamSession : public QObject
void ConnectSdeckHaptics();
#endif
void DualSenseQueueRumbleHaptics(uint8_t strength);
void ConnectDualSenseRumbleHaptics();
void RegQueueRumbleHaptics(uint16_t strength);
void ConnectRumbleHaptics();

public:
explicit StreamSession(const StreamSessionConnectInfo &connect_info, QObject *parent = nullptr);
Expand Down Expand Up @@ -326,6 +329,7 @@ class StreamSession : public QObject
signals:
void FfmpegFrameAvailable();
void DualSenseRumbleHapticPushed(uint8_t strength);
void RegRumbleHapticPushed(uint16_t strength);
#if CHIAKI_GUI_ENABLE_STEAMDECK_NATIVE
void SdeckHapticPushed(haptic_packet_t packetl, haptic_packet_t packetr);
#endif
Expand Down
5 changes: 3 additions & 2 deletions gui/src/controllermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ Controller::~Controller()
// Clear trigger effects, SDL doesn't do it automatically
const uint8_t clear_effect[10] = { 0 };
this->SetTriggerEffects(0x05, clear_effect, 0x05, clear_effect, 0x00);
this->SetRumble(0,0);
SDL_GameControllerClose(controller);
}
#endif
Expand Down Expand Up @@ -830,12 +831,12 @@ void Controller::SetDualsenseMic(bool on)
#endif
}

void Controller::SetHapticRumble(uint16_t left, uint16_t right, int ms)
void Controller::SetHapticRumble(uint16_t left, uint16_t right)
{
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
if(!controller)
return;
SDL_GameControllerRumble(controller, left, right, ms);
SDL_GameControllerRumble(controller, left, right, 5000);
#endif
}

Expand Down
121 changes: 75 additions & 46 deletions gui/src/streamsession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#define NEW_DPAD_TOUCH_INTERVAL_MS 500
#define DPAD_TOUCH_UPDATE_INTERVAL_MS 10
#define STEAMDECK_HAPTIC_PACKETS_PER_ANALYSIS 4 // send packets every interval * packets per analysis
#define DUALSENSE_HAPTIC_PACKETS_PER_RUMBLE 3
#define RUMBLE_HAPTICS_PACKETS_PER_RUMBLE 3
#define STEAMDECK_HAPTIC_SAMPLING_RATE 3000
// DualShock4 touchpad is 1920 x 942
#define PS4_TOUCHPAD_MAX_X 1920.0f
Expand Down Expand Up @@ -185,8 +185,9 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje
ps5_haptic_intensity(1),
ps5_rumble_intensity(0),
ps5_trigger_intensity(0x07),
ds_rumble_haptics_connected(false),
ds_rumble_haptics_on(false)
rumble_haptics_connected(false),
ds_rumble_haptics_on(false),
reg_rumble_haptics_on(false)
{
mic_buf.buf = nullptr;
connected = false;
Expand Down Expand Up @@ -451,6 +452,7 @@ StreamSession::StreamSession(const StreamSessionConnectInfo &connect_info, QObje
}
#endif
rumble_haptics_intensity = connect_info.rumble_haptics_intensity;
ConnectRumbleHaptics();
}
UpdateGamepads();

Expand Down Expand Up @@ -491,8 +493,15 @@ StreamSession::~StreamSession()
}
#endif
#if CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
for(auto controller : controllers)
controller->Unref();
QMetaObject::invokeMethod(this, [this]() {
for(auto controller : controllers)
{
const uint8_t clear_effect[10] = { 0 };
controller->SetTriggerEffects(0x05, clear_effect, 0x05, clear_effect, 0x00);
controller->SetRumble(0,0);
controller->Unref();
}
});
#endif
#if CHIAKI_GUI_ENABLE_SETSU
setsu_free(setsu);
Expand Down Expand Up @@ -1344,18 +1353,22 @@ void StreamSession::DisconnectHaptics()
}
}

void StreamSession::ConnectDualSenseRumbleHaptics()
void StreamSession::ConnectRumbleHaptics()
{
if(ds_rumble_haptics_connected)
if(rumble_haptics_connected)
return;
ds_rumble_haptics = {};
ds_rumble_haptics.reserve(20);
reg_rumble_haptics = {};
reg_rumble_haptics.reserve(20);
connect(this, &StreamSession::DualSenseRumbleHapticPushed, this, &StreamSession::DualSenseQueueRumbleHaptics);
auto ds_rumble_haptics_interval = DUALSENSE_HAPTIC_PACKETS_PER_RUMBLE * 10;
auto ds_rumble_haptics_timer = new QTimer(this);
connect(ds_rumble_haptics_timer, &QTimer::timeout, this, [this]{
connect(this, &StreamSession::RegRumbleHapticPushed, this, &StreamSession::RegQueueRumbleHaptics);
auto rumble_haptics_interval = RUMBLE_HAPTICS_PACKETS_PER_RUMBLE * 10;
auto rumble_haptics_timer = new QTimer(this);
connect(rumble_haptics_timer, &QTimer::timeout, this, [this]{
bool changed = false;
uint16_t strength = 0;
uint16_t ds_strength = 0;
uint32_t reg_strength = 0;
uint8_t ds_intensity = 0;
switch(rumble_haptics_intensity)
{
Expand All @@ -1377,38 +1390,52 @@ void StreamSession::ConnectDualSenseRumbleHaptics()
default:
break;
}
for(size_t i = 0; i < DUALSENSE_HAPTIC_PACKETS_PER_RUMBLE; i++)
for(size_t i = 0; i < RUMBLE_HAPTICS_PACKETS_PER_RUMBLE; i++)
{
if(!ds_rumble_haptics.isEmpty())
strength += ds_rumble_haptics.dequeue();
ds_strength += ds_rumble_haptics.dequeue();
if(!reg_rumble_haptics.isEmpty())
reg_strength += reg_rumble_haptics.dequeue();
}
strength /= DUALSENSE_HAPTIC_PACKETS_PER_RUMBLE;
if(strength > 0 || ds_rumble_haptics_on)
{
QMetaObject::invokeMethod(this, [this, strength, ds_intensity]() {
for(auto controller : controllers)
{
if(controller->IsDualSense())
controller->SetDualSenseRumble(strength, strength, ds_intensity);
}
});
}
if(strength > 0)
ds_rumble_haptics_on = true;
else
ds_rumble_haptics_on = false;
ds_strength /= RUMBLE_HAPTICS_PACKETS_PER_RUMBLE;
reg_strength /= RUMBLE_HAPTICS_PACKETS_PER_RUMBLE;
QMetaObject::invokeMethod(this, [this, ds_strength, reg_strength, ds_intensity]() {
for(auto controller : controllers)
{
#if CHIAKI_GUI_ENABLE_STEAMDECK_NATIVE
if(haptics_handheld < 1 && (controller->IsHandheld() || (sdeck && controller->IsSteamVirtualUnmasked())))
#else
if(haptics_handheld < 1 && controller->IsHandheld())
#endif
continue;
if(controller->IsDualSense() && (ds_strength > 0 || ds_rumble_haptics_on))
controller->SetDualSenseRumble(ds_strength, ds_strength, ds_intensity);

if(!controller->IsDualSense() && (reg_strength > 0 || reg_rumble_haptics_on))
controller->SetHapticRumble(reg_strength, reg_strength);
}
});
ds_rumble_haptics_on = ds_strength > 0 ? true : false;
reg_rumble_haptics_on = reg_strength > 0 ? true : false;
});
ds_rumble_haptics_timer->start(ds_rumble_haptics_interval);
ds_rumble_haptics_connected = true;
rumble_haptics_timer->start(rumble_haptics_interval);
rumble_haptics_connected = true;
}

void StreamSession::DualSenseQueueRumbleHaptics(uint8_t strength)
{
if(!ds_rumble_haptics_connected)
if(!rumble_haptics_connected)
return;
ds_rumble_haptics.enqueue(strength);
}

void StreamSession::RegQueueRumbleHaptics(uint16_t strength)
{
if(!rumble_haptics_connected)
return;
reg_rumble_haptics.enqueue(strength);
}

void StreamSession::ConnectHaptics()
{
if (this->haptics_output > 0)
Expand All @@ -1419,7 +1446,6 @@ void StreamSession::ConnectHaptics()
if (!haptics_resampler_buf)
{
CHIAKI_LOGW(this->log.GetChiakiLog(), "Haptics resampler buf wasn't allocated, can't use haptics.");
ConnectDualSenseRumbleHaptics();
return;
}
#ifdef Q_OS_MACOS
Expand Down Expand Up @@ -1452,7 +1478,6 @@ void StreamSession::ConnectHaptics()
return;
}
CHIAKI_LOGW(log.GetChiakiLog(), "DualSense features were enabled and a DualSense is connected, but could not find the DualSense audio device!");
ConnectDualSenseRumbleHaptics();
return;
}

Expand Down Expand Up @@ -1718,22 +1743,26 @@ void StreamSession::PushHapticsFrame(uint8_t *buf, size_t buf_size)
// Set minimum rumble value if above rumble min for controllers that shift up to 9 bits when rumbling
left = ((left > 0 && left < (1 << 9)) ? (1 << 9) : left);
right = ((right > 0 && right < (1 << 9)) ? (1 << 9) : right);
QMetaObject::invokeMethod(this, [this, left, right, dualsense_strength]() {
for(auto controller : controllers)
{
uint16_t strength = (left > right) ? left : right;
bool send_reg_rumble_haptics = false;
bool send_ds_rumble_haptics = false;
for(auto controller : controllers)
{
#if CHIAKI_GUI_ENABLE_STEAMDECK_NATIVE
if(haptics_handheld < 1 && (controller->IsHandheld() || (sdeck && controller->IsSteamVirtualUnmasked())))
if(haptics_handheld < 1 && (controller->IsHandheld() || (sdeck && controller->IsSteamVirtualUnmasked())))
#else
if(haptics_handheld < 1 && controller->IsHandheld())
if(haptics_handheld < 1 && controller->IsHandheld())
#endif
continue;
uint16_t strength = (left > right) ? left : right;
if(controller->IsDualSense())
emit DualSenseRumbleHapticPushed(dualsense_strength);
else
controller->SetHapticRumble(strength, strength, 10);
}
});
continue;
if(controller->IsDualSense())
send_ds_rumble_haptics = true;
else
send_reg_rumble_haptics = true;
}
if(send_ds_rumble_haptics)
emit DualSenseRumbleHapticPushed(dualsense_strength);
if(send_reg_rumble_haptics)
emit RegRumbleHapticPushed(strength);
return;
}
if(haptics_output == 0)
Expand Down

0 comments on commit 82a0b2d

Please sign in to comment.