I have a stamina bar in my game, and when the value is equal to 0, I want an AudioStreamPlayer3D to play a specific sound. Here's the code I used:

onready var PlayerAudio = $"../../../../../../PlayerAudio"

const tired_sight = preload("res://SFX/Player/Tired sighs.wav")

func _physics_process(delta):
	if value == 0:
		PlayerAudio.stop()
		if PlayerAudio.is_playing() == false:
			PlayerAudio.stream = tired_sight
			PlayerAudio.play()

What it does is: first check if the value of the stamina is zero, if it is, then stop the Audio that the AudioStreamPlayer is playing (this is supposed to stop a “running” sound effect, which I want to stop when running out of stamina). Stopping this audio is important because there's always another sound playing the moment before the value gets to zero. After that, it checks if the node is playing something, but since the audio is stopped before, the condition is always false (no audio playing). Finally, if there isn't any audio playing, then play the “tired” sound effect.

The issue is that for some reason, the AudioStreamPlayer stops, but the sound starts playing when the stamina is no longer zero (stamina recharges after a few seconds). The "tired" sound should play when the stamina is zero.

  • You could add method callbacks(special keys) to the animations to cue the sound to play, or you should be able to read the state of the FSM from script and queue sound accordingly in your script.

A wild guess is that since _physics_process() is called 60 times a second, the code dealing with AudioStreamPlayer might be getting executed multiple times on successive calls of _physics_process(). I.e., that code starts executing before the previous execution has finished.

If that's the problem, it could be prevented using a boolean flag, a signal or a mutex.

The logic is correct for the first time stamina hits zero. It stops the running sound and starts the tired sound.
But on the next update after that, stamina is still zero, so it stops the sound again, but this time it's the tired sound.
Basically the tired sound is being stopped and restarted from the beginning every 0.0167ms (that's the buzzing sound, just the beginning of tired being played rapidly).
Once stamina goes above zero, the stop playing code stops happening, allowing the tired sound to finish.

Try changing the stamina check to this:

if value == 0 and PlayerAudio.stream != tired_sight:

That will make it only stop playing the sound if the current stream isn't tired.

    Kojack This seems to work, but for some reason, the tired sound only plays when I'm holding the key that plays the "running sound effect". I changed the code to this, since stopping and playing the stream in the same if statement just stopped the audio in the same frame it started:

    	if value == 0:
    		GlobalScript.tired = true
    		GlobalScript.can_run = false
    		if PlayerAudio.stream == run_sight and PlayerAudio.is_playing() == true:
    			PlayerAudio.stop()
    		if PlayerAudio.stream != tired_sight and PlayerAudio.is_playing() == false:
    			PlayerAudio.stream = tired_sight
    			PlayerAudio.play()

    The tired sound only plays when I'm still pressing "shift", which is the key that plays the run sound. A soon as I release it, the sound stops playing.

    Assuming you haven't already, if I were you I'd look into using/implementing a finite state machine(there is a built in state machine as part of the animation tree) for the character controller. Not only can you play appropriate animations in response to the state the character is in such as idle, walking, running, jumping, falling, hurt, tired, knockedout or sleeping etc. But also play appropriate sounds too.

    You could add method callbacks(special keys) to the animations to cue the sound to play, or you should be able to read the state of the FSM from script and queue sound accordingly in your script.