ORPHE TRACKで発生していた問題
ORPHE TRACK で音声(効果音・TTS・動画音声など)を再生すると、
Spotify や Apple Music などの音楽アプリが停止してしまう問題がありました。
ランニングアプリでは
- 音楽はそのまま
- フィードバック音声だけ重ねて再生
という UX が求められるため、
この現象は実用上大きな課題でした。
最初のアプローチ:audio_session を使って競合を制御した
まずは audio_session を使って、
OS の「音声セッション」そのものを適切に設定しようとしました。
audio_session は
-
音声ポリシー
-
他アプリとの優先度
-
再生の扱い
などを一元的に設定できる便利なライブラリです。
しかし実際には……
問題:audioplayers だけ競合を解消できなかった
audio_session の設定を適用しても、
audioplayers のみ他アプリの音楽を止めてしまう挙動が残りました。
競合の流れ(問題状況)
+---------------------------+
| 他アプリ:音楽再生中 |
+-------------+-------------+
|
v
+---------------------------+
| ORPHE TRACK:音声再生 |
+-------------+-------------+
|
v
+---------------------------+
| audio_session 設定を適用 |
| → video_player はOK |
| → flutter_tts もOK |
| → audioplayers がNG |
| (音楽アプリが停止) |
+---------------------------+
最終アプローチ:各ライブラリに用意された「競合設定」を直接使う方針へ
試行錯誤した結果、
“audio_session だけに依存せず、ライブラリごとの競合設定を直接調整する”
という方式が最も安定しました。
Flutter の音声関連ライブラリは
OSレベルの音声セッション制御を、それぞれ独自にラップしているため、
-
まとめて統一制御する
よりも
-
各ライブラリの公式 API で明示的に設定する
ほうが確実に動作します。
具体的に適用した設定
video_player(mixWithOthers)
VideoPlayerController.asset(
theme.asset.videos.logo.path,
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
);
mixWithOthers: true
→ 他アプリの音声を止めず、自分の音声を重ねる
audioplayers(duckOthers)
await audioPlayer.play(
AssetSource(theme.asset.sounds.countDownStart.path),
volume: 1.0,
ctx: AudioContext(
iOS: AudioContextIOS(
options: {AVAudioSessionOptions.duckOthers},
),
android: AudioContextAndroid(
audioFocus: AndroidAudioFocus.gainTransientMayDuck,
),
),
);
duckOthers
→ 他アプリの音楽の音量を一時的に下げる(ducking)
→ 完全停止はしない
flutter_tts(AudioSessionConfiguration.speech)
final session = await AudioSession.instance;
await session.configure(
AudioSessionConfiguration.speech().copyWith(
avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.duckOthers,
androidAudioFocusGainType:
AndroidAudioFocusGainType.gainTransientMayDuck,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
usage: AndroidAudioUsage.assistanceAccessibility,
),
androidWillPauseWhenDucked: false,
),
);
TTSは OS の音声セッション干渉が強いため、
speech() ベースで duckOthers に寄せるのが最も安定。
最終的な競合解消フロー
+-----------------------------------------+
| ORPHE TRACK:音声再生要求 |
+-------------------------+---------------+
|
v
+-----------------------------------------+
| audio_session で基礎設定 |
| しかし一部ライブラリは制御しきれない |
+-------------------------+---------------+
|
v
+-----------------------------------------+
| ライブラリごとに競合設定を適用 |
| - video_player: mixWithOthers |
| - audioplayers: duckOthers |
| - flutter_tts: speech() + duckOthers |
+-------------------------+---------------+
|
v
+-----------------------------------------+
| 結果 |
| - 音楽アプリは停止しない |
| - ORPHE TRACK の音声だけ重ねられる |
+-----------------------------------------+
なぜこの構成に落ち着いたか
- Flutterの音声ライブラリは “音声セッションの扱い” が個別に実装されている
- そのため「完全統一」よりも、「ケースごとに最適化」のほうが安定
- 特に audioplayers は OS の音声フォーカス処理の影響を受けやすい
結果として、
ライブラリ別に公式APIで競合設定を明示することで、
最も期待通りの挙動になりました。
記事3のまとめ
-
ORPHE TRACK では音声再生時に音楽アプリが止まる問題が発生した
-
最初は audio_session による統合制御を試したが、
audioplayers だけ期待通りに競合を解消できなかった
-
最終的には、video_player / audioplayers / flutter_tts の
各ライブラリが持つ公式競合設定を直接使用する方針を採用
-
結果として、
-
音楽アプリを止めず
-
ORPHE TRACK の音声だけを安全に重ねられる
という動作が実現した
-