Repository navigation
Conversation
feat(ps5): add USB UAC1 4-channel audio haptics support|
Why it uses regular rumble instead of audio-driven way? I think you could feed my code to your LLM Agent so it can come up with a better solution |
|
I've been working on this in my personal branch for a while. But my lack of knowledge on this is keeping me from getting it working correctly. It seems everytime a get one feature working, another stops working. If I get pcm haptics working, gyro and legacy rumble disappear. Something tells me this has do with hid-playstation feature flags, but I need to do some actual research now. I changed this to a draft until I figure it out. |
73fc73b to
0b734a8
Compare
|
Okay, I refactored it a bit. If anybody wants to tear it apart, feel free. |
0b734a8 to
44eaa80
Compare
44eaa80 to
cfbdf54
Compare
Present a 4-channel 48 kHz UAC1 audio function in the DualSense modes and turn the haptic channels (3-4) into controller haptics over RF. USB identity, so PipeWire picks the Direct profile and libScePad accepts the pad: - audio function on interfaces 0-2 with a silent 2ch mic, gamepad on 3 - byte-for-byte 273-byte report descriptor, pairing report tail, bcdDevice 1.00 - no serial string in MODE_PS5_GAME (wrap of tud_descriptor_device_cb) - static firmware-info payload (0x20); NULL output buffers ignored Audio haptics: - level from 20 ms RMS with an instant-rise/halving-fall envelope and auto gain; square-root drive - tone style: per-actuator 0x83 tones; frequency from zero crossings - split style: below 80 Hz as 0x80 rumble, the rest as tones - rumble style: 0x80 rumble merged with legacy host rumble - panel toggle, style button and gain slider (WebUSB fields 30/31/88) WirePlumber: an optional hook starts only the haptic channels of a first-seen Direct route at 100% (`make install-wireplumber`). docs/DUALSENSE_HAPTICS.md covers setup, Proton launch options and the hardware measurements behind the design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A real USB DualSense either answers a feature GET in full or stalls it;
it never sends a short reply. Answer 0x05, 0x09, 0x20, 0x22, 0x81,
0x83, 0xE0, 0xF1 (63 B), 0x85, 0xF2 and 0xF5 at the sizes the report
descriptor declares, with a real pad's values where they are known,
and stall everything else (0x03/0x08 capability, 0x0A, 0x21, ...).
Product string "DualSense Wireless Controller", as a real USB DualSense
reports it ("Wireless Controller" alone is the DS4's).
Input report: fill the bytes a real pad sends where we have no source
(sensor temperature, two constant bytes, the second timestamp).
UAC1 function: use a real pad's terminal/unit IDs and associations
(mic path IT 4 -> FU 5 -> OT 6), master mute + volume only, adaptive
sync on the OUT endpoint, no sampling-frequency control, and its max
packet sizes (392/196, mic packets 192). Volume ranges match what Linux
shows for a real pad.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OUTPUT 0x82/0x83 select actuators as trackpads 0 left, 1 right, 2 both and grips 3 left, 4 right, 5 both (0x81 swaps 0 and 1). The PS-mode pad click pulse always sent 1, so every click buzzed the right pad. Add HSIDE_* constants and hsidePads(); psPadClickEdge() now takes the click mask and buzzes the pad(s) clicked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The controller's OUTPUT handlers for 0x87/0x88/0x89 are raw haptic sample streams (0x87 [target][samples] to the trackpad or grip actuators, 0x88 a stereo grip stream, 0x89 a length-prefixed 0x87). OpenPuck relayed only 0x80-0x86, believing these were settings reports, while the real puck forwards every OUTPUT report up to 64 bytes, so audio-to-haptics apps produced no feel through it. - Relay 0x80-0x89 and raise the haptic relay payload to 63 bytes so a full stream report fits (frame 67 B, under the 96 B MAXLEN). Feature-0x01 command relays keep their 60-byte cap (RELAY_CMD_MAXP). - An 8 kHz stereo stream needs ~258 frames/s, above the 250 Hz poll cycle: flush a second queued relay in the same cycle while the ring holds a backlog. - Resend an unanswered 0x86-0x89 frame once with the same PID (ESB dedup drops it if the first copy landed). - Count ring evictions; the serial stat line shows relay=/s and drop=. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the PCM haptic stream (OUTPUT 0x86 format, 0x88 stereo frames of 31 u-law samples per side at 4 kHz, measured on the controller's IMU) and a DualSense "wave" audio-haptic style that uses it: the haptic channels low-passed at 300 Hz (two Butterworth sections, also the anti-alias), decimated to 4 kHz and streamed to the grip actuators. - The format is re-sent every second and the stream stays up for 300 ms after the gate closes, so short pauses don't pay the controller's ~40 ms pre-buffer again. The first frames are sized so playback starts after 24 ms instead of 31 ms. - Wave plays the channels as sent (no auto gain); 100% is 40% of full scale. It is the new default style, also for a cfg.bin saved by an earlier build of this branch (whose stored style was the old tone default, not a choice). - Grip limiter: PCM peaks above a knee (default 70% of full scale) round off instead of clipping, which plays as a pop. 100 = off. Persisted; WebUSB field 115, status blob p[199]. - WebUSB field 88 accepts wave (3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In Switch Pro mode the console streams HD rumble: two bands per side, each with its own amplitude and frequency. OpenPuck used to keep only one amplitude per side for the 0x80 rumble. Now it renders the bands: - each side's low + high band, summed and phase-continuous, as a 4 kHz PCM stream on that side's grip (where a Pro Controller's actuators are). The stream stays up through 500 ms gaps so new effects don't pay the controller's pre-buffer again. - each side's high band as an 0x83 tone on that side's trackpad, held under a quiet ceiling around 250-300 Hz where the pads ring loudly. Tones are refreshed every 50 ms, re-sent at once on a hit, and cut with -128 dB when the band goes quiet. The decoder now tracks band frequencies (absolute 7-bit and relative 5-bit codes, around 160/320 Hz) and maps amplitude code 100, not 127, to full scale as the public amplitude table and SDL's encoder do: every level used to play at 0.56x. SPI 0x6012 reports device type 3 (Pro Controller), which Eden's direct driver needs to accept input. Switch Pro always renders HD rumble; the rumble style applies to the other modes. Grip strength follows the rumble strength; trackpad strength is a separate setting (persisted, WebUSB field 141, the same field upstream PR safijari#303 uses; status blob p[200]). The relay scrub now drops queued 0x83 tones on reconnect/stop and keeps their -128 dB cuts. The band decoder and renderer are adapted from Froggerdog's HD Rumble Emulation in upstream PR safijari#303 (without its profiles, trackpad D-pad and shortcut feedback). tests/hd_rumble/run.py covers decode, rendering, stop and transitions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- DualSense tab: the haptics style button cycles rumble, tone, split and wave (the firmware default). - Rumble card, on firmware with protocol v22: a grip limiter select (field 115; 50-90%, 70% default, or off) and an HD trackpad strength select (field 141; off = grips only, 100% default), with a note on how Switch Pro renders HD rumble. Older firmware hides them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- DUALSENSE_HAPTICS.md: wave style (300 Hz low-pass, PCM to the grips), grip limiter, feature reports answered like a real pad, protocol v22 fields and blob bytes, and the FFXIV prefix repair on Wine 11. - PROTOCOL.md: 63-byte haptic relay and backlog flush, OUTPUT 0x86-0x89 table, actuator routing, measured PCM stream behaviour, Switch Pro HD rumble (section 9.5), fields 30/31/88/115/141 and the v22 status blob tail. - README: HD rumble and grip limiter, DualSense setup on Linux/Proton, the Steam-mode 0x87-0x89 relay. - TESTING_GUIDE: HD rumble, grip limiter, audio styles, PCM relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
280d5e4 to
9881556
Compare
|
Yet another refactor. Updated the protocol to improve a bunch of haptic related protocols, improved dualsense audio haptics, and added switch pro hd rumble. |
|
Working well for me in Arknights Endfield and Infinity Nikki in Dualsense mode. Touchpad haptics for touchpad "click" feels a bit stiffer than the typical steam controller experience. |
|
Try disabling audio haptics, and tell me if it still feels "stiff" I suspect it might latency from the audio stream queue. |
- Add relayEnqueueFront for priority messages - Bypass queued PCM frames for click events - Replace click button with dropdown for haptic style
|
Switch rumble feels much better, however it seems to rumble twice. It will fall off and then come back a bit at the end. Doesn't happen on #303. It also feels like it is 'dropping' the rumble a bit. Like a packet is being lost? On the Mario Kart World character select panning across each character doesn't feel consistent like on a real Pro controller. Might be a symptom of some interference with the rumble not ending properly. |
|
@Jefemy There very well could be dropped packets, so I'll look into it. |
Just tried messing around with all the settings on the panel using the built in web panel test rumble. On everything I tested there is a slight "click" after the rumble should end. Its power doesn't seem to be affected by any of the settings. |
|
are you using the docs/index.html in the pr, or the github.io config panel? Because the github.io one won't show my added options unless this PR gets merged. |
|
I am using the panel and uf2 downloaded from the main branch of your repo. I also first tested it using a uf2 compiled from the branch pulled here. |
|
Oh, if your using the main in my ripps818/openpuck repo, it has tons of other stuff. You should make a new issue on my repo, not here. Because it might not be affecting this PR. |
|
I only used the main branch to make sure it wasn't fixed by another commit. Both issues are still in this PR. I will make an issue on your repo to continue this though. EDIT: Switch HD Rumble works great now. Have been using it for a few weeks now |
When an HD rumble went silent, the grip stream ran 500 ms of silence and then hdStop sent two -128 dB pad cuts and a 0x86 op 1. Sent to idle actuators, these play as a click of their own, at a strength no rumble setting changes. hdStop now cuts the pads only while a tone is on, and trims the grip stream only if it played within the last 150 ms. A silent stream just stops sending frames, like the DualSense wave stream. A PCM stream keeps a fixed fill once it starts, so a frame lost on a bad channel takes its samples out of that cushion for good, and two or three losses run the stream dry. A 0x86-0x89 frame still unanswered after its same-PID resend now goes back to the front of the ring for up to two later cycles. The same cycle's backlog flush is skipped after a requeue. Fixes #14 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 32a8ff2)
The Switch and the DualSense each keep their own haptic style and grip limiter knee, since different systems may suit different ones. The limiter was one global shared by both, and the Switch had no style at all. Switch HD rumble now has the DualSense's four styles for its grips: wave (the streamed waveform, as before and still the default), tone (each side's stronger band as a grip tone), split (low band as 0x80 rumble, high band as a grip tone) and rumble (the stronger band as 0x80). The pads always play the high band as tones. Changing style mid-effect stops the old style's vibration. Protocol 22 (not bumped): fields 120-123 (limiter) and 124-127 (style) by type, blob p[201..208]. Fields 115 and 88 still work for older panels, and a config from before the per-type fields starts both types from the legacy shared values. The panel falls back to 115 / 88 on older firmware. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…100% The wave style's two full-scale bands made the Switch's grips rough at the shared 200% strength, so the Switch now starts on the tone style at 100% and every other type keeps wave at 200%. Grip strength is kept per type: field 22 edits the active type's, and a config from before the per-type values seeds every type with the old shared value unless it was still the default. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Off plays the console's rumble as plain 0x80 motor rumble (each side's peak, relayed on change), shaped by the host-rumble style and strength like every other mode. Protocol 22 (not bumped): field 128, blob p[209]. The panel shows the toggle on the Switch tab and greys the haptics style and grip limiter while it is off. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plain motor rumble needs the 200% the other types start with, where the HD tones want the Switch's 100%, so turning HD rumble off would have made the rumble weak. The Switch now keeps a second strength for HD rumble off (200% by default); the Strength setting edits whichever applies, and field 128 switches between them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each type already kept its own grip strength, but the Rumble card's one Strength select only edited the active type's. Every controller tab now has a Rumble strength row (the Switch's follows its HD Rumble setting) and the Switch tab has the HD trackpad strength. Protocol 22 (not bumped): fields 108-111 edit a type's strength, blob p[210..213] read them; field 22 still edits the active type's, and older firmware keeps working on the active tab. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Upstream took protocol v22 and blob byte p[196] for the RF recovery marker, and appended a 10-byte ext[] block to cfg.bin. This branch's fields move behind both: - status blob: protocol 23, audio haptics and per-type haptics bytes shift from p[196..213] to p[197..214] (WB_PAYLEN 213); panel and docs follow - cfg.bin: the haptics tail sits after ext[], so builds of this PR drop their saved haptics settings to defaults once - Makefile wraps the device descriptor alongside upstream's string descriptor and control-transfer wraps - DualSense feature reports keep this branch's real-pad answers (0x03 stalls, as a real pad does); upstream's identical firmware info table is kept once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The wave style sums each grip's low and high band as two sine oscillators that kept running through the 500 ms hang between effects. Each new effect so started with a random offset between the bands, and with close band frequencies the sum beats: the same rumble swelled on one play and faded on the next. Reset a side's two phases when it starts sounding after silence, where the level is zero so the jump can't click. Refs #17 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grip PCM stream played rough on both: most frames were resent and many played twice. The controller answers each frame in its ESB ACK, either empty within ~200 us or packed with queued reports up to 127 bytes, ending ~800-1000 us after the frame. A 400 us window cut most replies off, empty ACKs did not count, and a retry on a later cycle carries a new PID, so the controller played the duplicate. - Wait up to 1200 us after a PCM frame and count an empty ACK as delivered. - Turn the radio from TX to RX in hardware (DISABLED->RXEN). In DualSense mode the USB audio processing preempted the loop between TX end and a software RXEN, and 83% of frames got no reply; now 1%, and polls are back at 250/s. - Send 0x86 once per stream, on both paths: re-sending it every second made the grips stutter once a second. A controller whose link drops gets a fresh stream when it returns. - Start a Switch HD stream 62 samples ahead, so an effect starts ~15.5 ms sooner. - Limiter knee 85 for both types (IMU: no measurable strength lost; 70 cost 3-5%, and blind A/B tests preferred 85). Cap the Switch wave at 100% grip strength and the DualSense wave at 250% gain: past full scale the grips flatten, clip, and grind. - Debug op 0x35 (0xB1 frame): PCM stream counters, reply delays and outcomes, the controller's 0x44 events, and DualSense audio state. - Op 0x36: a Switch HD pan test, a tone panned left-right-left. Refs #17 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 0x35 / 0xB1 grip PCM counters grow from 134 to 198 bytes: - wait in the relay ring before each 0x88 frame's first send, and the gap between frames made for one controller (ms histograms) - "ran dry" events sorted into end of stream, mid-stream, and mid-stream within 500 ms of the start - each frame's first send against a steady sample clock from the stream's first frame: earliest, latest, last, and the value at the last mid-stream dry - other relays sent while a stream runs, relay turns skipped after a missed poll, and the poll count Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two faults, found with the new send-timing counters and a blind 5-play test (ticks felt matched dropped packets and underruns one for one): - The poll loop skipped the next relay turn whenever a poll got no data. After a PCM frame the controller has often just sent its input in that frame's ACK, so the poll's empty ACK was taken for a drop: ~90 skipped turns/s, frames held a 4 ms cycle, send times spread ~14 ms against the controller's ~15.5 ms fill, and streams ran dry mid-play. An empty ACK with a good CRC now counts as a reply. Skips 0-1 per play, no mid-stream underruns. Applies to every mode. - The nRF5x USB driver drops an ISO OUT packet without an error when no transfer is armed at its SOF. The audio callback re-armed only after its DSP, so a slow packet lost the next one: 1 ms cut out of the wave, felt as a tick. It now arms a second buffer first; every packet arrives (host and puck counts match). PROTOCOL.md also records the DualSense limiter A/B: 85 and 100 felt alike, 70 weaker; 85 stays the default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pan test (op 0x36, "Test haptic rumble") fed the Switch HD path, so it did nothing outside Switch Pro mode with HD rumble. Elsewhere it now runs the wave generator itself and streams the same equal-power pan to the grips as PCM, then plays silence through the hang and lets the stream run dry, so it ends without the stop click. A controller already streaming DualSense audio is skipped: two writers on one stream garble both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tone was the Switch default only because the wave style's two full-scale bands felt rough on it. The wave fixes, the 100% grip strength cap and the limiter knee of 85 fixed that, and wave plays both HD bands at their own frequencies, left and right apart, like the DualSense already does. Saved styles are kept; fresh setups and Reset to defaults get wave. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A flash save while the DualSense audio stream played wedged the ISO OUT endpoint: the driver kept a transfer pending, refused every re-arm, and dropped every packet until a bus reset. In a game, switching the style Wave -> Rumble -> Wave in the panel left Wave silent for good (measured: alt 1, 1000 packets/s sent, none received; a USB reset brought it back). The same stall is why the controller's input reports stopped after a settings change mid-game. - While the audio interface is at alt 1, a setting applies at once and its save waits until the stream stops; every WebUSB save goes through saveCfgSoon. A mode switch or firmware-update reboot writes a held save first. - A watchdog re-arms the endpoint when alt 1 goes 50 ms without a packet (the host sends one every frame), for any other flash write that lands mid-stream. Counted in the 0xB1 frame (200 bytes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The grips play down to ~0.5% of full scale in proportion (IMU), but a hand barely feels a few percent, so soft footsteps vanish and a fading effect seems to stop dead. Soft boost is an upward compressor on the grip PCM stream, Switch and DualSense: below 20% of full scale the level rises 2:1 toward it, capped at 1.5x / 2x / 3x / 4x; louder passages pass unchanged to the limiter. One envelope for both grips keeps the balance, and below 0.3% the boost fades out so a quiet track's noise stays quiet. Per type, fields 144-147, blob p[215..218], saved in cfg.bin. The default is 1.5x: in a blind A/B the fade felt continuous with it and cut out with it off, and 2x lifted some effects too far. Also brings the limiter and haptic-style defaults in the docs up to date (85%, wave). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel showed 24 words free on the usbd task (800 bytes, fixed in the core) in DualSense mode, against 105-122 in the other modes. Only DualSense runs USB audio there: - Every 1 ms audio packet copied its 63-byte 0x88 frame onto the stack before queueing it. Producers now fill the frame in place (hapticPcmSend 104 -> 40 bytes of stack). - WebUSB settings saves ran LittleFS on that stack. saveCfgSoon now only marks the save; cfgSaveTask writes it from the loop task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The band split, its energies and the zero crossings only feed the tone, split and rumble styles, yet ran on every 48 kHz sample in Wave too: about a third of the per-sample work in the usbd task. Output is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "Test haptic rumble" in the Rumble card plays the grip pan test (op 0x36): the Switch tab's haptics style in Switch Pro mode, Wave elsewhere. - Soft boost (Off / Gentle 1.5x / Low 2x / Medium 3x / High 4x) on the Switch and DS5 tabs, fields 144 + type; hidden on firmware without it. - The grip limiter offers 85% (the new default) and 95%; both style dropdowns mark Wave as the default. - Notes say where the Wave style stops getting stronger: 100% grip strength on the Switch, 250% gain on the DualSense. In Wave the DualSense gain's Auto reads "Auto (100%)", since Wave skips the auto gain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Played one Black Flag clip to the puck and a real DualSense together and read both controllers' accelerometers. Mid-level effects matched at 0.5x and the clip's hits reached the grips' ceiling at 2x, so: - the DualSense Strength runs 0-100% of each style's strongest (protocol 24, was percent/2 of 10-500%); in Wave 100% is 2x the track and Auto 25% (0.5x); a gain saved on the old scale loads as Auto - a Wave expander raises the gain with the track's own level - the Wave gate drops to 16 so quiet menu effects play - Soft boost starts off on the DualSense; the Grip limiter stays 85 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Summary
Adds haptics that use the Steam Controller 2's actuators directly instead of motor-style rumble:
0x87-0x89), so audio-to-haptics apps work through the puck.Changes
Eight commits, each builds on its own:
UAC1 4-channel audio haptics (
mode_ps5_audio.*). 48 kHz 4-channel output plus a silent 2-channel mic, which PipeWire's stock DualSense profile requires. Channels 3 and 4 drive the actuators in one of four styles, set from the panel:0x83tones per actuator.0x80, merged with the game's normal rumble.DualSense identity:
bcdDevice1.00, no serial string in the single-HID mode;PS trackpad click fix: the click pulse now buzzes the pad that was clicked. It always used side 1, which is the right pad.
Steam mode relay of OUTPUT
0x87-0x89. These are raw haptic sample streams, not settings reports, and the real puck forwards them. Changes:PCM stream + wave style + grip limiter:
hapticPcmStart/hapticPcmSend/hapticUlaw;Switch Pro HD rumble. Decoder:
0x6012reports device type 3, which Eden's direct Pro Controller driver needs.Renderer (in the main loop, not the USB callback):
0x83tones, capped at -15 dB between 250 and 300 Hz;The decode and renderer are adapted from @Froggerdog's HD Rumble Emulation in Add optional Switch Pro profiles, trackpad D-pad and HD Rumble Emulation #303, without its profiles, trackpad D-pad and shortcut changes.
Panel: wave in the DualSense style button. A grip limiter select and an HD trackpad strength select in the Rumble card, hidden on older firmware.
Docs:
docs/DUALSENSE_HAPTICS.md: setup, Proton notes, hardware measurements;0x86-0x89table, actuator routing, measured PCM behaviour, HD rumble (§9.5), new fields;Update
Four things were added on top of the eight commits, after testing and the reports in the comments:
-128 dBcut or0x86op 1 sent to an idle one plays as a click of its own. An unanswered0x86-0x89frame goes back to the front of the ring for up to two more poll cycles. This path is shared, so DualSense wave and Steam-mode0x87-0x89relay get the retry too.120-127). The Switch gets the four styles for its grips (wave, tone, split, rumble); both types start on wave. Fields88and115still work for older panels.108-111;22still edits the active type). The Switch starts at 100%, where its wave stops getting stronger; the other types start at 200%.128). Off plays plain motor rumble with its own strength (200%).In the panel, each controller's tab now has Rumble strength, and the Switch and DS5 tabs have Haptics style and Grip limiter; the Switch tab also has HD Rumble and HD trackpad strength. The Rumble card keeps Style and Test rumble.
The status blob grows to
p[202..214](limiter and style per type, HD Rumble, strength per type). A config from before these starts every type from the old shared limiter, style and strength.New tests:
tests/type_haptics/run.py, and more cases intests/hd_rumble/run.py. Hardware: Mario Kart in Eden feels fine.Update 2: upstream merge and haptics fixes
Merged current
main(no conflicts left) and brought over the fixes from testing in the comments (Jefemy's reports on HD rumble position and strength):0x86goes out once per stream. Re-sending it every second made the grips stutter once a second.144-147, blobp[215..218]): an upward compressor that raises quiet wave passages such as footsteps, Off / 1.5x (default) / 2x / 3x / 4x.0x36): a tone panned across the grips, in every mode. Op0x35reads PCM stream counters (debug).Behaviour changes worth a look
0x03/0x08, which SDL probes for capabilities.0x87-0x89. Steam's own traffic uses them only for haptic streams.Protocol / config
Status blob v23 (upstream is at 22, with the RF recovery marker at
p[196]) appendsp[197..201]: audio gain/2, audio haptics on/off, audio style, grip limiter knee, HD trackpad strength/2.WebUSB fields:
30audio gain (percent/2, 0 = auto)31audio haptics on/off88audio style (0 rumble, 1 tone, 2 split, 3 wave)115grip limiter knee (50-100, 100 = off)141HD trackpad strength (percent/2)None of these collide with Add optional Switch Pro profiles, trackpad D-pad and HD Rumble Emulation #303. 141 is Add optional Switch Pro profiles, trackpad D-pad and HD Rumble Emulation #303's own field for the same setting.
Later fields:
108-111,120-127,128and144-147, blobp[202..218](see the updates above).cfg.bin: new fields are appended to the tail, after upstream'sext[]block, so existing files keep loading. A file from an earlier build of this PR loses its saved haptics settings once and starts on the defaults.Related PRs
ps5GetCommon, which also stalls0x03.Testing
make check(clang-format 18) passes; every commit builds.python3 tests/hd_rumble/run.py: HD decode, rendering, stop, transition, pan test and soft boost tests, built with ASan/UBSan and-Werror;python3 tests/type_haptics/run.py: per-type defaults and config loading.docs/DUALSENSE_HAPTICS.md.Linux setup
No WirePlumber config is required. Set the DualSense Direct sink's haptic channels to 100% once:
make install-wireplumberinstalls an optional hook that does this automatically. libScePad games (Sony PC ports) need GE-Proton 11-6+ and Steam Input off.AI Disclosure
Note
This feature was heavily developed with the assistance of LLMs/AI. Everything has been reviewed, built, and hardware-verified.
🤖 Generated with Claude Code