HD rumble on Switch Pro Controllers and Joy-Cons, through SDL's effect call #1213
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!1213
Loading…
Reference in a new issue
No description provided.
Delete branch "hd-rumble"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
HD rumble on Switch Pro Controllers and Joy-Cons
A Nintendo Switch Pro Controller or Joy-Con now plays the game's HD rumble as the Switch does: each side of the controller, both frequency bands, at the frequencies and amplitudes the game asks for, instead of two motor strengths. Every other controller keeps the rumble it has today.
How
nn::hid::VibrationValue: low and high band amplitude and frequency, per side) already reachvibration_sendinhost/nn_hid.c. One added call hands the raw value for its side tohost/pad_sdl.c(hos_sdl_rumble_hd), beside the motor values it already gets. The motor mapping is unchanged.host/hd_rumble.h(new, pure, no SDL): encodes a value into the 4 bytes a Switch controller plays per side and builds the 10-byte rumble report (0x10, packet number, left, right) that SDL's Switch driver takes throughSDL_SendGamepadEffect(SDL 3.2 on,HIDAPI_DriverSwitch_SendJoystickEffect, which writes it in place of its own rumble).host/hos_sdl.c: the pad's type (SDL_GetGamepadType/SDL_GameControllerGetType) and the effect call (SDL_SendGamepadEffect/SDL_GameControllerSendEffect), both looked up as optional, so an SDL without them loads as before.host/pad_sdl.c: a pad SDL reports as a Switch Pro Controller or Joy-Con (left, right or pair) gets the packet. A Pro Controller (and a pair) plays the game's left side on its left and right on its right; a single Joy-Con plays its own side. A packet goes out when it changes, never sooner than 32 ms after the last one (SDL keeps 30 ms between its own writes: faster can turn a Bluetooth controller off), and a rumble that goes on is sent again every 40 ms (SDL refreshes its own every 50 ms, or the controller stops it). The stop is sent once. Such a pad gets noSDL_RumbleGamepadcall, so SDL's internal rumble never runs over it.HOS_HD_RUMBLE=0sends the Switch controllers the motor rumble, as before.Tests
tests/test_hd_rumble.pycompiles the encoder: rows of the documented frequency and amplitude tables come out as their bytes, every amplitude code 1 to 100 comes back from its own amplitude, the reverse engineering notes' example packet (a8 89 e3 4d), silence, clamping of both frequencies and the amplitude (with a strength over 100%), each side's place in the packet for a Pro Controller, a pair and single Joy-Cons, which SDL3 and SDL2 types count, and the cadence. It also runspad_sdl.c'shd_openandhd_pollagainst a fake SDL: nothing is sent while silent, a held rumble about every 40 ms and never faster than 32 ms, one stop, and a pad whose SDL refuses the packet goes to the motor rumble for good. It checks that the poll scales HD rumble by the menu's Rumble strength.Docs
The readme's Controls section says which controllers get HD rumble, that it needs SDL3, and that on Linux SDL's Switch driver needs read and write access to the controller's hidraw device (Steam's udev rules,
steam-devices); without it the kernel's hid-nintendo driver runs the controller, with the motor rumble.Needs a real controller
I could not test the feel here. Worth checking with a Pro Controller and Joy-Cons, over USB and Bluetooth: that the rumble feels right and steady (no buzz between refreshes, no stop during a long rumble), that it stops when it should, and that Bluetooth stays connected at this cadence. OoT asks for small amplitudes for most effects (0.02 to 0.06), so HD rumble at the game's own strength is gentle, as on the console.
Credits
The encoding follows the community's documentation: Switchbrew's HID "Rumble data" page and dekuNukem's Nintendo_Switch_Reverse_Engineering (
rumble_data_table.md), written from their formulas and tables. The report layout and timing follow SDL's Switch driver (zlib license).Tested on hardware
A Switch 1 Pro Controller over USB on Linux, with the system SDL3 (3.2+) and hidraw access: the log shows
hid: HD rumble on "Nintendo Switch Pro Controller"(no fallback), and the HD rumble is felt in play (sword on grass, hits, landing, item catches), steady and stopping cleanly. The first version sent OoT's small amplitudes (mostly 0.02-0.1, from its RumbleCall gains) unboosted and was too faint to feel;ea47d62puts each band through the same curve the motor path has used since #263, keeping the frequencies.Still untested on hardware: Bluetooth, single Joy-Cons, Windows with the bundled SDL3.
Self-review notes:
Checked and fine:
To fix / consider:
@ -0,0 +120,4 @@static inline void hd_rumble_packet(int kind, const uint8_t left[4], const uint8_t right[4], uint8_t out[HD_RUMBLE_PACKET_SIZE]) {out[0] = HD_RUMBLE_REPORT;out[1] = 0; /* (SDL numbers the packets itself) */const uint8_t *l = kind == HD_RUMBLE_JOYCON_RIGHT ? right : left, *r = kind == HD_RUMBLE_JOYCON_LEFT ? left : right;A single Joy-Con plays only its own side here. nn_hid always presents a FullKey Pro Controller, though, so the game keeps addressing both sides, and an effect it sends to just the other side never reaches a lone Joy-Con (the motor path uses the max of both sides). For JOYCON_LEFT/RIGHT, merge the two sides before encoding: take each band's stronger amplitude, with the frequency that goes with it. A Joy-Con pair opened as two separate pads would then both play the merged value, which is still better than losing effects. Needs a test row.
Minor: any refusal turns HD off for as long as the pad is open, including a one-off write failure on a pad that otherwise takes HD. With SDL3 the write is queued on SDL's rumble thread, so false here effectively means "unsupported", and that's fine. The comment just shouldn't suggest more than that.
Self-review addressed:
5e2d7ac. A lone Joy-Con now plays both sides merged (hd_rumble_merge: each band at the stronger side's amplitude, with that side's frequency), like the motor path's max; Pro Controllers and pairs keep each side separate. Test rows cover the per-band max, one-sided input, and a left-only effect on a right Joy-Con (and the reverse).HOS_HD_RUMBLE=0switches them to the motor rumble. A vendor-id check wouldn't help: SDL's Switch driver rewrites every pad that identifies as a Pro Controller or Joy-Con to Nintendo's VID/PID (UpdateDeviceIdentity), so they can't be told apart.c5514b9), no conflicts.