I need to play audio without interrupting an existing playlist. My goal is to play an audio over the current audio service that is going on the phone, ideally I would like to duck the audio and play my stream over it. Pretty much like GPS services do while driving (Waze, Google maps).
Flutter: How to configure audio_session to duck others
1.9k views Asked by Paulo Briceño Gonzalez At
2
There are 2 answers
0
On
Here is what I did:
packages:
- audio_session
- just_audio
- audio_service
In main.dart:
Define this method anywhere, please keep an eye on the
Future initAudioService() async {
//INITIALIZE audio_service
return
audioHandler = await audio_service.AudioService.init(
///This will be the custom Audio Service we use
builder: () => MyCustomAudioService(),
config: const audio_service.AudioServiceConfig(
androidNotificationChannelId: 'com.optimized.strength.audio',
androidNotificationChannelName: 'Audio Service',
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
),
);
}
Then for the Custom Audio Service We defined it below:
NOTE: I didn't need to use the broadcasting or any other function except the play sound, so you can directly copy paste the below part and tweak/adjust it as you please.
class MyCustomAudioService extends BaseAudioHandler with SeekHandler {
final AudioPlayer _player = AudioPlayer();
MyCustomAudioService() {
_init();
}
Future<void> _init() async {
try {
// Load the audio from assets
await _player.setAsset('assets/sounds/tick_sound.mp3');
// Listen to changes in player state and update the UI accordingly
_player.playerStateStream.listen((playerState) {
final playing = playerState.playing;
final processingState = playerState.processingState;
if (processingState == ProcessingState.completed) {
// Handle completion of audio playback
stop(); // Or any other logic you want to implement on completion
} else if (playing) {
_broadcastState(playing: true);
} else {
_broadcastState(playing: false);
}
}, onError: (Object e, StackTrace stackTrace) {
print("Error listening to playerStateStream: $e");
_broadcastState(playing: false, error: e.toString());
});
} catch (e) {
print("Error initializing player: $e");
_broadcastState(playing: false, error: e.toString());
}
}
void _broadcastState({bool playing = false, String? error}) {
final state = AudioProcessingState.idle;
playbackState.add(playbackState.value.copyWith(
playing: playing,
processingState: error == null ? state : AudioProcessingState.error,
// error: error,
));
}
@override
Future<void> play() async {
try {
final _session = await audio_session.AudioSession.instance;
//audio_session DUCK OTHERS CONFIGURATION
await _session.configure(const audio_session.AudioSessionConfiguration(
avAudioSessionCategory: audio_session.AVAudioSessionCategory.playback,
avAudioSessionCategoryOptions:
audio_session.AVAudioSessionCategoryOptions.duckOthers,
avAudioSessionMode: audio_session.AVAudioSessionMode.defaultMode,
avAudioSessionRouteSharingPolicy:
audio_session.AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions:
audio_session.AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: audio_session.AndroidAudioAttributes(
contentType: audio_session.AndroidAudioContentType.music,
flags: audio_session.AndroidAudioFlags.none,
usage: audio_session.AndroidAudioUsage.media,
),
androidAudioFocusGainType:
audio_session.AndroidAudioFocusGainType.gainTransientMayDuck,
androidWillPauseWhenDucked: true,
));
_session.setActive(true);
print('played inside the service');
// Set the source every time play is called to ensure it can be replayed
await _player.setAsset('assets/sounds/tick_sound.mp3');
await _player.play();
_session.setActive(false);
} catch (e) {
print("Error playing audio: $e");
}
}
@override
Future<void> pause() async {
_player.pause();
}
@override
Future<void> seek(Duration position) async {
_player.seek(position);
}
@override
Future<void> stop() async {
await _player.stop();
await _player.dispose();
super.stop();
}
}
Finally in the UI
InkWell(
onTap: () {
MyCustomAudioService().play();
}
Steps:
Dependencies:
Example:
import 'package:audio_service/audio_service.dart'; import 'package:audio_session/audio_session.dart'; import 'audio_player_service.dart'; Future initAudioService() async { //audio_session INSTANCE final session = await AudioSession.instance; //audio_session DUCK OTHERS CONFIGURATION await session.configure(const AudioSessionConfiguration( avAudioSessionCategory: AVAudioSessionCategory.playback, avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.duckOthers, avAudioSessionMode: AVAudioSessionMode.defaultMode, avAudioSessionRouteSharingPolicy: AVAudioSessionRouteSharingPolicy.defaultPolicy, avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none, androidAudioAttributes: AndroidAudioAttributes( contentType: AndroidAudioContentType.music, flags: AndroidAudioFlags.none, usage: AndroidAudioUsage.media, ), androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransientMayDuck, androidWillPauseWhenDucked: true, )); //INITIALIZE audio_service return await AudioService.init( builder: () => AudioPlayerService(), config: const AudioServiceConfig( androidNotificationChannelId: 'com.YOUR_COMPANY.YOUR_APP_NAME.audio', androidNotificationChannelName: 'Audio Service Demo', androidNotificationOngoing: true, androidStopForegroundOnPause: true, ), ); } //MAIN void main() async { await initAudioService(); runApp(const MaterialApp(home: MyApp())); }