Skip to content

Play announcements on a separate player so music is ducked, not replaced - #67

Open
baileyboy0304 wants to merge 1 commit into
msp1974:devfrom
baileyboy0304:fix/announce-separate-player
Open

baileyboy0304 wants to merge 1 commit into
msp1974:devfrom
baileyboy0304:fix/announce-separate-player

Conversation

@baileyboy0304

@baileyboy0304 baileyboy0304 commented Oct 2, 2026 •

Copy link
Copy Markdown

Problem

media_player.play_media with announce: true stops music instead of ducking it. Seen with Music Assistant playing on the VACA media player and an announcement sent to the same entity: the music stops, and Music Assistant's timeline keeps running silently until the music is paused and played again.

Two causes:

  1. The announcement replaces the music. The integration doesn't forward announce (fixed in Forward announce flag to the device in play_media ViewAssist_Companion_App#329), and every play-media action goes to MusicPlayerService.play(url), which swaps the current media item for the TTS clip.
  2. The HA TTS WAV never reports ended. HA streams TTS WAVs over chunked HTTP with placeholder 0xFFFFFFFF RIFF and data lengths. ExoPlayer reads that as about 12 h of audio and stays in STATE_READY after the speech ends. Example: a 1.46 s clip declared data size 0xffffffff with 140,436 actual bytes.

Changes

  • AnnouncementPlayer (new): plays play-media with announce: true on its own ExoPlayer.
    • Uses the same audio attributes as the voice reply player (USAGE_NOTIFICATION / CONTENT_TYPE_SPEECH).
    • Holds AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK only while speaking, so the music player's existing focus listener ducks and restores the music. Music source, position and play state are untouched, and paused music stays paused.
    • Downloads the clip, corrects the WAV lengths and plays it from a cache file. If the download fails or is over 20 MB, it streams the URL instead.
    • A watchdog releases audio focus 1.5 s after the known duration if STATE_ENDED never arrives.
    • Releases focus on completion, error, stop() and when a newer announcement replaces it.
  • Satellite.handleMediaPlayerAction: reads announce from the payload and sends the URL to the announcement player or the music player. Without the flag, behaviour is unchanged.
  • MusicPlayerService:
    • setVolume() no longer discards volume changes received while ducked. Previously, a volume restore sent during speech (e.g. after View Assist's listening duck) was lost and music came back at the old ducked level. The new value is now stored and applied when the duck ends.
    • The ducked flag is cleared when no un-duck animation is needed (volume already at its target), so it no longer stays set and blocks later volume changes.
    • The un-duck animation stops if the music is ducked again and finishes on the latest target volume.
  • SatelliteMediaManager: owns the announcement player and stops it in stopAll().

Testing

Tested on a Facebook Portal+ (Android 10) with Music Assistant playing to the VACA media player. An Echo Dot forwards Assist TTS URLs with announce: true, with View Assist ducking and a volume restore after the reply:

  • The music ducks when the announcement arrives and keeps its stream and position.
  • The WAV lengths are corrected and the player reaches STATE_ENDED (the watchdog did not fire).
  • Audio focus is abandoned and the music un-ducks.
  • A volume restore received during the speech (72%) is applied after the un-duck.

Not yet tested: music paused before an announcement, failed or cancelled announcements, back-to-back announcements, and announce: false regression.

Note

On the Portal+, Android ties STREAM_NOTIFICATION to STREAM_MUSIC, so the Voice and Music volume settings move together. That happens without this change too and isn't addressed here.

🤖 Generated with Claude Code

…replaced

play-media with announce=true is now played by a new AnnouncementPlayer
instead of MusicPlayerService, so the music source, position and play
state are kept. It holds transient may-duck audio focus only while
speaking; the music player ducks and restores via its focus listener.

HA streams TTS WAVs with placeholder 0xFFFFFFFF RIFF/data lengths over
chunked HTTP, which ExoPlayer treats as ~12h and never reaches
STATE_ENDED. Announcements are downloaded, the WAV lengths corrected and
played from a cache file, with a watchdog after the known duration.

MusicPlayerService: volume changes received while ducked are no longer
discarded (e.g. a volume restore sent during speech), and the ducked
flag is cleared when no un-duck animation is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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