AVEbrahimi
AVEbrahimi

Reputation: 19134

Flutter just_audio loop mode is not looping

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;
  }
});

Upvotes: 4

Views: 4644

Answers (3)

buckleyJohnson
buckleyJohnson

Reputation: 489

The way I did it was to simply put in the onStart method:

await _player.setLoopMode(LoopMode.all);

This of course isn't changeable by the user.

Upvotes: 1

Suragch
Suragch

Reputation: 511558

From inside your BackgroundAudioTask subclass you should override the onSetRepeatMode method.

import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';

class AudioPlayerTask extends BackgroundAudioTask {
  AudioPlayer _player = new AudioPlayer();

  ...

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

This particular implementation is using just_audio as the audio player. Note that AudioPlayerTask is the only class that the just_audio AudioPlayer appears in. It shouldn't be in any other class of your app. Everywhere else you'll use audio_service's AudioService class.

For example, when a user presses a button, you could run the following line of code:

AudioService.setRepeatMode(AudioServiceRepeatMode.none);

This will notify the system and also let the BackgroundAudioTask run onSetRepeatMode so that the internal AudioPlayer class can do its thing.

Upvotes: 5

Ryan Heise
Ryan Heise

Reputation: 2766

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.

Upvotes: 2

Related Questions