How to play a sound repeadetly when a keyboard button is pressed

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By markwiering

Hello there,

I am making a 2D cat game in the stable release of Godot 3.1, in which the cat begins to shoot when you press the “ui_select” (in my case, Left Control) key.

I want a certain .wav-file of a gunshot to be played whenever the player presses “ui_select”. I tried to implement this using the following code inside of func _physics_process(delta):

var speler = AudioStreamPlayer.new();
self.add_child(speler);
speler.stream = load("res://sfx/ShootGunMagnum.wav");
speler.play();

Then I moved up the “speler” variable, as not to re-initialise it at every frame, resulting in a code like this:

var speler = AudioStreamPlayer.new();

func _physics_process(delta):
	if Input.is_action_pressed("ui_select"):
		self.add_child(speler);
		speler.stream = load("res://sfx/ShootGunMagnum.wav");
		speler.play(true);

This yields an error, however: “ERROR: add_child: Can’t add child ‘@@3’ to ‘Speler’, already has a parent* ‘Speler’.
At: scene/main/node.cpp:1165

Since func _physics_process(delta): is called every frame, having code that starts playing sound will just play the absolute beginning of the sound file and then, before having finished the end of the sound file, start itself over again, creating some kind of low beep rather than actual shot sounds.

  • I am looking for a way to:
    • Not to re-add a child countless of times; this is obviously not the desired behaviour.
    • Implement some way to detect that a .wav-file is playing, so the .wav-file isn’t being started when it’s still playing.

The desired behaviour is to have the same .wav-file play itself over and over again as long as the “ui_select” button is pressed. If you release the button, the current .wav-file should finish (no interrupt!). If you simply press the “ui_select” button instead of keeping it pressed, the .wav-file should play once.

How do I do that?

:bust_in_silhouette: Reply From: MisterAcoustic

Hello - don’t know if you’ve solved this by now, but here is one answer.

You should take a look at https://docs.godotengine.org/en/stable/tutorials/3d/fps_tutorial/part_six.html?highlight=queue_free#writing-a-sound-system-we-can-use-anywhere

For your situation, you may want to add control variables that say whether or not you’ve added the child, set the stream, and started playing. Once it’s started, set the variable so it wont’ be done again:

var speler = AudioStreamPlayer.new();

var first_time = true
var is_playing = false

func _physics_process(delta):
    if Input.is_action_pressed("ui_select"):
        if  first_time:
            self.add_child(speler);
            speler.stream = load("res://sfx/ShootGunMagnum.wav");
            first_time = false
        if not is_playing:
            speler.play(true);
            is_playing = true

In the example, you can see that they connect a ‘finished’ event with a function that they provide in the gdscript (“sound_finished”).

yourstreamplayer.connect("finished", self, "sound_finished")

This event is called each time the sound finishes playing completely, and the code looks like this:

func sound_finished():
	if should_loop:
		audio_node.play(0.0)
	else:
		audio_node.stop()
		queue_free()

You can declare your own ‘should_loop’ variable (which you would set to true when you start the sound playing). When your key is released, you then set that to false, which will allow the current loop of the sound to finish playing normally, and stop when it’s complete.

Probably, you should adopt the whole of the audio code from that example, rather than trying to roll your own - but I hope this gets you where you want to go.