Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Silence between tracks in just_audio

I want to have a variable length pause between tracks in a playlist created with a just_audio AudioPlayer instance (there is a background track which I want playing during this interval). Something to the effect of:

_voiceAudioPlayer.currentIndexStream.listen((event) {
  _voiceAudioPlayer.pause();
  Future.delayed(const Duration(seconds: 4), () => _voiceAudioPlayer.play());
});

This throws an error:

"Unhandled Exception: Bad state: Cannot fire new event. Controller is already firing an event"

Is there a clean way to do this? I'm considering adding silent mp3s at every other track in the playlist, but feel there there ought to be a better way.

like image 452
Raghu N Avatar asked Sep 18 '25 09:09

Raghu N


1 Answers

This error happens because currentIndexStream is a "sync" broadcast stream, so you can't trigger another state change event while the current event is being processed (i.e. in the same cycle of the event loop). But you can get around that by scheduling a microtask to happen after the current cycle:

_voiceAudioPlayer.currentIndexStream.listen((index) {
  scheduleMicrotask(() async {
    _voiceAudioPlayer.pause();
    await Future.delayed(Duration(seconds: 4));
    _voiceAudioPlayer.play();
  });
});

Still, I wouldn't depend on this callback being executed soon enough due to the gapless nature of just_audio's playlists. That is, the next audio track will begin playing immediately, so you're bound to hear at least a fraction of the next item's audio before the pause happens.

There is an open feature request for a SilenceAudioSource which could be inserted into a playlist (you can vote for that issue by clicking the thumbs up button if you'd like it to be implemented.) A silent audio file which you proposed is actually the simplest alternative to SilenceAudioSource.

Otherwise, another approach would be to not use the gapless playlists feature at all (since you don't need the gapless feature anyway), and just implement your own logic to advance the queue:

final queue = [source1, source2, source3, ...];
for (var source in queue) {
  await _voiceAudioPlayer.setAudioSource(source);
  await _voiceAudioPlayer.play();
  await Future.delayed(seconds: 4);
}

The above example does not handle pause/resume logic, but it is just to show that it is possible for you to take the playlist logic into your own hands if you don't require the gapless feature.

like image 101
Ryan Heise Avatar answered Sep 19 '25 23:09

Ryan Heise