flame-engine / flame

A Flutter based game engine.
https://flame-engine.org
MIT License
9.28k stars 913 forks source link

Audio issue #3367

Open RawadZogheib opened 3 hours ago

RawadZogheib commented 3 hours ago

What happened?

When I remove the audio the game a fast and all is functioning well, but when I add the audio I have 2 issue:

1) If I'm in a WhatsApp call the game have a bad functionality (this issue is not accruing if I remove the audio plugin) 2) I we play 5 round or more the game start lagging and having low FPS (this issue is not accruing if I remove the audio plugin)

I think there is an issue with the dispose in this plugin after checking the code!

What do you expect?

a solution for the bug? :')

How can we reproduce this?

I guess if there is a good dispose for the audio after it's played the issue should be solved because this issue isn't available on AudioPlayer!

What steps should take to fix this?

.

Do have an example of where the bug occurs?

no

Relevant log output

There is no exception!

Execute in a terminal and put output into the code block below

flutter doctor -v [✓] Flutter (Channel stable, 3.24.4, on macOS 13.7.1 22H221 darwin-x64, locale en-LB) • Flutter version 3.24.4 on channel stable at /Users/rawadzogheib/Documents/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision 603104015d (3 weeks ago), 2024-10-24 08:01:25 -0700 • Engine revision db49896cf2 • Dart version 3.5.4 • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/rawadzogheib/Library/Android/sdk • Platform android-34, build-tools 34.0.0 • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 21.0.3+-79915915-b509.11) • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.2) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 15C500b • CocoaPods version 1.15.2

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

[✓] Android Studio (version 2024.2) • 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 21.0.3+-79915915-b509.11)

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

[✓] Connected device (2 available)
• macOS (desktop) • macos • darwin-x64 • macOS 13.7.1 22H221 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 131.0.6778.69

[✓] Network resources • All expected network resources are available.

• No issues found!

Affected platforms

Android, iOS

Other information

import 'dart:math';

import 'package:flame_audio/flame_audio.dart';
import 'package:kung_fu_cat/locale/get_storage_helper.dart';
import 'package:kung_fu_cat/remote/firebase/crud_firestore.dart';

class AudioHelper {
  static final AudioHelper _singleton = AudioHelper._internal();

  factory AudioHelper() {
    return _singleton;
  }

  AudioHelper._internal() {
    _musicVolume = GetStorageHelper().getMusicVolume();
    _gameVolume = GetStorageHelper().getGameVolume();
  }

  double _musicVolume = 1.0;
  double _gameVolume = 1.0;

  double get musicVolume => _musicVolume;

  double get gameVolume => _gameVolume;

  set musicVolume(double value) {
    _musicVolume = value;
    GetStorageHelper().setMusicVolume(musicVolume: _musicVolume);
  }

  set gameVolume(double value) {
    _gameVolume = value;
    GetStorageHelper().setGameVolume(gameVolume: _gameVolume);
  }

  AudioPool? clickSound;
  AudioPool? slicingSound0;
  AudioPool? slicingSound1;
  AudioPool? slicingSound2;
  AudioPool? slicingSound3;
  AudioPool? coinThrowSoundSound;
  AudioPool? bombThrowSoundSound;
  AudioPool? gameOverBombSound;
  AudioPool? chooseGameSwipe;

  Future<void> initAudio() async {
    try {
      FlameAudio.bgm.initialize();
      await FlameAudio.audioCache.loadAll([
        'background_music/background_song.mp3',
        'button_sound/button_click_sound.mp3',
        'slicing_sound/slicing_sound0.mp3',
        'slicing_sound/slicing_sound1.mp3',
        'slicing_sound/slicing_sound2.mp3',
        'slicing_sound/slicing_sound3.mp3',
        'coin_sound/coin_throw_sound.mp3',
        'bomb_sound/bomb_throw_sound.mp3',
        'loading_game/countdown_one.mp3',
        'loading_game/countdown_two.mp3',
        'loading_game/countdown_three.mp3',
        'loading_game/countdown_go.mp3',
        'loading_game/countdown_end_sound.mp3',
        'golden_cat_sound/golden_cat_throw_sound.mp3',
        'golden_cat_sound/thunder_sound.mp3',
        'background_music/golden_cat_song.mp3',
        'game_view/game_over_via_bomb_sound.mp3',
        'game_view/game_over_via_fruit_sound.mp3',
        'game_view/next_level_sound.mp3',
        'choose_game_view/choose_game_swipe.mp3',
        'waiting_room/opponent_found_sound.mp3',
        'waiting_room/prize_pool_sound.mp3',
        'waiting_room/start_multiplayer_game_sound.mp3',
        'waiting_room/waiting_opponent_sound.mp3',
      ]);
      await iniAudioPool();
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio First Init");
    }
  }

