Flutter: AudioPlayers how to stop multiple sounds at once? - flutter

I use AudioPlayers package.
I have Button1 that plays the sounds. (Code Below)
AudioCache playerCache = new AudioCache(); // you already initialized this
AudioPlayer player = new AudioPlayer();
void _playFile(String yol, String name) async {
player = await playerCache.play(yol);
}
But there is another button which calling "Button2" has to stop all of the sounds at once. I wrote this :
void cancelPlay() {
print("stop");
playSounds.removeRange(0, playSounds.length);
player.stop();
player.stop();
}
However , when user click Button2 , it only stops the last sound. I want that to stop all of the sounds. How to do that ?
I guess The problem is, that every time i call _playFile() (press Button 1) a new instance of AudioPlayer is assigned to the player variable, hence in cancelPlay() the player variable holds only the last instance of AudiPlayer.
How can i do to store the instances in a list.
Thank you.

make a variable
List<AudioPlayer> audioPlayers = [];
every time you tap button1, add new audioPlayer
audioPlayers.add(new AudioPlayer());
then when you tap on button2, do something like this to stop every audioPlayer
audioPlayers.forEach((audioPlayer) => audioPlayer.stop());
or even better would be function to toggle the AudioPlaybackState of audioPlayers, so if they are paused then play and vice versa. Ofcourse this example is based on presumption that every audioPlayer controller has same AudioPlaybackState as first:
void toggleAudioPlayers() {
if (audioPlayers.first.playbackState == AudioPlaybackState.playing) {
audioPlayers.forEach((audioPlayer) => audioPlayer.stop());
} else {
audioPlayers.forEach((audioPlayer) => audioPlayer.play());
}
}
Dont forget to dispose every audioPlayer controller to prevent memory leaks:
audioPlayers.forEach((audioPlayer) => audioPlayer.dispose());

AudioPlayer.players.forEach((key, value) {
value.stop();
});
when you play another audio you need to call this code before

Related

After pause menu score doesn't work anymore

does anyone know how to fix this? In my unity game when I go to pause menu and then continue playing, my scoreboard stops updating. I have two scoreboards, one in game and one in pause menu. The one in pause menu works well and updates but the one in game freezes after once visited in pause menu.
Here is my pausecodes and codes to add money (score):
public void PauseGame()
{
Time.timeScale = 0;
}
public void UnPauseGame()
{
Time.timeScale = 1;
}
}
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
Destroy(this.gameObject);
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
}
public class textManager : MonoBehaviour
{
public static textManager instance;
public Text moneyText;
public int money;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
Data data = SaveSystem.LoadData();
money = data.balance;
moneyText.text = "Balance: " + money.ToString() + " $";
}
public void AddMoney()
{
money = money + 10;
moneyText.text = "Balance: " + money.ToString() + " $";
SaveSystem.SaveData(this);
}
public int findMoney()
{
return money;
}
}
Please ask more info if needed.
I have tried to delete the one scoreboard in pause menu and after that the in-game pause menu started working right, but I would like to have still that other scoreboard too.
if (collision.gameObject.tag == "Respawn") // When player lifts fish up
{
// TODO: Player gets money (points) when this happens
textManager.instance.AddMoney();
Debug.Log("Add money");
Destroy(this.gameObject);
}
As I said this is the fix for what you posted as for the pause unpause problem you have to post the actual code where you do the pause unpause behavior so can people help you out my friend. As for what you posted what you've been doing is destroying the script just before excuting the call to textManager.instance.AddMoney(); and this will never run in the order you set in your code.
The scope of a static field is global, this means there can only be one.
Your textManager (side note: a classes first letter has to be upper case) is certainly attached to multiple GameObjects, once to the object displaying the score in the scene UI and once to a UI element in the pause menu.
As soon as you pause your game the first time, the TextManager attached to the object in the pause menu will run Awake() and the instance (please capitalise your properties as well) will be overriden and the reference to the ingame TextManager gets discarded.
The sloppy fix is re-initializing the ingame TextManager when you unpause the game, assigning it to be the Instance again. I'd not recommend doing that though.
The better solution is to implement an event on the player that gets triggered when the score changes and making the player instance a Singleton object since there will be only one player in all circumstances.
The UI elements displaying the score can then subscribe to this event in OnEnable() and unsubscribe in OnDisable() (do not forget to unsubscribe from events).
Addendum: You should not destroy your object before all code has been executed. Your code will still work because of how things are managed on the C++ layer of Unity, but it is definitely bad practice.

Q: How to play OneShotAnimations in reverse with new rive

I have an app where I have an animated check mark made in rive. It is a one shot animation and I want to play it and have it stay in the end of the animation once it's done and then also be able to reverse it when the user clicks on it again. I tried using a SimpleAnimation but I also wasn't able to achieve what I want but it did stay after the animation was done. I don't, however, know how to play an animation and then reverse it or play another animation.
The check mark looks something like this: https://cln.sh/MwLFNs
They are two separate animations but they are copy pasted but in reverse so I can use either one or both.
What I need is to have the animation play and then when the user clicks on the check, I want the animation to change to another one which would then play. How can I achieve this?
Thanks for the help!
you can use State machines, get the state and decide what to do when you want
// Getting state machine
void _onRiveInit(Artboard artboard) async {
final controller =
StateMachineController.fromArtboard(artboard, 'State Machine 1');
if (controller != null) {
artboard.addController(controller);
_lick = controller.findInput<bool>('Lick') as SMITrigger;
}
}
void _onHandlerStateMachine() => _lick?.value = !_lick!.value;
_onRiveInit is a function that is fired when the Rive file is being loading,
after you get the controller to the state machine to modify his state (This is important the name of the State Machine has to be the same of the animation State machine name)
and to modified the value of the trigger your must get the reference on this way
_lick = controller.findInput('Lick') as SMITrigger;
then you can use
void _onHandlerStateMachine() => _lick?.value = !_lick!.value;
to put or remove the animation on any time, also you can have more than one trigger.
If your need that your animation do something and after a time do something else you can use a debouncer function
_debouncer.run(() {
_onHandlerStateMachine()
});
**Remember update the view if are using state management
when the rive file is load
// Loads a Rive file
Future<void> _loadRiveFile() async {
final bytes = await rootBundle.load(riveFileName);
RiveFile rFile = RiveFile.import(bytes);
final artboard = rFile.mainArtboard;
_onRiveInit(artboard);
globalArtboard = artboard
..addController(
_animationController = SimpleAnimation('idle'),
);
}
Here I have and example of a Rive animation of Teddy, using Flutter with provider
https://github.com/alcampospalacios/flutter-rive-animations

