Skip to content

feat(lib): add support for the Soundcore Liberty 4 (A3953) - #384

Open
SeanathanVT wants to merge 3 commits into
Oppzippy:masterfrom
SeanathanVT:feature/device-support-a3953-soundcore-liberty-4
Open

SeanathanVT wants to merge 3 commits into
Oppzippy:masterfrom
SeanathanVT:feature/device-support-a3953-soundcore-liberty-4

Conversation

@SeanathanVT

@SeanathanVT SeanathanVT commented Sep 25, 2026 •

Copy link
Copy Markdown

AI disclosure

Written using Claude Code (Sonnet 5) and OpenCode (Qwen 3.8 27B), with review and real-hardware testing by me throughout. Reverse-engineering of the A3953 protocol was AI-assisted (decompiled APK analysis, cross-checked against real captures from my own Liberty 4 and Life P3).

Summary

  • New device support for the Soundcore Liberty 4 (A3953): sound modes, wear detection, LDAC, auto power off, wearing tone, case battery, press sensitivity, button configuration (single/double/triple press, read/write), ambient-sound-mode-cycle (read/write), low battery prompt, ambient sound prompt, spatial audio, custom equalizer with Hear ID, dual connections (multi-device pairing list), and TWS status/host device.
  • Split out of Add macOS support #372 (macOS support) at your request. The two PRs don't depend on each other.

Feature parity with the Liberty 4 family

Checked against this project's other two Liberty 4 devices (A3947 Liberty 4 NC, A3954 Liberty 4 Pro) by comparing actual builder wiring, not assumptions. Where A3953 lacks something, checked whether the byte even exists in its state packet: if it doesn't, the device genuinely doesn't have that feature, it's not unimplemented.

Feature A3947 (Liberty 4 NC) A3953 (Liberty 4) A3954 (Liberty 4 Pro)
Sound modes ✅ ✅ ✅
Equalizer + Hear ID ✅ ✅ ✅
Button config (read/write) ✅ ✅¹ ✅
Ambient sound mode cycle ❌ ✅ ✅
Dual connections ❌ ✅ ✅
Spatial audio ❌ ✅ ✅
Press sensitivity ❌ ✅ ❌
Ambient sound prompt ❌ ✅ ❌
LDAC ❌ ✅ ✅
Wearing detection ❌ ✅ ✅
Wearing tone ✅ ✅ ❌
Low battery prompt ✅ ✅ ✅
Auto power off ✅ ✅ ✅
TWS status ✅ ✅ ✅
Limit high volume ✅ ❌² ✅
Touch tone ✅ ❌² ❌
Gaming mode ✅ ❌² ❌
Sound leak compensation ✅ ❌² ✅
Surround sound ✅ ❌² ❌
Auto play/pause ✅ ❌² ❌
Touch lock ✅ ❌² ❌
Case-specific features (language, own serial/firmware, air pressure, easy chat) ❌ ❌ ✅³

¹ Single, double, and triple press confirmed on real hardware. No long press, see below.
² No corresponding byte anywhere in A3953StateUpdatePacket. Every byte in that packet is accounted for, so this isn't missing work, the hardware doesn't have it.
³ A3954 has a smart case with its own firmware/serial and sensors; A3947 and A3953 don't have that hardware.

Non-obvious decisions

Fixed a bug in the shared dual connections parser, not just added A3953 support. A trailing device entry in the paired-device list can be padded a few bytes short of what its own length byte says. The parser errored on this instead of decoding it. Fixed by clamping instead. This affects every device using common::modules::dual_connections, not just the A3953.

Long press isn't configurable. The official app only offers single, double, and triple press for the A3953 (it skips long press when building its press-kind tabs, and ignores long-press changes when deciding whether to save), and writing a long press action has no effect on real hardware. Its bytes are still in the state update packet between double and triple press, so the A3953's own parser skips over them. Nothing in the shared button_configuration module changed.

Hear ID is read and written back unchanged, not exposed as a setting. Same as this project's existing A3947 and A3955 handling. Equalizer writes always turn Hear ID off so the equalizer values actually take effect.

device_colour (one byte) is read and written back but not shown as a setting. It's a single character, not a number. Confirmed '2' means Cloud White from a real capture; the other retail colors are unknown. Left unexposed either way, since no other device in this project shows its case color.

Verification

Reverse-engineered against the official app's decompiled source, then cross-checked against real hardware captures for the state update packet, equalizer writes (including the Hear ID round-trip), the dual connections device list, ambient-sound-mode-cycle writes, and button config writes (confirmed that changing one button's action only affects that button). Hardware testing was done through the macOS backend from #372; nothing in this PR is backend-specific. cargo test, cargo clippy, and cargo fmt --check pass.

Screenshots (macOS)

Screenshot 2026-09-14 at 12 34 46 Screenshot 2026-09-14 at 12 34 56 Screenshot 2026-09-14 at 12 35 00 Screenshot 2026-09-14 at 12 35 05 Screenshot 2026-09-14 at 12 35 10 Screenshot 2026-09-14 at 12 35 13 Screenshot 2026-09-14 at 12 35 17

- Sound modes, equalizer with Hear ID round-trip, button configuration
  and ambient-sound-mode-cycle (read/write), dual connections, spatial
  audio, press sensitivity, ambient sound prompt, low battery prompt,
  wear detection, wearing tone, LDAC, auto power off, case battery,
  and TWS status.
- Hear ID and device_colour are written back unchanged, not exposed
  as settings, matching a3947/a3955.
- Fix the shared dual connections parser: a trailing device entry can
  be padded short of its own length byte, so clamp to the remaining
  input instead of erroring. Affects every device using
  common::modules::dual_connections.

Co-Authored-By: Claude <[email protected]>
@SeanathanVT SeanathanVT mentioned this pull request Sep 25, 2026
@Oppzippy

Copy link
Copy Markdown
Owner

Looks good for the most part. Does configuring the long press actions work despite the Soundcore app only listing single, double, and triple press? I wasn't sure if long press is listed because it actually works or if it's listed due to common::modules::button_configuration not having a good way of hiding those settings.

@SeanathanVT

Copy link
Copy Markdown
Author

Looks good for the most part. Does configuring the long press actions work despite the Soundcore app only listing single, double, and triple press? I wasn't sure if long press is listed because it actually works or if it's listed due to common::modules::button_configuration not having a good way of hiding those settings.

I'm sorry for the delay -- I will test this today!

SeanathanVT and others added 2 commits October 6, 2026 11:46
- The official app hides long press for the A3953, and writing it has
  no effect on real hardware.
- Skip its bytes in the state update packet so triple press still
  parses from the right offset.
- Double and triple press are now confirmed on real hardware.

Co-Authored-By: Claude <[email protected]>
- Assert all six button actions in the real capture test, so a wrong
  long press skip shows up as a triple press mismatch.
- Note the byte math behind the button split on serialization.

Co-Authored-By: Claude <[email protected]>
@SeanathanVT

Copy link
Copy Markdown
Author

Confirmed that long-press does not in fact work. Also tested the other stuff I didn't test before (double and triple press), and that works without issue. Updated the code and PR text accordingly.

sanjaymalladi added a commit to sanjaymalladi/OpenSCQ30 that referenced this pull request Oct 13, 2026
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.

Add support for Soundcore Liberty 4 (A3953)

2 participants