bluefireteam / audioplayers

A Flutter package to play multiple audio files simultaneously (Android/iOS/web/Linux/Windows/macOS)
https://pub.dartlang.org/packages/audioplayers
MIT License
2.01k stars 845 forks source link

AudioPlayer replay. When audio1 pause. And start audio2 on another screen. #1793

Closed catalunha closed 6 months ago

catalunha commented 6 months ago

Checklist

Current bug behaviour

AudioPlayers works well when I have two audios on the same screen.

But when I have audio1 on page1. And audio2 in a bottomSheet. Generates the bug.

bug behavior: Open page1 with audio1. Play audio1 Pause audio1 On page1 open bottomSheet with audio2 Play audio2 Audio1 starts playing along with audio2.

Expected behaviour

Open page1 with audio1. Play audio1 Pause audio1 On page1 open bottomSheet with audio2 Play audio2 Pause audio2 (audio1 is paused) Close bottomSheet. Play audio1 Pause audio1

Steps to reproduce

  1. Execute flutter run on the code sample
  2. ...
  3. ...

Code sample

Code for play audio1 - part a ```dart class AudioInsidePageWidget extends StatefulWidget { const AudioInsidePageWidget({ super.key, required this.audioPlayer, }); final AudioPlayer audioPlayer; @override State createState() => _AudioInsidePageWidgetState(); } class _AudioInsidePageWidgetState extends State { @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { for (final PronunciationSvcModel element in state.getVersesResponse?.audioFiles ?? []) { if (element.isDefault != null && element.isDefault!) { widget.audioPlayer.setSourceUrl( element.filePath!, ); widget.audioPlayer.setReleaseMode(ReleaseMode.stop); return PlayerInsideContainerWidget(player: widget.audioPlayer); } } return const SizedBox.shrink(); }, ); } } ```
Code for play audio1 - part b ```dart import 'dart:async'; import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:biblingo_mobile/theme/extra_colors.dart'; class PlayerInsideContainerWidget extends StatefulWidget { final AudioPlayer player; const PlayerInsideContainerWidget({ required this.player, super.key, }); @override State createState() { return _PlayerInsideContainerWidgetState(); } } class _PlayerInsideContainerWidgetState extends State with WidgetsBindingObserver { PlayerState? _playerState; Duration? _duration = Duration.zero; Duration? _position = Duration.zero; StreamSubscription? _durationSubscription; StreamSubscription? _positionSubscription; StreamSubscription? _playerCompleteSubscription; StreamSubscription? _playerStateChangeSubscription; bool get _isPlaying => _playerState == PlayerState.playing; bool get _isPaused => _playerState == PlayerState.paused; String get _durationText => _duration?.toString().split('.').first ?? '0:00:00'; String get _positionText => _position?.toString().split('.').first ?? '0:00:00'; AudioPlayer get player => widget.player; double _speed = 1.00; @override void initState() { super.initState(); // Use initial values from player _playerState = player.state; player.getDuration().then( (value) => setState(() { _duration = value; }), ); player.getCurrentPosition().then( (value) => setState(() { _position = value; }), ); _initStreams(); WidgetsBinding.instance.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { log('*** *** AppLifecycleState :$state'); if (AppLifecycleState.resumed != state) { log('--- --- AppLifecycleState :$state'); _pause(); } super.didChangeAppLifecycleState(state); } @override void dispose() { _durationSubscription?.cancel(); _positionSubscription?.cancel(); _playerCompleteSubscription?.cancel(); _playerStateChangeSubscription?.cancel(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void setState(VoidCallback fn) { if (mounted) { super.setState(fn); } } @override Widget build(BuildContext context) { // if (_duration == null) { // return const Center(child: LoadingLinearWidget()); // } return Container( decoration: BoxDecoration( color: Theme.of(context).extension()?.surfaceContainer, borderRadius: BorderRadius.circular(17.0), ), child: Padding( padding: const EdgeInsets.only(left: 10, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( // mainAxisAlignment: MainAxisAlignment.end, // crossAxisAlignment: CrossAxisAlignment.end, // crossAxisAlignment: WrapCrossAlignment.center, children: [ Visibility( visible: _isPlaying ? false : true, child: InkWell( onTap: _isPlaying ? null : _play, child: const Icon(Symbols.play_arrow_rounded), ), ), Visibility( visible: _isPlaying ? true : false, child: InkWell( onTap: _isPlaying ? _pause : null, child: const Icon(Symbols.pause_rounded), ), ), Flexible( child: Stack( alignment: AlignmentDirectional.bottomEnd, children: [ Slider( onChanged: (value) { final duration = _duration; if (duration == null) { return; } final position = value * duration.inMilliseconds; player.seek(Duration(milliseconds: position.round())); }, value: (_position != null && _duration != null && _position!.inMilliseconds > 0 && _position!.inMilliseconds < _duration!.inMilliseconds) ? _position!.inMilliseconds / _duration!.inMilliseconds : 0.0, ), Row( mainAxisSize: MainAxisSize.max, // crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: [ Text( '$_positionText / $_durationText', style: Theme.of(context).textTheme.labelMedium, ), const SizedBox( width: 25, ), ], ), ], ), ), Container( decoration: BoxDecoration( color: Theme.of(context) .extension() ?.surfaceContainerHighest, borderRadius: BorderRadius.circular(7.0), ), child: Padding( padding: const EdgeInsets.only(left: 10, right: 10), child: Row( children: [ Visibility( visible: player.playbackRate == 1.0 ? true : false, child: InkWell( onTap: () async { setState(() { _speed = 0.5; }); await player.setPlaybackRate(_speed); }, child: const Text('1.0x'), ), ), Visibility( visible: player.playbackRate != 1.0 ? true : false, child: InkWell( onTap: () async { setState(() { _speed = 1; }); await player.setPlaybackRate(_speed); }, child: const Text('0.5x'), ), ), ], ), ), ), const SizedBox( width: 5, ), ], ), const SizedBox( height: 5, ), // Row( // mainAxisSize: MainAxisSize.max, // // crossAxisAlignment: CrossAxisAlignment.end, // mainAxisAlignment: MainAxisAlignment.end, // children: [ // Text( // '$_positionText / $_durationText', // style: Theme.of(context).textTheme.labelMedium, // ), // const SizedBox( // width: 90, // ), // ], // ), ], ), ), ); } void _initStreams() { _durationSubscription = player.onDurationChanged.listen((duration) { setState(() => _duration = duration); }); _positionSubscription = player.onPositionChanged.listen( (p) => setState(() => _position = p), ); _playerCompleteSubscription = player.onPlayerComplete.listen((event) { setState(() { _playerState = PlayerState.stopped; _position = Duration.zero; }); }); _playerStateChangeSubscription = player.onPlayerStateChanged.listen((state) { setState(() { _playerState = state; }); }); } Future _play() async { await player.resume(); setState(() => _playerState = PlayerState.playing); } Future _pause() async { await player.pause(); setState(() => _playerState = PlayerState.paused); } Future _stop() async { await player.stop(); setState(() { _playerState = PlayerState.stopped; _position = Duration.zero; }); } } ```
Code for play audio2 in bottomSheet ```dart void tryAudioPlay({ required AudioPlayer audioPlayer, required String? audioPath, BuildContext? context, bool shouldNotifyIfMissing = false, }) { try { if (isAudioLoaded(audioPlayer)) { if (audioPlayer.state == PlayerState.playing) { audioPlayer.stop().then((_) => audioPlayer.resume()); } else { audioPlayer.resume(); } } else if (audioPath != '' && audioPath != null && audioPath.isNotEmpty) { audioPlayer.setSourceUrl(audioPath).then((_) => audioPlayer.resume()); } else { throw Exception('No audio available'); } } catch (_) { if (shouldNotifyIfMissing && context != null) { scheduleSnackBarMessage( context, L10n.of(context, 'helpers.utils.noaudio'), ); } } } ```

