[PR #238] feat: DualSense audio haptics, Switch Pro HD rumble and full-rate PCM haptic relay #251

Open
opened 2026-10-22 05:08:17 +00:00 by roman · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/safijari/openpuck/pull/238
Author: @ripps818
Created: 8/20/2026
Status: 🔄 Open

Base: main ← Head: dualsense-haptic


📝 Commits (10+)

  • c9a4980 feat(ps5): add UAC1 4-channel audio haptics
  • b8bcdb9 feat(ps5): answer DualSense feature reports like a real pad
  • 2ab1472 fix(haptics): buzz the trackpad that was clicked in PlayStation modes
  • 62f63c8 feat(haptics): relay OUTPUT haptic sample streams 0x87-0x89 at full rate
  • ac4aad8 feat(ps5): wave style streams audio haptics as PCM to the grips
  • 011a7ba feat(swpro): HD rumble on the trackpads and grips
  • f2279c5 feat(panel): wave style, grip limiter and HD trackpad strength
  • 9881556 docs: document wave style, PCM streams, HD rumble and new fields
  • 165abc9 feat(haptics): reduce trackpad click latency
  • 9275d82 fix(haptics): no click after HD rumble, retry lost PCM frames

📊 Changes

32 files changed (+4098 additions, -196 deletions)

View changed files

📝 Makefile (+30 -2)
📝 OpenPuck/OpenPuck.ino (+16 -5)
📝 OpenPuck/config.cpp (+184 -1)
📝 OpenPuck/config.h (+69 -0)
📝 OpenPuck/gamepad_util.cpp (+11 -5)
📝 OpenPuck/gamepad_util.h (+1 -1)
📝 OpenPuck/haptics.cpp (+869 -54)
📝 OpenPuck/haptics.h (+176 -5)
📝 OpenPuck/mode_hidgyro.cpp (+1 -1)
📝 OpenPuck/mode_ps5.cpp (+160 -28)
➕ OpenPuck/mode_ps5_audio.cpp (+936 -0)
➕ OpenPuck/mode_ps5_audio.h (+28 -0)
📝 OpenPuck/mode_switch_pro.cpp (+116 -27)
📝 OpenPuck/puck_hid.cpp (+10 -6)
📝 OpenPuck/puck_hid.h (+1 -1)
📝 OpenPuck/rf_link.cpp (+61 -15)
📝 OpenPuck/rf_link.h (+3 -0)
📝 OpenPuck/serial_console.cpp (+2 -2)
📝 OpenPuck/usb_app_drivers.cpp (+1 -0)
📝 OpenPuck/usb_app_drivers.h (+1 -0)

...and 12 more files

📄 Description

Summary

Adds haptics that use the Steam Controller 2's actuators directly instead of motor-style rumble:

  • DualSense mode: the puck presents a real pad's 4-channel USB audio function and plays the game's haptic channels on the controller. By default it streams them as PCM to the grip actuators, as a real DualSense does.
  • Switch Pro mode: renders the console's HD rumble. Both bands play on the grips, and the high band plays as tones on the trackpads.
  • Steam mode: relays the controller's PCM haptic streams (OUTPUT 0x87-0x89), so audio-to-haptics apps work through the puck.

Changes

Eight commits, each builds on its own:

  1. 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:

    • wave (default): the waveform itself, low-passed at 300 Hz and streamed to the grips as 4 kHz u-law PCM.
    • tone: 0x83 tones per actuator.
    • split: below 80 Hz as rumble, the rest as tones.
    • rumble: 0x80, merged with the game's normal rumble.
  2. DualSense identity:

    • byte-for-byte report descriptor, pairing report tail, bcdDevice 1.00, no serial string in the single-HID mode;
    • feature reports answered at their declared sizes with a real pad's values, or stalled, never a short reply;
    • product string "DualSense Wireless Controller", as a real pad reports it.
  3. PS trackpad click fix: the click pulse now buzzes the pad that was clicked. It always used side 1, which is the right pad.

  4. Steam mode relay of OUTPUT 0x87-0x89. These are raw haptic sample streams, not settings reports, and the real puck forwards them. Changes:

    • relay payload raised to 63 bytes for haptic reports (feature commands stay at 60);
    • a second relay per poll cycle while the ring is backed up (an 8 kHz stream needs ~258 frames/s);
    • one same-PID resend of an unanswered PCM frame.
  5. PCM stream + wave style + grip limiter:

    • shared hapticPcmStart / hapticPcmSend / hapticUlaw;
    • short first frames, so playback starts after 24 ms instead of 31 ms;
    • a soft-limit knee (default 85%) rounds off grip peaks instead of clipping them into pops.
  6. Switch Pro HD rumble. Decoder:

    • tracks band frequencies;
    • maps amplitude code 100 to full scale, matching the public amplitude table and SDL's encoder (it used to play every level at 0.56x);
    • SPI 0x6012 reports device type 3, which Eden's direct Pro Controller driver needs.

    Renderer (in the main loop, not the USB callback):

    • grips: low + high band per side, summed and phase-continuous, as PCM;
    • trackpads: the high band as 0x83 tones, capped at -15 dB between 250 and 300 Hz;
    • silence stops it after a 500 ms hang.

    The decode and renderer are adapted from @Froggerdog's HD Rumble Emulation in #303, without its profiles, trackpad D-pad and shortcut changes.

  7. 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.

  8. Docs:

    • docs/DUALSENSE_HAPTICS.md: setup, Proton notes, hardware measurements;
    • PROTOCOL.md: OUTPUT 0x86-0x89 table, actuator routing, measured PCM behaviour, HD rumble (§9.5), new fields;
    • README and TESTING_GUIDE.

Update

Four things were added on top of the eight commits, after testing and the reports in the comments:

  1. No click after HD rumble, and fewer dropped PCM frames. Stop messages now go only to actuators that may still be playing, since a -128 dB cut or 0x86 op 1 sent to an idle one plays as a click of its own. An unanswered 0x86-0x89 frame goes back to the front of the ring for up to two more poll cycles. This path is shared, so DualSense wave and Steam-mode 0x87-0x89 relay get the retry too.
  2. Per-type haptic style and grip limiter for the Switch and the DualSense (fields 120-127). The Switch gets the four styles for its grips (wave, tone, split, rumble); both types start on wave. Fields 88 and 115 still work for older panels.
  3. Per-type rumble strength (fields 108-111; 22 still edits the active type). The Switch starts at 100%, where its wave stops getting stronger; the other types start at 200%.
  4. HD Rumble toggle for the Switch (field 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 in tests/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):

  1. Wave frames are delivered once. The controller answers a PCM frame in its ESB ACK up to ~1 ms later, so the old 400 us window cut most replies off and the resends played twice. The puck now waits up to 1200 us, counts an empty ACK as delivered, and turns the radio around to RX in hardware, since in DualSense mode the USB audio work preempted a software RXEN and 83% of replies were missed (now 1%).
  2. 0x86 goes out once per stream. Re-sending it every second made the grips stutter once a second.
  3. Switch HD bands restart in step on each effect, so the same effect no longer swells or fades depending on where the bands' phases were.
  4. DualSense wave ticks and underruns: an empty ACK after a PCM frame no longer counts as a dropped poll, which skipped a third of relay turns; and the ISO OUT endpoint is armed with a second buffer before processing, because the nRF driver drops a packet when none is armed.
  5. USB audio survives settings saves. A flash write mid-stream wedged the ISO OUT endpoint until a bus reset. Saves now run from the loop task (also off the usbd task's 800-byte stack) and wait while the audio interface is open; a watchdog re-arms a stuck endpoint.
  6. Defaults: grip limiter 85% (IMU: no measurable strength lost, 70 cost 3-5%); the Switch starts on wave; the Switch wave is capped at 100% grip strength and the DualSense wave at 250% gain, past which the grips grind.
  7. Soft boost (fields 144-147, blob p[215..218]): an upward compressor that raises quiet wave passages such as footsteps, Off / 1.5x (default) / 2x / 3x / 4x.
  8. "Test haptic rumble" (op 0x36): a tone panned across the grips, in every mode. Op 0x35 reads PCM stream counters (debug).

Behaviour changes worth a look

  • Switch Pro renders HD rumble by default. The Switch tab's HD Rumble toggle (field 128) turns it off, which plays the console's rumble as plain motor rumble. The rumble style (field 39) applies in every other mode, and in Switch mode with HD Rumble off.
  • Unanswered PS5 feature reports now stall, as on a real pad. That includes 0x03/0x08, which SDL probes for capabilities.
  • Steam mode now forwards 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]) appends p[197..201]: audio gain/2, audio haptics on/off, audio style, grip limiter knee, HD trackpad strength/2.

  • WebUSB fields:

    • 30 audio gain (percent/2, 0 = auto)
    • 31 audio haptics on/off
    • 88 audio style (0 rumble, 1 tone, 2 split, 3 wave)
    • 115 grip limiter knee (50-100, 100 = off)
    • 141 HD trackpad strength (percent/2)

    None of these collide with #303. 141 is #303's own field for the same setting.

  • Later fields: 108-111, 120-127, 128 and 144-147, blob p[202..218] (see the updates above).

  • cfg.bin: new fields are appended to the tail, after upstream's ext[] 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.

  • #303: this PR carries #303's HD rumble core. If #303 lands first, I'll rebase this onto it and drop the duplicate.
  • #295 (merged): the firmware-info payload here is identical; the merge keeps this branch's ps5GetCommon, which also stalls 0x03.

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.
  • Hardware, this branch:
    • DualSense audio haptics (wave) in Control Resonant (GE-Proton, Steam Input off);
    • Switch Pro HD rumble in Super Mario Odyssey on Eden with the Direct Pro Controller driver.
  • Hardware, earlier versions of this PR: Stellar Blade, Hi-Fi Rush and FFXIV. Per-game status and known issues are in docs/DUALSENSE_HAPTICS.md.