Flutter Audioplayers delay

I'm coding a small game with the Flutter Framework.
I'm using audioplayers for the Sounds.
It works fine like this, when calling it for example 2 times a second.
But whenn I call it more than 5 times and again in the next second at some point the sound has like a delay and then after a second or so all the sounds play at once :) That sounds weired.
I also tested the audioplayers example from github on my iphone. Repeating the sounds in low frequency is ok, but when I repeat clicking the button as fast as possible at some point it gets some delay and the same thing is happening.
Is there some way to stop the previous Sound before and then playing the next one or isnt this possible?
Or is there some other Idea how to deal with the sounds?
This is how I use it:
AudioCache upgradeSound = new AudioCache();
void playUpgradeSound() {
_playUpgradeSound(upgradeSound);
}
void _playUpgradeSound(AudioCache ac) async{
await ac.play('audio/upgr.mp3');
}
Thank you very much in advance
I solve similar problem by having singleton class, and after first play I can get the state, and I can stop previous play.
class Audio {
static final playerCache = AudioCache();
static AudioPlayer audioPlayer;
static void play(String audioName) async {
audioPlayer = await playerCache.play('$audioName.mp3');
}
static void stop() {
audioPlayer.stop();
}
}
...
child: IconButton(
onPressed: () {
try {
if (Audio.audioPlayer.state ==
AudioPlayerState.PLAYING) {
Audio.stop();
} else {
Audio.play('bid');
}
} catch (e) {
Audio.play('bid');
}
},
...
There is a line of code in its own documentation.
To use the low latency API, better for gaming sounds, use:
AudioPlayer audioPlayer = AudioPlayer(mode: PlayerMode.LOW_LATENCY);
In this mode the backend won't fire any duration or position updates. Also, it is not possible to use the seek method to set the audio a specific position. This mode is also not available on web.
I hope it is useful.

Unity: LoadScene does not work when fired from timer

in my game, when the player dies, a dying sound is played and once the sound is over, the scene is supposed to be reloaded when the user still has enough lives.
Before I had the sound, the play died instantly upon calling the death() function:
public static void Death()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
}
This worked like a charm.
But now I added a death sound to it. Unfortunately, unity doesnt provide an event handler for when the sound is done playing (I want the scene to be reloaded not instantly anymore, but after the death sound is done playing), so I have decided to take it upon myself to just build a timer. The timer fires right after the death sound is over. This is what this function has become:
public static void Death()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
PlayDeathSound();
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = aSDeath.clip.length * 1000;
timer.Start();
timer.Elapsed += delegate
{
timer.Stop();
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
};
}
As you can see, to make sure the timer REALLY fires, I set up a "debug.Log("TIMER")" to see, if it really works. And guess what: it does. The debug now shows "TIMER" in its console. But you know what doesnt work anymore? The two lines of code right beneath that.
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
It's the same exact lines that worked just before - but when fired from the timer, they just get ignored? How is this even possible?
When I change it all back, it works again. Only when the timer fires the two lines, they get ignored.
This is totally odd or am I missing something? Thank you!
Okay I am not an expert on C# and delegate but apparently it creates a separate thread and you can only use SceneManager.GetActiveScene on main thread.
Since i am not so sure about delegate i will offer an easier solution. You can use a coroutine since you know how much you have to wait like this:
public void Death()
{
StartCoroutine(DeathCoroutine());
}
IEnumerator DeathCoroutine()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
PlayDeathSound();
// wait for duration of the clip than continue executing rest of the code
yield return new WaitForSeconds(aSDeath.clip.length);
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
}
What about using a coroutine ? You just start it when the player dies, and yield while your sound is still playing.

Single Audiosource for multiple clips?

I have a couple of audio sources in a script, idea being that one is used for effects and the other for music. I am noticing that if an effect plays at the same time as another one, it actually stops the previous one from playing, probably because they are playing from the same audio source.
My question is, do I really have to instantiate a new audio source every time i want to play a new sound, just to make sure that one sound will not stop a currently playing one ? Is there a better way ?
The method that plays a sound effect(and the awake one) in my class are these :
void Awake() {
audioSource = GetComponents<AudioSource>()[0];
audioEffectsSource = GetComponents<AudioSource>()[1];
audioSource.volume = PlayerPrefsManager.getMasterVolume();
audioEffectsSource.volume = PlayerPrefsManager.getSFXMasterVolume();
}
public void playSoundEffect(AudioClip clip)
{
if(clip != null)
{
audioEffectsSource.clip = clip;
audioEffectsSource.volume = PlayerPrefsManager.getSFXMasterVolume();
audioEffectsSource.PlayOneShot(clip);
}
}
The script is attached to a musicmanager gameobject with 2 audiosources, one for music fx and one for sound tracks.