Warm tip: This article is reproduced from serverfault.com, please click

Flutter just_audio loop mode is not looping

发布于 2020-11-29 06:39:23

I am using flutter audio_service (https://pub.dev/packages/audio_service) and just_audio (https://pub.dev/packages/just_audio) to play audio files in foreground and background.

I subclassed BackgroundAudioTask, added an instance of AudioPlayer, and override needed methods. For example I updated repeat mode:

@override
  Future<void> onSetRepeatMode(AudioServiceRepeatMode repeatMode) async {
    super.onSetRepeatMode(repeatMode);
    switch (repeatMode)
    {
      case  AudioServiceRepeatMode.all:
        _audioPlayer.setLoopMode(LoopMode.all);
        break;
      case  AudioServiceRepeatMode.none:
        _audioPlayer.setLoopMode(LoopMode.off);
        break;
      case AudioServiceRepeatMode.one:
        _audioPlayer.setLoopMode(LoopMode.one);
        break;
      case AudioServiceRepeatMode.group:
        _audioPlayer.setLoopMode(LoopMode.all);
        break;
    }
  }

@override
  Future<void> onPlayFromMediaId(String mediaId) async {
    await _audioPlayer.stop();
    // Get queue index by mediaId.
    _queueIndex = _queue.indexWhere((test) => test.id == mediaId);
    // Set url source to _audioPlayer.
    downloadAndPlay(_mediaItem);
  }

I am trying to add a playlist and have a loop of audio files, but seems something is missing and after playing track #1, the player seeks to start of same #1 track and plays it again. Here is my code (part of downloadAndPlay) :

var list=List<AudioSource>();

for (int t=0;t<_queue.length;t++)
  {
    var mi=_queue[t];
    var url = mi.extras['source'];
    list.add(AudioSource.uri(Uri.parse(url)));
  }

D("Loading list with ${list.length} items");
D("Seeking to index : $_queueIndex");
await _audioPlayer.load(ConcatenatingAudioSource(children: list),
    initialIndex: _queueIndex, initialPosition: Duration.zero);
AudioService.updateQueue(_queue);
AudioService.setRepeatMode(AudioServiceRepeatMode.all);
_audioPlayer.play();

I added this to check if ProcessingState.completed fired, which means it reached end of track, but it doesn't fire:

playerEventSubscription = _audioPlayer.playbackEventStream.listen((event) {
  D("audioPlayerTask: playbackEventStream : ${event.processingState}");
  switch (event.processingState) {
    case ProcessingState.ready:
      _setState(state: AudioProcessingState.ready);
      break;
    case ProcessingState.buffering:
      _setState(state: AudioProcessingState.buffering);
      break;
    case ProcessingState.completed:
      _handlePlaybackCompleted();
      break;
    default:
      break;
  }
});
Questioner
AVEbrahimi
Viewed
0
Ryan Heise 2020-12-01 16:12:55

You never invoked _audioPlayer.setLoopMode() so it won't loop.

You are also using audio_service but your audio code appears to be half inside and half outside audio_service. Specifically, your instance of _audioPlayer lives outside audio_service, while your implementation of setRepeatMode lives inside audio_service and will not have access to your _audioPlayer instance which is outside audio_service.

Encapsulate all of your audio logic within audio_service so that your setRepeatMode implementation is able to reference your _audioPlayer instance and call _audioPlayer.setLoopMode() on it. This is actually the very reason why we encapsulate (bundle the methods with the data that they need to operate on), but in this case there is another reason why we do it in audio_service, and that is that everything outside the audio_service "capsule" could be destroyed at any time. So if your app transitions into the background and your UI is destroyed, you don't want half of your audio logic to be destroyed. If you encapsulate it all within the audio_service, it will be able to survive the destruction of the UI and since it's self contained, will have everything it needs to be able to keep on playing audio in the background.

Also, the fact that your app may also want to play audio in the foreground does not mean that you need to break that encapsulation. This encapsulated audio code is perfectly capable of running with the UI and without the UI, and so you don't actually need to change your programming style to support the foreground case.