Linux setup

No WirePlumber config is required. Set the DualSense Direct sink's haptic channels to 100% once:

pactl set-sink-volume alsa_output.usb-Sony_Interactive_Entertainment_DualSense_Wireless_Controller-00.Direct__Direct__sink 40% 40% 100% 100%

make install-wireplumber installs 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


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/safijari/openpuck/pull/238 **Author:** [@ripps818](https://github.com/ripps818) **Created:** 8/20/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `dualsense-haptic` --- ### 📝 Commits (10+) - [`c9a4980`](https://github.com/safijari/openpuck/commit/c9a4980b71f854cb61f0ade199db00bbe7bb8f50) feat(ps5): add UAC1 4-channel audio haptics - [`b8bcdb9`](https://github.com/safijari/openpuck/commit/b8bcdb9dde060838c72303adff07a9d4f4b41dd0) feat(ps5): answer DualSense feature reports like a real pad - [`2ab1472`](https://github.com/safijari/openpuck/commit/2ab1472a90479f7dce69b5d41ea671f56ea0b419) fix(haptics): buzz the trackpad that was clicked in PlayStation modes - [`62f63c8`](https://github.com/safijari/openpuck/commit/62f63c86b36c031a77cc4987dfffa4950d11d2f8) feat(haptics): relay OUTPUT haptic sample streams 0x87-0x89 at full rate - [`ac4aad8`](https://github.com/safijari/openpuck/commit/ac4aad80720f6f7cfc40d31477cdd8b7dc61e41c) feat(ps5): wave style streams audio haptics as PCM to the grips - [`011a7ba`](https://github.com/safijari/openpuck/commit/011a7ba942f2b38243c2c8035a34345bd64d309c) feat(swpro): HD rumble on the trackpads and grips - [`f2279c5`](https://github.com/safijari/openpuck/commit/f2279c5c85bef04c9beea86c777f28c0cccc1150) feat(panel): wave style, grip limiter and HD trackpad strength - [`9881556`](https://github.com/safijari/openpuck/commit/9881556b213b1642a365a26f57208341418f3e4e) docs: document wave style, PCM streams, HD rumble and new fields - [`165abc9`](https://github.com/safijari/openpuck/commit/165abc9a4eb15897f15c161bd5fca7de7cd31d2b) feat(haptics): reduce trackpad click latency - [`9275d82`](https://github.com/safijari/openpuck/commit/9275d8230718158eccebadd7bd839891370cd05c) fix(haptics): no click after HD rumble, retry lost PCM frames ### 📊 Changes **32 files changed** (+4098 additions, -196 deletions) <details> <summary>View changed files</summary> 📝 `Makefile` (+30 -2) 📝 `OpenPuck/OpenPuck.ino` (+16 -5) 📝 `OpenPuck/config.cpp` (+184 -1) 📝 `OpenPuck/config.h` (+69 -0) 📝 `OpenPuck/gamepad_util.cpp` (+11 -5) 📝 `OpenPuck/gamepad_util.h` (+1 -1) 📝 `OpenPuck/haptics.cpp` (+869 -54) 📝 `OpenPuck/haptics.h` (+176 -5) 📝 `OpenPuck/mode_hidgyro.cpp` (+1 -1) 📝 `OpenPuck/mode_ps5.cpp` (+160 -28) ➕ `OpenPuck/mode_ps5_audio.cpp` (+936 -0) ➕ `OpenPuck/mode_ps5_audio.h` (+28 -0) 📝 `OpenPuck/mode_switch_pro.cpp` (+116 -27) 📝 `OpenPuck/puck_hid.cpp` (+10 -6) 📝 `OpenPuck/puck_hid.h` (+1 -1) 📝 `OpenPuck/rf_link.cpp` (+61 -15) 📝 `OpenPuck/rf_link.h` (+3 -0) 📝 `OpenPuck/serial_console.cpp` (+2 -2) 📝 `OpenPuck/usb_app_drivers.cpp` (+1 -0) 📝 `OpenPuck/usb_app_drivers.h` (+1 -0) _...and 12 more files_ </details> ### 📄 Description ## Summary Adds haptics that use the Steam Controller 2's actuators directly instead of motor-style rumble: - **DualSense mode:** the puck presents a real pad's 4-channel USB audio function and plays the game's haptic channels on the controller. By default it streams them as PCM to the grip actuators, as a real DualSense does. - **Switch Pro mode:** renders the console's HD rumble. Both bands play on the grips, and the high band plays as tones on the trackpads. - **Steam mode:** relays the controller's PCM haptic streams (OUTPUT `0x87`-`0x89`), so audio-to-haptics apps work through the puck. ## Changes Eight commits, each builds on its own: 1. **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: - **wave** (default): the waveform itself, low-passed at 300 Hz and streamed to the grips as 4 kHz u-law PCM. - **tone:** `0x83` tones per actuator. - **split:** below 80 Hz as rumble, the rest as tones. - **rumble:** `0x80`, merged with the game's normal rumble. 2. **DualSense identity**: - byte-for-byte report descriptor, pairing report tail, `bcdDevice` 1.00, no serial string in the single-HID mode; - feature reports answered at their declared sizes with a real pad's values, or stalled, never a short reply; - product string "DualSense Wireless Controller", as a real pad reports it. 3. **PS trackpad click fix:** the click pulse now buzzes the pad that was clicked. It always used side 1, which is the right pad. 4. **Steam mode relay of OUTPUT `0x87`-`0x89`.** These are raw haptic sample streams, not settings reports, and the real puck forwards them. Changes: - relay payload raised to 63 bytes for haptic reports (feature commands stay at 60); - a second relay per poll cycle while the ring is backed up (an 8 kHz stream needs ~258 frames/s); - one same-PID resend of an unanswered PCM frame. 5. **PCM stream + wave style + grip limiter:** - shared `hapticPcmStart` / `hapticPcmSend` / `hapticUlaw`; - short first frames, so playback starts after 24 ms instead of 31 ms; - a soft-limit knee (default 85%) rounds off grip peaks instead of clipping them into pops. 6. **Switch Pro HD rumble.** Decoder: - tracks band frequencies; - maps amplitude code 100 to full scale, matching the public amplitude table and SDL's encoder (it used to play every level at 0.56x); - SPI `0x6012` reports device type 3, which Eden's direct Pro Controller driver needs. Renderer (in the main loop, not the USB callback): - grips: low + high band per side, summed and phase-continuous, as PCM; - trackpads: the high band as `0x83` tones, capped at -15 dB between 250 and 300 Hz; - silence stops it after a 500 ms hang. The decode and renderer are adapted from @Froggerdog's HD Rumble Emulation in #303, without its profiles, trackpad D-pad and shortcut changes. 7. **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. 8. **Docs:** - `docs/DUALSENSE_HAPTICS.md`: setup, Proton notes, hardware measurements; - PROTOCOL.md: OUTPUT `0x86`-`0x89` table, actuator routing, measured PCM behaviour, HD rumble (§9.5), new fields; - README and TESTING_GUIDE. ## Update Four things were added on top of the eight commits, after testing and the reports in the comments: 1. **No click after HD rumble, and fewer dropped PCM frames.** Stop messages now go only to actuators that may still be playing, since a `-128 dB` cut or `0x86` op 1 sent to an idle one plays as a click of its own. An unanswered `0x86`-`0x89` frame goes back to the front of the ring for up to two more poll cycles. This path is shared, so DualSense wave and Steam-mode `0x87`-`0x89` relay get the retry too. 2. **Per-type haptic style and grip limiter** for the Switch and the DualSense (fields `120`-`127`). The Switch gets the four styles for its grips (wave, tone, split, rumble); both types start on wave. Fields `88` and `115` still work for older panels. 3. **Per-type rumble strength** (fields `108`-`111`; `22` still edits the active type). The Switch starts at 100%, where its wave stops getting stronger; the other types start at 200%. 4. **HD Rumble toggle for the Switch** (field `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 in `tests/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): 1. **Wave frames are delivered once.** The controller answers a PCM frame in its ESB ACK up to ~1 ms later, so the old 400 us window cut most replies off and the resends played twice. The puck now waits up to 1200 us, counts an empty ACK as delivered, and turns the radio around to RX in hardware, since in DualSense mode the USB audio work preempted a software RXEN and 83% of replies were missed (now 1%). 2. **`0x86` goes out once per stream.** Re-sending it every second made the grips stutter once a second. 3. **Switch HD bands restart in step** on each effect, so the same effect no longer swells or fades depending on where the bands' phases were. 4. **DualSense wave ticks and underruns:** an empty ACK after a PCM frame no longer counts as a dropped poll, which skipped a third of relay turns; and the ISO OUT endpoint is armed with a second buffer before processing, because the nRF driver drops a packet when none is armed. 5. **USB audio survives settings saves.** A flash write mid-stream wedged the ISO OUT endpoint until a bus reset. Saves now run from the loop task (also off the usbd task's 800-byte stack) and wait while the audio interface is open; a watchdog re-arms a stuck endpoint. 6. **Defaults:** grip limiter 85% (IMU: no measurable strength lost, 70 cost 3-5%); the Switch starts on wave; the Switch wave is capped at 100% grip strength and the DualSense wave at 250% gain, past which the grips grind. 7. **Soft boost** (fields `144`-`147`, blob `p[215..218]`): an upward compressor that raises quiet wave passages such as footsteps, Off / 1.5x (default) / 2x / 3x / 4x. 8. **"Test haptic rumble"** (op `0x36`): a tone panned across the grips, in every mode. Op `0x35` reads PCM stream counters (debug). ## Behaviour changes worth a look - **Switch Pro renders HD rumble by default.** The Switch tab's HD Rumble toggle (field 128) turns it off, which plays the console's rumble as plain motor rumble. The rumble style (field 39) applies in every other mode, and in Switch mode with HD Rumble off. - **Unanswered PS5 feature reports now stall**, as on a real pad. That includes `0x03`/`0x08`, which SDL probes for capabilities. - **Steam mode now forwards `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]`) appends `p[197..201]`: audio gain/2, audio haptics on/off, audio style, grip limiter knee, HD trackpad strength/2. - **WebUSB fields:** - `30` audio gain (percent/2, 0 = auto) - `31` audio haptics on/off - `88` audio style (0 rumble, 1 tone, 2 split, 3 wave) - `115` grip limiter knee (50-100, 100 = off) - `141` HD trackpad strength (percent/2) None of these collide with #303. 141 is #303's own field for the same setting. - **Later fields:** `108`-`111`, `120`-`127`, `128` and `144`-`147`, blob `p[202..218]` (see the updates above). - **`cfg.bin`:** new fields are appended to the tail, after upstream's `ext[]` 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 - **#303:** this PR carries #303's HD rumble core. If #303 lands first, I'll rebase this onto it and drop the duplicate. - **#295** (merged): the firmware-info payload here is identical; the merge keeps this branch's `ps5GetCommon`, which also stalls `0x03`. ## 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. - Hardware, this branch: - DualSense audio haptics (wave) in *Control Resonant* (GE-Proton, Steam Input off); - Switch Pro HD rumble in *Super Mario Odyssey* on Eden with the Direct Pro Controller driver. - Hardware, earlier versions of this PR: *Stellar Blade*, *Hi-Fi Rush* and *FFXIV*. Per-game status and known issues are in `docs/DUALSENSE_HAPTICS.md`. ## Linux setup No WirePlumber config is required. Set the DualSense Direct sink's haptic channels to 100% once: ```sh pactl set-sink-volume alsa_output.usb-Sony_Interactive_Entertainment_DualSense_Wireless_Controller-00.Direct__Direct__sink 40% 40% 100% 100% ``` `make install-wireplumber` installs 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](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
safijari/openpuck#251
No description provided.