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.02k stars 847 forks source link

Loop is NOT working in the background #1038

Open tomasbaran opened 3 years ago

tomasbaran commented 3 years ago

Looping the audio is working in the foreground — but NOT in the background.

Full Description

I added this in my Info.plist

<key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>

Code to Reproduce

audio_player_handler.dart:

import 'package:ambee2/models/animations/light_animation.dart';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart';
// import 'package:just_audio/just_audio.dart';
import 'package:audioplayers/audioplayers.dart';

class AudioPlayerHandler extends BaseAudioHandler {
  final _player = AudioPlayer();

  AudioPlayerHandler() {
    //set the audio to repeat itself once it's done
    _player.setReleaseMode(ReleaseMode.LOOP);
  }

  Future<void> setUrl(String url) async => await _player.setUrl(url);

  Future<void> playUrl(String url, double volumeValue) => _player.play(url, volume: volumeValue);

  Future<void> resume() => _player.resume();

  @override
  Future<void> pause() => _player.pause();

  @override
  Future<void> stop() => _player.stop();

  Future<void> setVolume(double newValue) => _player.setVolume(newValue);
}

I also have objects called: LightAnimation that has a property audioHandler defined like this:

class LightAnimation {
  String id;
  String audioUrl;
  AudioPlayerHandler audioHandler = AudioPlayerHandler();

  LightAnimation({
    this.id,
    this.audioUrl,
  });
}

Then I just access from a different part of my codebase

await lightAnimation.audioHandler.playUrl(lightAnimation.audioUrl, volumeSetValue);

Log Errors no errors

Screenshots

Platforms

flutter doctor -v

[✓] Flutter (Channel stable, 2.5.3, on macOS 11.5.2 20G95 darwin-arm, locale en-US)
    • Flutter version 2.5.3 at /Users/tomasbaran/code/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 18116933e7 (7 weeks ago), 2021-10-15 10:46:35 -0700
    • Engine revision d3ea636dc5
    • Dart version 2.14.4

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
    • Android SDK at /Users/tomasbaran/Library/Android/sdk
    • Platform android-31, build-tools 31.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 13.1, Build version 13A1030d
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2020.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • 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 11.0.10+0-b96-7249189)