Affected platforms

Android

Platform details

See in flutter doctor -v

AudioPlayers Version

audioplayers: ^6.0.0

Build mode

No response

Audio Files/URLs/Sources

Any audio file

Screenshots

page1 audio1 image

bottomSheet audio2 image

Logs

When play and pause audio 1 on page 1. ``` D/AudioManager(16118): dispatching onAudioFocusChange(-3) to android.media.AudioManager@2539b09xyz.luan.audioplayers.player.FocusManager$$ExternalSyntheticLambda0@e81bd27 ```

Notice that the AudioPlayer appears to play a playlist in dispatching onAudioFocusChange...

When play audio2 in bottomSheet. ``` V/MediaPlayer(16118): resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false V/MediaPlayer(16118): cleanDrmObj: mDrmObj=null mDrmSessionId=null V/MediaHTTPService(16118): MediaHTTPService(android.media.MediaHTTPService@c66b3ca): Cookies: null V/MediaHTTPService(16118): makeHTTPConnection: CookieHandler (java.net.CookieManager@9816ad7) exists. V/MediaHTTPService(16118): makeHTTPConnection(android.media.MediaHTTPService@c66b3ca): cookieHandler: java.net.CookieManager@9816ad7 Cookies: null D/EGL_emulation(16118): app_time_stats: avg=2140.51ms min=128.28ms max=4152.74ms count=2 D/AudioManager(16118): dispatching onAudioFocusChange(-3) to android.media.AudioManager@2539b09xyz.luan.audioplayers.player.FocusManager$$ExternalSyntheticLambda0@1010335 D/EGL_emulation(16118): app_time_stats: avg=12.06ms min=7.58ms max=18.04ms count=49 V/MediaPlayer(16118): resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false V/MediaPlayer(16118): cleanDrmObj: mDrmObj=null mDrmSessionId=null V/MediaPlayer(16118): resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false V/MediaPlayer(16118): cleanDrmObj: mDrmObj=null mDrmSessionId=null D/AudioManager(16118): dispatching onAudioFocusChange(1) to android.media.AudioManager@2539b09xyz.luan.audioplayers.player.FocusManager$$ExternalSyntheticLambda0@1010335 D/EGL_emulation(16118): app_time_stats: avg=15.14ms min=7.64ms max=34.28ms count=43 ```
Flutter doctor: ``` catalunha@pop-os:~/heetoo/biblingo_mobile$ flutter doctor -v [✓] Flutter (Channel stable, 3.19.1, on Pop!_OS 22.04 LTS 6.8.0-76060800daily20240311-generic, locale en_US.UTF-8) • Flutter version 3.19.1 on channel stable at /home/catalunha/development/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision abb292a07e (9 weeks ago), 2024-02-20 14:35:05 -0800 • Engine revision 04817c99c9 • Dart version 3.3.0 • DevTools version 2.31.1 [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /home/catalunha/Android/Sdk • Platform android-34, build-tools 34.0.0 • ANDROID_HOME = /home/catalunha/Android/Sdk • Java binary at: /home/catalunha/development/android-studio/jbr/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) • All Android licenses accepted. [✓] Chrome - develop for the web • Chrome at google-chrome [✓] Linux toolchain - develop for Linux desktop • Ubuntu clang version 14.0.0-1ubuntu1.1 • cmake version 3.22.1 • ninja version 1.10.1 • pkg-config version 0.29.2 [✓] Android Studio (version 2023.1) • Android Studio at /home/catalunha/development/android-studio • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314) [✓] VS Code (version 1.88.1) • VS Code at /usr/share/code • Flutter extension version 3.82.0 [✓] Connected device (3 available) • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64 • Android 12 (API 31) (emulator) • Linux (desktop) • linux • linux-x64 • Pop!_OS 22.04 LTS 6.8.0-76060800daily20240311-generic • Chrome (web) • chrome • web-javascript • Google Chrome 124.0.6367.60 [✓] Network resources • All expected network resources are available. • No issues found! ```

Related issues / more information

1662

Working on PR

no way

catalunha commented 6 months ago

I just disabled // audioFocus: AndroidAudioFocus.gainTransientMayDuck, And problem was solved.

      AudioPlayer.global.setAudioContext(
        AudioContext(
          android: const AudioContextAndroid(
            stayAwake: false,
            contentType: AndroidContentType.speech,
            // audioFocus: AndroidAudioFocus.gainTransientMayDuck,
            usageType: AndroidUsageType.game,
          ),
          iOS: AudioContextIOS(
            category: AVAudioSessionCategory.playAndRecord,
            options: const {
              AVAudioSessionOptions.allowAirPlay,
              AVAudioSessionOptions.allowBluetooth,
              AVAudioSessionOptions.allowBluetoothA2DP,
              AVAudioSessionOptions.defaultToSpeaker,
              AVAudioSessionOptions.duckOthers,
            },
          ),
        ),
      );