Need advice on designing an audio playing system for a bullet hell game

Godot Version

Godot Engine v4.7.stable.official.5b4e0cb0f

Question

Hi, I’m currently doing a little project to broaden my knowledge. The project is a bullet hell shoot em up style, so there will be a high amount of projectiles on the screen at once.

Currently I’m on the stage where I’m doing the sound code and nodes. Right now I have a Sound Manager Node2D in which i store AudioStreamPlayer nodes as children in the scene tree. To avoid audio clipping and other issues resulted by playing multiple sounds from the same player, I’ve started duplicating the AudioStreamPlayer with the assigned sound, then running a function inside of it, that plays a sound and when its finished, it queue_free()'s the duplicated AudioStreamPlayer node.

Sound Manager:

@export var shooting: Array[AudioStreamPlayer]

func play_sfx(mode,index):
	if mode == "shooting":
		var sfx_dupe = shooting[index].duplicate()
		add_child(sfx_dupe)
		sfx_dupe.play_sound()

Sound Player:

func play_sound():
	play()
	await finished
	queue_free()

However, when I started limit testing the capabilities of this setup, I’ve noticed that when there is an absolutely high amount of projectiles on screen, the sound players overlap, resulting in higher volumes. Same goes for my player character that upon shooting, despite shooting in the same intervals, you can hear a bit of a “pitch up” every second. From browsing other help posts, I’ve seen people talking about making an AudioStreamPlayer pool, however now that I’ve started considering it and thinking about it, wouldn’t it result in the pretty much same thing? Multiple AudioStreamPlayers playing at once?

For the same sound can you set max_polyphony on your AudioStreamPlayer, this should allow playing the same stream over itself up to a few times. Others are recommending a sound pool specifically to avoid add_child and queue_free being called often, scene tree manipulation is expensive, it doesn’t solve any audio problems that you aren’t already solving. A better approach in my opinion would be leveraging the AudioStreamPolyphonic resource.

Are you worried about the volume of the sounds? (of which clipping can mean the combined volume is too loud to properly play, but I think you used clipped to mean the audio is being reset mid-stream?) If the volume is your main concern then you can use bus effects, make a new bus for bullets or sound effects in general and apply a “Compressor Effect” which automatically reduces volume above a threshold.

Okay I think I might’ve confused some terms. Lemme show here as an example

Here is a video of me using polyphony 1 on my audio stream nodes:

And here is a video with higher polyphony (same thing happened with my approach of spawning and queue_free()'ing audio stream nodes):


The actual enemies will not have this much projectiles, but here are videos as a some sort of a “stress test”. As you can hear, when im using higher polyphony/spawning new nodes:

  1. Enemy projectile sound volume stack when multiple enemies are shooting
  2. Every second or so, there is a difference in the player shooting sound, despite the fact that shots are evenly spaced

Is there a way to counteract this? or should I choose whether or not i want a lower or higher polyphony depending on the amount of projectiles shot by enemies?

To clarify your scene tree, each enemy has one audio stream player?

what needs counteracting? What do you want to happen with the sounds? What should it sound like, can you make a mock-up?

I’d say generally rapid-fire sounds won’t use a super-short sample played over and over, rather a authored longer stream with multiple fires in one play, you can .stop the sound if cutting it short is needed.

Stress testing audio may not be that helpful from an artistic perspective, similar to how stress testing rendering rarely looks good.

In my scene, enemies are created using .duplicate(), then each of them have this “SoundComponent” node assigned to them using the @export:

image

So each spawned enemy use the same AudioStreamPlayer node. The thing that i want to counteract are those two points i wrote in my last reply, the fact that when multiple enemies shoot at once, the volume is way higher, and the one where when player is shooting, every second or so, you can hear a small “pitch up” (player node is using the same sound component node, its just in the player scene)

Here is a better example of what i meant previously with the issue about player shooting:

Both of these enemies have the same rate of fire, however you can hear that the way it sounds, changes every once in a while, despite shooting in the same intervals of time

The player could/should use a longer and repeating sample, bake the polyphony in. Gives you a lot more control over your audio, while reducing complicated code too, I feel like you can get this done with only an AudioStreamPlayer for the player.

For enemies I’m assuming they have different rates of fire, enough variance to make perfect looping samples a hassle. The audio you’re producing isn’t very important to gameplay and with so many cues going at the same time it’ll be hard to notice the audio not matching up.

Still if you want to play one blip per shot a fact of the matter is that audio is additive, if you don’t want the audio to get louder you can try using post-processing effects like the compressor effect I mentioned, otherwise you have to cut the sound off early.