[✓] VS Code (version 1.62.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.29.0

[✓] Connected device (3 available)
    • iPhone (mobile)    • 00008020-000415840E50003A            • ios            • iOS 15.1 19B74
    • iPhone 13 (mobile) • 2E82BA27-306B-4011-B9A3-265D4732604D • ios            •
      com.apple.CoreSimulator.SimRuntime.iOS-15-0 (simulator)
    • Chrome (web)       • chrome                               • web-javascript • Google Chrome 96.0.4664.55

• No issues found!
tomasbaran commented 2 years ago

probably related: https://github.com/bluefireteam/audioplayers/issues/118

tomasbaran commented 2 years ago

I also posted this issue here: https://stackoverflow.com/questions/70225224/audio-not-looping-in-the-background?noredirect=1#comment124531255_70225224 to gather all information in one spot.

tomasbaran commented 2 years ago

probably related: https://github.com/bluefireteam/audioplayers/issues/864

wahyu-handayani commented 2 years ago

Try to change _player.setReleaseMode(ReleaseMode.LOOP); into _player.setLoopMode(LoopMode.one);

tomasbaran commented 2 years ago

@wahyu-handayani Thanks for your tip. Unfortunately, this doesn't help since setLoopMode is not an AudioPlayer() method:

Screen Shot 2022-01-10 at 12 23 43
tomasbaran commented 2 years ago

@luanpotter Can you please comment on this issue since issues about this problem have been unresolved here for years. Shall I just move on a different package? For me it is crucial that the audio looping works in the background and I don't see any comment about it from its creators. Thanks.

wahyu-handayani commented 2 years ago

Hi @tomasbaran right now I am trying to build an apps that can play a music on the background using audioplayers package, and when I read this article https://denis-korovitskii.medium.com/flutter-demo-audioplayers-on-background-via-audio-service-c95d65c90ae1 , I am confused enough, so I read your code and trying to implement it. It is understandable when I read your code. If you don't mind, I would like to ask, how you initialized lightAnimation ? I try to call it by defining late LightAnimation lightAnimation; first and then call await lightAnimation.audioHandler.playUrl(lightAnimation.audioUrl, volumeSetValue); in an onPressed button, but it says that Unhandled Exception: LateInitializationError: Field 'lightAnimation' has not been initialized.

tomasbaran commented 2 years ago

Let me see if I can be helpful. First off, I've never heard of late term, so I'm not sure what it is supposed to do.

In my case, the app loads the library of LightAnimations that are on Firebase. So, I have in my app defined:

 List<LightAnimation> localLibraryAnimations = [];

And once a user taps let's say on red light animations category then the app starts to load all the light animations from Firebase and assigning them to localLibraryAnimations like this:

_currentAnimation = LightAnimation(
        // animationCardImageBuilder: is for better loading experience but not essential in your case I guess
        // animationCardImageBuilder: FutureBuilder(
        //   future: getImageFile(),
        //   builder: (context, snapshot) {
        //     if (snapshot.hasData)
        //       return Image.file(
        //         snapshot.data,
        //         fit: BoxFit.cover,
        //         width: double.infinity,
        //         height: double.infinity,
        //       );
        //     else if (snapshot.hasError) {
        //       return Text('Error#7: ${snapshot.error}', style: TextStyle(color: sColorPrimary56), textAlign: TextAlign.center);
        //     } else {
        //       return Center(
        //         child: Image.asset('assets/images/logo.png', width: 40),
        //       );
        //     }
        //   },
        // ),
        id: animation['animation_id'],
        audioUrl: animation['audio_file_url'] ?? silentAudioFileUrl,
      );

localLibraryAnimations.add(_currentAnimation);

So, to sum it up.

  1. User taps a category (e.g. red ones)
  2. App will load data from Firebase
  3. App will assign data from Firebase to localLibraryAnimations by adding LightAnimation one by one
  4. upon localLibraryAnimations.add(LightAnimation), LightAnimation will automatically initialize audioHandler like this:
    AudioPlayerHandler audioHandler = AudioPlayerHandler();
wahyu-handayani commented 2 years ago

@tomasbaran Thank you very much for your detail explanation, I have read your comment and tried it on my own and finally.. got the good result. But there is something else that I would like to ask.. It was success to run on the background, but there is no something like notification on the background to pause or play or something that indicates we are playing a music. Is there a way to show it on the background ? By the way, I am so sorry for disturbing you

tomasbaran commented 2 years ago

No problem @wahyu-handayani . I'm sure there is a way to show it but I don't know how since I didn't need this feature. Good luck!

Have you tried looping the audio in the background by any chance? If so, were you able to loop the audio in the background, please?

wahyu-handayani commented 2 years ago

@tomasbaran I am able to do looping, here is the code and I call this inside onPressed method:

playerA = await audioCacheA.loop("Rain/${widget.musicAsset}.mp3");

where AudioPlayer playerA = AudioPlayer(); and AudioCache audioCacheA = AudioCache();. It will loop in the background, but I don't know how to broadcast and show it in notification bar of the phone. I would like to make sure, so you don't use a feature to show to the user in a form of notification bar ?

tomasbaran commented 2 years ago

Thanks @wahyu-handayani. Interesting, so looping in the background works for local assets (audioCache, your case) but not network assets like in my case.

No, I don't show or inform users in form of notification bar.

wahyu-handayani commented 2 years ago

@tomasbaran you're welcome, glad to get your help

tomasbaran commented 2 years ago

Unfortunately, I have bad news for your @wahyu-handayani. I have tried to loop in the background local asset just like you did via AudioCache() but the looping stops in the background once you lock the screen. You can try it out yourself.

If you want the author to fix these issues, please upvote with thumb up these issues:

This should speed up the process of fixing the loop in the background issue. Thanks.

Gustl22 commented 2 years ago

We would be happy to have an iOS developer, who can contribute to this library and submit a PR to this topic. Is it still valid for audioplayers: ^1.1.0?

alihuseyin commented 1 year ago

This issue is valid on iOS devices for audioplayers 3.0.1.

JustDominik commented 9 months ago

Problem still exists for audioplayers 5.2.1. Though from my test I have some extra info: In my app I have the option to run more than one players. This bug only happens though when only one of the players is working. As a result, my best guess is that ios kills the background before the player can loop. When a 2nd player is running though, it seems to keep the background alive so the loop goes through.

This opens at least the option for some pretty ugly workarounds.

gitmole7 commented 9 months ago

Has anyone found it possible to achieve a seamless loop for network/url audio sources?

await _player.setReleaseMode(ReleaseMode.loop); leaves a small gap at the start of each loop, even with seamlessly loop-able audio files.