godot-docs icon indicating copy to clipboard operation
godot-docs copied to clipboard

There's no doc about playing a lot of audios at same time.

Open nonunknown opened this issue 4 years ago • 7 comments

Your Godot version: 3.2.4 RC1

Issue description: Altought the docs talk about how WAV file format can play thousands of it without any performance issue, the docs doesnt says how this can be archieved, I finally manage to found a tutorial from a user who did it right, but that is not begginer friendly and I couldnt understand it well too.

Maybe there's a better approach? In any case this should be documented, playing a lot of sounds in a little space of time is not simple!

Source Content

https://www.youtube.com/watch?v=DsAIrA9UE4E https://www.youtube.com/watch?v=3CFWlQzMthY

Source Code

extends Node

var dic : Dictionary = {}

func play_sfx(audio_clip : AudioStream, priority : int = 0):
	for child in $sfx.get_children():
		if child.playing == false:
			child.stream = audio_clip
			child.play()
			dic[child.name] = priority
			break
	
		if child.get_index() == $sfx.get_child_count() - 1:
			var priority_player = check_priority_and_find_oldest(dic, priority) #finds player with same/lowest priority and oldest player
			if priority_player != null:
				$sfx.get_node(priority_player).stream = audio_clip
				$sfx.get_node(priority_player).play()
			else:
				print("priority player is null")
	pass

#playes at most 3 sounds at the same time, in a lot of cases bad, because you get less sound feedback.
#execept when you want to restrict the amount of sounds, for example crashes/destructions/debris
func check_priority(_dic : Dictionary, _priority):
	var prio_list : Array = []
	
	for key in _dic:
		if _priority > _dic[key]:
			prio_list.append(key)#append key(sfx_player.name) to the array
	
	#get the lowest priority from prio_list
	var last_prio = null
	for key in prio_list:
		if last_prio == null:
			last_prio = key
			continue
		if _dic[key] < _dic[last_prio]:
			last_prio = key
	return last_prio
	pass

#playes new sounds all the time, bad if you have important sound, wich you liked to play
func find_oldest_player():
	var last_child = null
	
	for child in $sfx.get_children():
		if last_child == null:
			last_child = child
			continue
		#find player wich played the longest
		if child.get_playback_position() > last_child.get_playback_position():
			last_child = child
	
	return last_child.name
	pass
	
#good for all types of situations, important sound get played most of the time and sounds doesn't get get
#swallowed up most of the time
func check_priority_and_find_oldest(_dic, _priority): #1,3,1 == 1
	var prio_list : Array = []
	for key in _dic: 
		if _priority >= _dic[key]:
			prio_list.append(key) #append key(sfx_player.name) with same/lower priority to an array
	
	#find oldest 
	if prio_list.empty():
		return null
	var oldest_player = prio_list[0]
	for i in range(1, prio_list.size() -1):
		if $sfx.get_node(oldest_player).get_playback_position() < $sfx.get_node(prio_list[i]).get_playback_position():
			oldest_player = prio_list[i]

	return oldest_player
	pass
	
	###### Here is a little coding challenge  #######
#func lowest_priority_and_oldest(_dic, _priority):
	#make prio_list
	#append same/lower priority to prio_list
	#get the lowest priority
	#get all player with the same lowest priority
	#if there is more then 1 player
	#get the oldest player from the lowest priority players

func play_music(music_clip : AudioStream):
	$music/music_player.stream = music_clip
	$music/music_player.play()
	pass

nonunknown avatar Feb 05 '21 11:02 nonunknown

Here's what I use: https://github.com/Calinou/escape-space/blob/c151b264c622fcd823a2c43855b2a6f2d8bbe68f/autoload/sound.gd

To be fair, I think this should be solved at the core level by readding polyphony support.

Calinou avatar Feb 05 '21 13:02 Calinou

@Calinou This one lets me play a lot of audios in short time? Also makes sense to be a core issue, in all my projects I had problem with audio! but until there a doc to at least give a north to people out there!

nonunknown avatar Feb 05 '21 14:02 nonunknown

This one lets me play a lot of audios in short time?

Yes :slightly_smiling_face:

Calinou avatar Feb 05 '21 14:02 Calinou

thank you very much!

nonunknown avatar Feb 05 '21 14:02 nonunknown

@Calinou made a C# version of it, worked flawlessly thank you again!

using Godot;
using System;

public static class OSTManager
{

    public static void PlaySFX(Node target, AudioStream stream, Vector2 position, float pitchScale = 1.0f) {
        AudioStreamPlayer2D sfx = new AudioStreamPlayer2D();
        target.GetTree().CurrentScene.AddChild(sfx);
        if (position != null) sfx.GlobalPosition = position;
        sfx.Stream = stream;
        sfx.VolumeDb = 0;
        sfx.PitchScale = pitchScale;
        sfx.Play();
        sfx.Connect("finished",sfx,"queue_free");
    }

    public static void PlaySFXPitched(Node target, AudioStream stream, Vector2 position) {
        RandomNumberGenerator rng = new RandomNumberGenerator();
        PlaySFX(target,stream,position,rng.RandfRange(.9f,1.7f));
    }

    
}

nonunknown avatar Feb 05 '21 17:02 nonunknown

4.0alpha has built-in polyphony support, but it can't be backported to 3.x since it relies on different AudioServer internals. I suppose this still needs to be documented on the manual page that mentions playing sounds in general.

https://github.com/Calinou/escape-space/blob/c151b264c622fcd823a2c43855b2a6f2d8bbe68f/autoload/sound.gd is short enough to include inline on the page, but we could also link to a maintained add-on in the asset library.

Calinou avatar May 10 '22 17:05 Calinou

yeah thats the perfect solution!

nonunknown avatar May 10 '22 18:05 nonunknown