  Future<void> playBackgroundMusic() async {
    if (FlameAudio.bgm.isPlaying) {
      try {
        await FlameAudio.bgm.stop();
      } catch (e) {
        _exception(
            exception: e.toString(),
            errorDescription: "On Audio Stop Background Music");
      }
    }
    try {
      await FlameAudio.bgm
          .play('background_music/background_song.mp3', volume: _musicVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Background Music");
    }
  }

  Future<void> playWaitingOpponentBackgroundMusic() async {
    if (FlameAudio.bgm.isPlaying) {
      try {
        await FlameAudio.bgm.stop();
      } catch (e) {
        _exception(
            exception: e.toString(),
            errorDescription: "On Audio Stop WaitingRoom Background Music");
      }
    }
    try {
      await FlameAudio.bgm
          .play('waiting_room/waiting_opponent_sound.mp3', volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play WaitingRoom Background Music");
    }
  }

  Future<void> playGoldenCatThrowMusic() async {
    if (FlameAudio.bgm.isPlaying) {
      try {
        await FlameAudio.bgm.stop();
      } catch (e) {
        _exception(
            exception: e.toString(),
            errorDescription: "On Audio Stop Golden Cat Background Music1");
      }
    }
    try {
      await FlameAudio.play('golden_cat_sound/golden_cat_throw_sound.mp3',
          volume: _gameVolume);
      await FlameAudio.bgm
          .play('background_music/golden_cat_song.mp3', volume: _musicVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Golden Cat Background Music1");
    }
  }

  Future<void> playGoldenCatBackgroundMusic() async {
    if (FlameAudio.bgm.isPlaying) {
      try {
        await FlameAudio.bgm.stop();
      } catch (e) {
        _exception(
            exception: e.toString(),
            errorDescription: "On Audio Stop Golden Cat Background Music2");
      }
    }
    try {
      await FlameAudio.bgm
          .play('background_music/golden_cat_song.mp3', volume: _musicVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Golden Cat Background Music2");
    }
  }

  Future<void> pauseBackgroundMusic() async {
    try {
      if (FlameAudio.bgm.isPlaying) {
        await FlameAudio.bgm.pause();
      }
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Pause Background Music");
    }
  }

  Future<void> stopBackgroundMusic() async {
    try {
      await FlameAudio.bgm.stop();
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Stop Background Music");
    }
  }

  Future<void> resumeBackgroundMusic() async {
    try {
      await FlameAudio.bgm.resume();
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Resume Background Music");
    }
  }

  Future<void> playChooseGameSwipeSound() async {
    try {
      await chooseGameSwipe!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Game Choose Swipe");
    }
  }

  Future<void> playOpponentFoundSound() async {
    try {
      await FlameAudio.play('waiting_room/opponent_found_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Opponent Found");
    }
  }

  Future<void> playPrizePoolSound() async {
    try {
      await FlameAudio.play('waiting_room/prize_pool_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Prize Pool");
    }
  }

  Future<void> playStartMultiplayerGameSound() async {
    try {
      await FlameAudio.play('waiting_room/start_multiplayer_game_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Game Multiplayer");
    }
  }

  Future<void> playCoinThrowSound() async {
    try {
      await coinThrowSoundSound!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Play Coin");
    }
  }

  Future<void> playBombSound() async {
    try {
      await bombThrowSoundSound!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Play Bomb");
    }
  }

  Future<void> playSelectSwordSound() async {
    try {
      await FlameAudio.play('locker_view/select_sword_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Select Sword");
    }
  }

  Future<void> playSelectCatSound() async {
    try {
      await FlameAudio.play('locker_view/select_cat_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Select Cat");
    }
  }

  Future<void> playGameOverBombSound() async {
    try {
      await gameOverBombSound!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play GameOver Bomb");
    }
  }

  Future<void> playGameOverFruitSound() async {
    try {
      await FlameAudio.play('game_view/game_over_via_fruit_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play GameOver Fruit");
    }
  }

  Future<void> playNextLevelSound() async {
    try {
      await FlameAudio.play('game_view/next_level_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Next Level");
    }
  }

  Future<void> playClickSound() async {
    try {
      await clickSound!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Play Click");
    }
  }

  /// Loading Game
  Future<void> playCountDownOneSound() async {
    try {
      await FlameAudio.play('loading_game/countdown_one.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Count Down One");
    }
  }

  Future<void> playCountDownTwoSound() async {
    try {
      await FlameAudio.play('loading_game/countdown_two.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Count Down Two");
    }
  }

  Future<void> playCountDownThreeSound() async {
    try {
      await FlameAudio.play('loading_game/countdown_three.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Count Down Thre");
    }
  }

  Future<void> playCountDownGoSound() async {
    try {
      await FlameAudio.play('loading_game/countdown_go.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Count Down Go");
    }
  }

  Future<void> playCountDownEndSoundSound() async {
    try {
      await FlameAudio.play('loading_game/countdown_end_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Count Down End");
    }
  }

  Future<void> playThunderSound() async {
    try {
      await FlameAudio.play('golden_cat_sound/thunder_sound.mp3',
          volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Play Thunder");
    }
  }

  Future<void> playRandomSlicingSound() async {
    try {
      final int rand = Random().nextInt(3);
      if (rand == 0) await slicingSound0!.start(volume: _gameVolume);
      if (rand == 1) await slicingSound1!.start(volume: _gameVolume);
      if (rand == 2) await slicingSound2!.start(volume: _gameVolume);
      if (rand == 3) await slicingSound3!.start(volume: _gameVolume);
    } catch (e) {
      _exception(
          exception: e.toString(),
          errorDescription: "On Audio Play Random Slicing");
    }
  }

  Future<void> iniAudioPool() async {
    await disposeAudioPool();
    try {
      clickSound = await FlameAudio.createPool(
          'button_sound/button_click_sound.mp3',
          maxPlayers: 5);
      chooseGameSwipe = await FlameAudio.createPool(
          'choose_game_view/choose_game_swipe.mp3',
          maxPlayers: 10);
    } catch (e) {
      _exception(exception: e.toString(), errorDescription: "On Audio Init");
    }
    await iniGameAudioPool();
  }

  Future<void> iniGameAudioPool() async {
    await disposeGameAudioPool();
    try {
      slicingSound0 = await FlameAudio.createPool(
          'slicing_sound/slicing_sound0.mp3',
          maxPlayers: 5);
      slicingSound1 = await FlameAudio.createPool(
          'slicing_sound/slicing_sound1.mp3',
          maxPlayers: 5);
      slicingSound2 = await FlameAudio.createPool(
          'slicing_sound/slicing_sound2.mp3',
          maxPlayers: 5);
      slicingSound3 = await FlameAudio.createPool(
          'slicing_sound/slicing_sound3.mp3',
          maxPlayers: 5);
      coinThrowSoundSound = await FlameAudio.createPool(
          'coin_sound/coin_throw_sound.mp3',
          maxPlayers: 5);
      bombThrowSoundSound = await FlameAudio.createPool(
          'bomb_sound/bomb_throw_sound.mp3',
          maxPlayers: 5);
      gameOverBombSound = await FlameAudio.createPool(
          'game_view/game_over_via_bomb_sound.mp3',
          maxPlayers: 5);
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Game Init");
    }
  }

  Future<void> disposeAudioPool() async {
    try {
      if (clickSound != null) await clickSound!.dispose();
      if (chooseGameSwipe != null) await chooseGameSwipe!.dispose();
      clickSound = null;
      chooseGameSwipe = null;
    } catch (e) {
      _exception(exception: e.toString(), errorDescription: "On Audio Dispose");
    }
  }

  Future<void> disposeGameAudioPool() async {
    try {
      if (slicingSound0 != null) await slicingSound0!.dispose();
      if (slicingSound1 != null) await slicingSound1!.dispose();
      if (slicingSound2 != null) await slicingSound2!.dispose();
      if (slicingSound3 != null) await slicingSound3!.dispose();
      if (coinThrowSoundSound != null) await coinThrowSoundSound!.dispose();
      if (bombThrowSoundSound != null) await bombThrowSoundSound!.dispose();
      if (gameOverBombSound != null) await gameOverBombSound!.dispose();
      slicingSound0 = null;
      slicingSound1 = null;
      slicingSound2 = null;
      slicingSound3 = null;
      coinThrowSoundSound = null;
      bombThrowSoundSound = null;
      gameOverBombSound = null;
    } catch (e) {
      _exception(
          exception: e.toString(), errorDescription: "On Audio Game Dispose");
    }
  }

  void _exception(
      {required String exception, required String errorDescription}) {
    final CRUDFirestore crudFirestore = CRUDFirestore();
    crudFirestore.exception(
        errorCode: "44974",
        exception: exception,
        showSelectedGameId: true,
        errorDescription: errorDescription);
  }
}

Are you interested in working on a PR for this?

spydon commented 2 hours ago

Can you check in the Flutter DevTools if you're having more audioplayers instances than you expect? Other than that I would try to only play background sounds to see if it is the pools that are the problem, or the other way around.

RawadZogheib commented 1 hour ago

Hello, @spydon, thank you for responding,

DevTools I can see all the flame Component but I can't see the AudioPool.

But I think "FlameAudio.bgm.initialize();" is called only once on the begin of the game and never disposed, because I always have background music in the game, and it's initialized in the First App load after signing.

I only use bgm.play(); and bam.stop() to switch background music?

It can be the issue?