Skip to content

ULT WEAR: Bass Control (ULT mode + Clear Bass), EQ slot fix, product image - #35

Open
abhishekchotaliya wants to merge 2 commits into
AmitRajput-Dev:mainfrom
abhishekchotaliya:feat/ult-mode
Open

abhishekchotaliya wants to merge 2 commits into
AmitRajput-Dev:mainfrom
abhishekchotaliya:feat/ult-mode

Conversation

@abhishekchotaliya

@abhishekchotaliya abhishekchotaliya commented Nov 9, 2026 •

Copy link
Copy Markdown

Summary

Adds Sony's Bass Control for the ULT WEAR (WH-ULT900N): the ULT mode the headset's ULT button cycles (Off / ULT 1 / ULT 2) plus the Clear Bass slider, in the same place Sony's app shows them. Adds the ULT WEAR product image too.

 <ContentView> secondary cards
   devicesCard
+  bassControlCard        # ULT WEAR only: Off | ULT 1 | ULT 2 + Clear Bass -10..+10
   equalizerCard          # on ULT WEAR: 5 bands only (Clear Bass moved up)
   dseeCard
 <QuickControlsView>
+  ultRow                 # ULT picker in the menu-bar panel
   equalizerRow
 CrossPlatformGUI (Windows/Linux)
+  _drawUltMode()         # same card in ImGui
   _drawEqualizer()

Protocol: EQ inquired type 0x03 (PRESET_EQ_AND_ULT_MODE, framing from mos9527/SonyHeadphonesClient's Sound Connect codegen):

GET 56 03 -> RET 57 03 <preset> <ult 00|01|02> 06 <clear bass> <400> <1k> <2.5k> <6.3k> <16k>   (each +10)
SET 58 03 <preset> <ult> <count> <bands>

Testing on real hardware also showed that the ULT WEAR's 56/58 00 EQ slots are shifted by one: the slider labelled 400 drove Clear Bass, the Bass slider did nothing, and 16k was unreachable. On this model the EQ is now read and written through 56/58 03.

ULT WEAR only. One gate, same pattern as _isWh1000Xm6():

Headphones
  _isUltWear()                 # v2 + name "Sony ULT WEAR" / "ULT WEAR" / "WH-ULT900N"
  _readUltModeParam()          # only sender of 56 03, returns early unless _isUltWear()
  requestEqualizer()
+   if _isUltWear() && _requestUltEqualizer(): return
  setEqualizerCustom()
+   if _isUltWear() && hasUltClearBass(): _setUltEqualizerCustom(); return
  setUltMode() / setClearBass()  # need an answered 56 03; re-read it, echo preset/ULT/bands, change one byte

The 2 s poll follows the physical ULT button and Clear Bass changes made in the phone app. "Copy diagnostics" now includes the raw 56 03 exchange.

Evidence

  • Hardware, ULT WEAR fw 2.1.0: reply captured 57 03 a0 01 06 11 0c 08 0b 0d 0f. Sony's app showed Clear Bass ≈ +6/+7 and 400…16k = +2 −2 +1 +3 +5, which matches the clear-bass-first decode. The old path had shown 400…16k = +7 +2 −2 +1 +3 with Bass 0. After the change: ULT 1/2/Off switch audibly, the ULT button is reflected in the app, Clear Bass changes bass punch, and the product image shows in the header. This was tested with a Command Line Tools build; Xcode itself wasn't used, so CI's xcodebuild is the first real Xcode build.
  • Tests (Client/tests/run-macos.sh, all passing), new cases in HeadphonesProtocolTests.cpp:
    • captured reply decodes as Clear Bass +7, then +2 −2 +1 +3 +5; a "Sony ULT WEAR" name makes the first EQ read use 56 03, never 56 00
    • custom EQ → 58 03 a0 <ult> 06 <cb> <bands>; ULT / Clear Bass writes re-read first and echo everything else verbatim
    • truncated or unknown replies → no write; transport failure → original exception (keeps shouldDisconnect)
    • WH-1000XM5, WH-CH720N, WF-1000XM5 and an unnamed headset, each faked to answer 56 03: never sent 56 03/58 03, no ULT/Clear Bass control, custom EQ still 58 00 a0 06 <cb> <bands>
    • v1 headsets send nothing new
  • Not confirmed on hardware: Clear Bass / ULT changes while a non-Manual preset such as Bright is selected. The code sends the headset's own preset byte back unchanged, and the slider re-syncs from the headset within 2 s.
Screenshot 2026-11-09 at 4 51 48 PM Screenshot 2026-11-09 at 4 52 12 PM Screenshot 2026-11-09 at 4 52 29 PM

Merge Danger

Door: two-way

Pure client feature: reverting the PR restores the old behaviour, and no persisted app state changes.

Blast Radius: ULT-only

Other models' EQ, DSEE and multipoint code paths are unchanged: their only shared-code change is a one-line ULT gate that is false for them, and tests cover it. Two things on the ULT WEAR: (1) its Manual EQ sliders now show the correct bands, so they'll read differently from before; (2) the ULT controls depend on the Bluetooth name containing "ULT WEAR" / "ULT900", so a renamed headset falls back to the old EQ path. The XM6 equalizer and the product images already depend on the name in the same way.

🤖 Generated with Claude Code

abhishekchotaliya and others added 2 commits November 9, 2026 16:45
Adds Sound Connect's "Bass Control" for the WH-ULT900N: the ULT mode the
headset's ULT button cycles (Off / ULT 1 / ULT 2) and the Clear Bass slider,
in a new card above the Equalizer (macOS window, menu-bar panel, ImGui GUI).

Protocol: EQ inquired type 0x03 (PRESET_EQ_AND_ULT_MODE, framing from
mos9527/SonyHeadphonesClient's Sound Connect codegen):
  GET 56 03 -> RET 57 03 <preset> <ult> 06 <clear bass> <400..16k>  (+10 bias)
  SET 58 03 <preset> <ult> <count> <bands>

Verified on a ULT WEAR (fw 2.1.0). It also showed that this model's 56/58 00
equalizer slots are shifted by one: the slider labelled 400 drove Clear Bass,
the Bass slider did nothing and 16k was unreachable. On the ULT WEAR the
equalizer is now read and written through 56/58 03 instead.

ULT WEAR only: _isUltWear() (v2 + model name, like _isWh1000Xm6()) gates the
only sender of 56 03, every 58 03 write needs an answered 56 03 first, and
the shared EQ functions just hand off to the ULT helpers. Other models never
see the inquiry and keep the 56/58 00 equalizer unchanged (covered by tests).

ULT and Clear Bass writes re-read 56 03 first and send the headset's own
preset, ULT mode and bands back unchanged, so they never revert an EQ change
made elsewhere. The 2 s poll follows the physical ULT button and Clear Bass
changes made in the phone app. Copy diagnostics includes the raw 56 03
exchange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Sony's official Off White ULT WEAR render (from the sony.co.uk product
page's scene7 asset Product-Intro-white_standard_M1, transparent PNG),
trimmed to its content and scaled to 285x435 to match the other renders.

The headset advertises itself as "Sony ULT WEAR", not by model number, so
both GUIs map names containing "ULT WEAR" or "ULT900" to wh-ult900n.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant