Skip to content

fix: Music Haptics plays in a container (rebind outside the main executable) - #10

Closed
Ping-2o wants to merge 1 commit into
samj-exe:stable1from
Ping-2o:fix/music-haptics-container-rebind
Closed

Ping-2o wants to merge 1 commit into
samj-exe:stable1from
Ping-2o:fix/music-haptics-container-rebind

Conversation

@Ping-2o

@Ping-2o Ping-2o commented Oct 12, 2026

Copy link
Copy Markdown

What this fixes

Music Haptics did nothing under LiveContainer, while Vibrations > Controls worked fine. The device log made it exact:

music haptics: switch on, Taptic Engine available, Spotify's output untappable
audio pipeline: Spotify output import unavailable
music haptics: Spotify's output could not be tapped (rebind failed); Music Haptics will not play

The Taptic Engine was available and the switch was on — but Spotify's audio output was never tapped, so the analyzer had no sound to follow. This is the "permission issue" reported for containers: it is not a TCC permission, it is the container's process layout.

Root cause

SGRebindImport only ever scanned the main executable (filetype != MH_EXECUTE -> continue). A container runs the host app as the executable and loads Spotify as a dylib, so Spotify's import slot for AudioOutputUnitStart was never in scope and the rebind always failed. No audio tap -> no analysis -> no haptics.

Changes

  • Core/SGRebind.m — walk every loaded image for the symbol instead of the main executable alone, skipping only the mod's own dylibs. This also repairs the Shared/ConnectDiscovery sendto/sendmsg rebinds, which shared the same main-executable-only assumption.
  • Shared/Audio/SGAudioPipeline.x — install() no longer uses dispatch_once, which locked a failure in for the whole process when Spotify's image was not loaded yet; it now retries until the rebind succeeds.
  • Shared/Haptics/MusicHaptics.x — recover instead of staying dead:
    • switch state and player thread are set up before registration, so a failed registration no longer leaves the feature inert and silent;
    • a failed CHHapticEngine is discarded and rebuilt rather than kept forever;
    • the Taptic capability is re-checked rather than cached;
    • the engine falls back to one built without Spotify's audio session (a container's guest app does not own it);
    • registration is retried on the first foreground, and sg_active is seeded from applicationState.
  • Diagnostics/Diagnostics.x — one startup line stating which part is missing (switch … Taptic Engine … Spotify's output tapped|not tapped yet|untappable), since this feature previously had several silent ways to do nothing.

Verification

  • scripts/check-layers.sh passes (the Diagnostics addition declares its two symbols locally rather than importing across layers).
  • The tweak compiles and links clean for arm64.
  • Built and run on device under LiveContainer; the log now reports the output as tapped and Music Haptics plays along.

Only source files are touched — no IPA, .deb, or other build output.

…utable)

Music Haptics did nothing under LiveContainer while Vibrations > Controls
worked: the Taptic Engine was available and the switch was on, but Spotify's
audio output was never tapped, so the analyzer had no sound to follow.

SGRebindImport only ever scanned the main executable (filetype MH_EXECUTE).
A container runs the host app as the executable and loads Spotify as a dylib,
so Spotify's import slot for AudioOutputUnitStart was never in scope and the
rebind always failed ("audio pipeline: Spotify output import unavailable").
It now walks every loaded image, skipping only the mod's own dylibs, which
also repairs the ConnectDiscovery sendto/sendmsg rebinds that shared the
main-executable-only assumption.

On top of that, the pipeline no longer locks a failed install in for the
whole launch (dispatch_once -> retry until the rebind succeeds), and Music
Haptics recovers instead of staying dead: the switch state and player thread
are set up before registration, a failed CHHapticEngine is discarded and
rebuilt, the capability is re-checked rather than cached, the engine falls
back to one built without Spotify's audio session, and registration is retried
on the first foreground. A startup line now states which part is missing.
@samj-exe
samj-exe deleted the branch samj-exe:stable1 October 13, 2026 15:11
@samj-exe samj-exe closed this 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.

2 participants