Skip to content

iOS: play the book alongside other apps' audio (mix modes, book level, lock screen card) - #446

Open
soumojitdas wants to merge 2 commits into
pounat:mainfrom
soumojitdas:ios-mix-audio
Open

soumojitdas wants to merge 2 commits into
pounat:mainfrom
soumojitdas:ios-mix-audio

Conversation

@soumojitdas

Copy link
Copy Markdown

An iOS option to play the book over another app's audio instead of pausing it: classical music in YouTube under an audiobook, say. It is off by default, so nothing changes unless someone switches it on.

What it adds

  • Settings → Media Controls → Mix with other audio: Off / Auto / Always, plus "Lower other audio" (iOS ducking). Auto mixes only when another app is already playing as the book starts, so a book played on its own keeps the lock screen and headset controls; Always never pauses other apps.
  • A Mix button (iOS only, under More by default) with the same options and a book level slider, −30 to +12 dB. iOS has one volume for everything and no app can set another app's, so the balance is set from the book's side: cuts scale the player volume (as a multiplier, so the sleep timer's fade keeps working) and boosts go through the existing processing tap behind a peak limiter.
  • A lock screen / Dynamic Island card and a Control Center control while mixing. iOS gives the system media controls only to a session that doesn't mix, so while mixing they belong to the other app. The book gets a Live Activity (cover, chapter, progress running at playback speed, skip and play/pause through the existing AudioPlaybackIntents) and an iOS 18 play/pause control.

How

  • AbsorbAudioMix (in the AbsorbPlayerCore package, so the widget intents can use it) holds the setting and makes the decision. Every place that sets the session category goes through its apply(): launch, AbsorbAudioEngine, IOSQueueAdvancer, the native core, the intents and NowPlayingPrimer. Mixing needs .mixWithOthers, and Apple allows no category options under .longFormAudio, so a mixing session uses the default route policy.
  • Dart asks native for the decision before its audio_session configure, and every setActive(true) now goes through _activateAudioSession(), which has native apply the decision first. Without that, resuming a book that last played alone re-activated the exclusive category and stopped the other app.
  • NowPlayingPrimer skips its claim while mixing: a mixing session can't hold Now Playing, and with ducking on the blip would leave the other app turned down. With ducking on, the engine also deactivates the session on pause so the other app gets its volume back.
  • The Live Activity exists only while the session mixes. BookLiveActivityController starts it from the foreground (iOS only lets an app start one there), updates it from the same app-group keys the home screen widgets read plus the engine's state, and only pushes an update on a visible change, since the progress bar runs on its own.

Testing

iPhone 14 Pro on iOS 27.0.1 with YouTube and Audiobookshelf 2.37.1, built with Xcode 27.1 and Flutter 3.47.7 and installed under a different bundle ID with free provisioning (so without CarPlay and Siri). The audio daemon's session log shows the book activating as a mixable session without interrupting YouTube, and resume keeping it mixable; the book level and the Live Activity were checked on the phone. Each commit builds on its own.

Android is untouched: the Dart changes sit behind Platform.isIOS and the Mix button is hidden there, but I couldn't run an Android build. The new strings are English-only, like the Android "Duck brief interruptions" toggle; happy to move them into the ARB files if you'd rather.

Two commits: mixing with the book level, then the Live Activity and control.

🤖 Generated with Claude Code

soumojitdas and others added 2 commits October 21, 2026 14:32
…to balance the two

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l Center while it mixes with another app

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