First commit 🎉
This commit is contained in:
commit
43ea213f9b
728 changed files with 37080 additions and 0 deletions
179
addons/popochiu/engine/audio_manager/audio_cue.gd
Normal file
179
addons/popochiu/engine/audio_manager/audio_cue.gd
Normal file
|
@ -0,0 +1,179 @@
|
|||
@tool
|
||||
class_name PopochiuAudioCue
|
||||
extends Resource
|
||||
## Used to play audio files with extra properties.
|
||||
##
|
||||
## You can set the pitch (with random values), volume, and audio bus, as well as specify whether
|
||||
## it loops, and whether it is 2D positioned.
|
||||
|
||||
|
||||
## The audio file to play.
|
||||
@export var audio: AudioStream
|
||||
## Whether the audio file will loop when played.
|
||||
@export var loop := false : set = set_loop
|
||||
## Whether this audio cue uses a 2D position.
|
||||
@export var is_2d := false
|
||||
## Whether the audio can be played simultaneously with other instances of itself. Especially useful
|
||||
## for audio cues set in a loop (where [member loop] is [code]true[/code]).
|
||||
@export var can_play_simultaneous := true
|
||||
## The pitch value (in semitones) to use when playing the audio file.
|
||||
@export var pitch := 0.0
|
||||
## The volume to use when playing the audio file.
|
||||
@export var volume := 0.0
|
||||
## The range of values to use for randomly changing the pitch of the audio file when played.
|
||||
@export var rnd_pitch := Vector2.ZERO
|
||||
## The range of values to use to randomly changing the volume of the audio file when played.
|
||||
@export var rnd_volume := Vector2.ZERO
|
||||
## Maximum distance from which the audio file is still hearable. This only works if [member is_2d]
|
||||
## is [code]true[/code].
|
||||
@export var max_distance := 2000
|
||||
## The audio bus in which the audio file will be played.
|
||||
@export var bus := "Master"
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue with a fade that lasts [param duration] seconds. If [param wait_to_end] is
|
||||
## set to [code]true[/code], the function will wait for the audio to finish. You can specify the
|
||||
## starting volume with [param from] and the target volume with [param to], as well as the
|
||||
## [param position_2d] of the [AudioStreamPlayer] or [AudioStreamPlayer2D] that will play the audio
|
||||
## file.
|
||||
func fade(
|
||||
duration := 1.0, wait_to_end := false, from := -80.0, to := INF, position_2d := Vector2.ZERO
|
||||
) -> void:
|
||||
if wait_to_end:
|
||||
await PopochiuUtils.e.am.play_fade_cue(resource_name, duration, from, to, position_2d, true)
|
||||
else:
|
||||
PopochiuUtils.e.am.play_fade_cue(resource_name, duration, from, to, position_2d)
|
||||
|
||||
|
||||
## Plays this audio cue with a fade that lasts [param duration] seconds. If [param wait_to_end] is
|
||||
## set to [code]true[/code], the function will wait for the audio to finish. You can specify the
|
||||
## starting volume with [param from] and the target volume with [param to], as well as the
|
||||
## [param position_2d] of the [AudioStreamPlayer] or [AudioStreamPlayer2D] that will play the audio
|
||||
## file.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_fade(
|
||||
duration := 1.0, wait_to_end := false, from := -80.0, to := INF, position_2d := Vector2.ZERO
|
||||
) -> Callable:
|
||||
return func ():
|
||||
if wait_to_end:
|
||||
await fade(duration, wait_to_end, from, to, position_2d)
|
||||
else:
|
||||
fade(duration, wait_to_end, from, to, position_2d)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
## Stops the audio cue, with an optional fade effect lasting [param fade_duration] seconds.
|
||||
func stop(fade_duration := 0.0) -> void:
|
||||
PopochiuUtils.e.am.stop(resource_name, fade_duration)
|
||||
|
||||
|
||||
## Stops the audio cue, with an optional fade effect lasting [param fade_duration] seconds.[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_stop(fade_duration := 0.0) -> Callable:
|
||||
return func ():
|
||||
stop(fade_duration)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
## Changes the [member AudioStreamPlayer.pitch_scale] of the [AudioStreamPlayer] playing the audio
|
||||
## file associated with this audio cue to [param pitch]. If the audio was played with a 2D position,
|
||||
## then [member AudioStreamPlayer2D.pitch_scale] will be affected.
|
||||
func change_stream_pitch(pitch := 0.0) -> void:
|
||||
PopochiuUtils.e.am.change_cue_pitch(resource_name, pitch)
|
||||
|
||||
|
||||
## Changes the [member AudioStreamPlayer.volume_db] of the [AudioStreamPlayer] playing the audio
|
||||
## file associated with this audio cue to [param volume]. If the audio was played with a 2D
|
||||
## position, then [member AudioStreamPlayer2D.volume_db] will be affected.
|
||||
func change_stream_volume(volume := 0.0) -> void:
|
||||
PopochiuUtils.e.am.change_cue_volume(resource_name, volume)
|
||||
|
||||
|
||||
## Returns the value of [member AudioStreamPlayer.pitch_scale] to be applied to the
|
||||
## [AudioStreamPlayer] playing the audio file associated with this audio cue. If the audio was
|
||||
## played with a 2D position, then [member AudioStreamPlayer2D.volume_db] will be affected.
|
||||
func get_pitch_scale() -> float:
|
||||
var p := PopochiuUtils.a.semitone_to_pitch(pitch)
|
||||
|
||||
if rnd_pitch != Vector2.ZERO:
|
||||
p = _get_rnd_pitch()
|
||||
|
||||
return p
|
||||
|
||||
|
||||
## Returns the playback position of this audio cue.
|
||||
func get_cue_playback_position() -> float:
|
||||
return PopochiuUtils.e.am.get_cue_playback_position(resource_name)
|
||||
|
||||
|
||||
## Maps [param values] to the properties of this audio cue. This is used by TabAudio when changing
|
||||
## the script of the audio cue to one of the types: [AudioCueSound] or [AudioCueMusic].
|
||||
func set_values(values: Dictionary) -> void:
|
||||
resource_name = values.resource_name
|
||||
audio = values.audio
|
||||
loop = values.loop
|
||||
is_2d = values.is_2d
|
||||
pitch = values.pitch
|
||||
volume = values.volume
|
||||
rnd_pitch = values.rnd_pitch
|
||||
rnd_volume = values.rnd_volume
|
||||
max_distance = values.max_distance
|
||||
bus = values.bus
|
||||
|
||||
|
||||
## Returns the properties of this audio cue as a [Dictionary]. This is used by TabAudio when
|
||||
## changing the script of the audio cue to one of the types: [AudioCueSound] or [AudioCueMusic].
|
||||
func get_values() -> Dictionary:
|
||||
return {
|
||||
resource_name = resource_name,
|
||||
audio = audio,
|
||||
loop = loop,
|
||||
is_2d = is_2d,
|
||||
pitch = pitch,
|
||||
volume = volume,
|
||||
rnd_pitch = rnd_pitch,
|
||||
rnd_volume = rnd_volume,
|
||||
max_distance = max_distance,
|
||||
bus = bus
|
||||
}
|
||||
|
||||
|
||||
## Returns [code]true[/code] if playing.
|
||||
func is_playing() -> bool:
|
||||
return PopochiuUtils.a.is_playing_cue(resource_name)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_loop(value: bool) -> void:
|
||||
loop = value
|
||||
|
||||
if not audio: return
|
||||
|
||||
match audio.get_class():
|
||||
'AudioStreamOggVorbis', 'AudioStreamMP3':
|
||||
audio.loop = value
|
||||
'AudioStreamWAV':
|
||||
(audio as AudioStreamWAV).loop_mode = (
|
||||
AudioStreamWAV.LOOP_FORWARD if value else AudioStreamWAV.LOOP_DISABLED
|
||||
)
|
||||
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _get_rnd_pitch() -> float:
|
||||
randomize()
|
||||
return PopochiuUtils.a.semitone_to_pitch(pitch + randf_range(rnd_pitch.x, rnd_pitch.y))
|
||||
|
||||
|
||||
func _get_rnd_volume() -> float:
|
||||
randomize()
|
||||
return volume + randf_range(rnd_volume.x, rnd_volume.y)
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/audio_manager/audio_cue.gd.uid
Normal file
1
addons/popochiu/engine/audio_manager/audio_cue.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://btinlvcono726
|
|
@ -0,0 +1,2 @@
|
|||
extends Resource
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://7s3126auywka
|
23
addons/popochiu/engine/audio_manager/audio_cue_music.gd
Normal file
23
addons/popochiu/engine/audio_manager/audio_cue_music.gd
Normal file
|
@ -0,0 +1,23 @@
|
|||
@tool
|
||||
class_name AudioCueMusic
|
||||
extends PopochiuAudioCue
|
||||
## A specific type of [PopochiuAudioCue] designed for playing music.
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue. It can fade for [param fade_duration] seconds, and you can change the track
|
||||
## starting position in seconds with [param music_position].
|
||||
func play(fade_duration := 0.0, music_position := 0.0) -> void:
|
||||
PopochiuUtils.e.am.play_music_cue(resource_name, fade_duration, music_position)
|
||||
|
||||
|
||||
## Plays this audio cue. It can fade for [param fade_duration] seconds, and you can change the track
|
||||
## starting position in seconds with [param music_position].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_play(fade_duration := 0.0, music_position := 0.0) -> Callable:
|
||||
return func ():
|
||||
await play(fade_duration, music_position)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bccg7ywc586ng
|
31
addons/popochiu/engine/audio_manager/audio_cue_sound.gd
Normal file
31
addons/popochiu/engine/audio_manager/audio_cue_sound.gd
Normal file
|
@ -0,0 +1,31 @@
|
|||
@tool
|
||||
class_name AudioCueSound
|
||||
extends PopochiuAudioCue
|
||||
## A specific type of [PopochiuAudioCue] designed for playing sounds.
|
||||
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Plays this audio cue. If [param wait_to_end] is set to [code]true[/code], the function will pause
|
||||
## until the audio clip finishes. You can play the file from a specific [param position_2d] in the
|
||||
## scene if [member PopochiuAudioCue.is_2d] is [code]true[/code].
|
||||
func play(wait_to_end := false, position_2d := Vector2.ZERO) -> void:
|
||||
if wait_to_end:
|
||||
await PopochiuUtils.e.am.play_sound_cue(resource_name, position_2d, true)
|
||||
else:
|
||||
PopochiuUtils.e.am.play_sound_cue(resource_name, position_2d)
|
||||
|
||||
|
||||
## Plays this audio cue. If [param wait_to_end] is set to [code]true[/code], the function will pause
|
||||
## until the audio clip finishes. You can play the file from a specific [param position_2d] in the
|
||||
## scene if [member PopochiuAudioCue.is_2d] is [code]true[/code].[br][br]
|
||||
## [i]This method is intended to be used inside a [method Popochiu.queue] of instructions.[/i]
|
||||
func queue_play(wait_to_end := false, position_2d := Vector2.ZERO) -> Callable:
|
||||
return func ():
|
||||
if wait_to_end:
|
||||
await play(true, position_2d)
|
||||
else:
|
||||
play(false, position_2d)
|
||||
await PopochiuUtils.e.get_tree().process_frame
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cqbmt4xruu8eo
|
403
addons/popochiu/engine/audio_manager/audio_manager.gd
Normal file
403
addons/popochiu/engine/audio_manager/audio_manager.gd
Normal file
|
@ -0,0 +1,403 @@
|
|||
class_name PopochiuAudioManager
|
||||
extends Node
|
||||
## Handles playing audio using [PopochiuAudioCue]s.
|
||||
##
|
||||
## It plays sound effects and music using [AudioStreamPlayer] or [AudioStreamPlayer2D], creating
|
||||
## these nodes at runtime if needed. By default, it has 6 nodes for positional streams and 5 for
|
||||
## playing non-positional streams.[br][br]
|
||||
## The [b]PopochiuAudioManager[/b] is loaded as a child of [Popochiu] when the game starts.
|
||||
|
||||
## Used to mark stream players created at runtime that should be [method Node.free] when they are
|
||||
## no longer needed.
|
||||
const TEMP_PLAYER := "temporal"
|
||||
## Specifies the path where the volume configuration for the audio buses used in the game is stored.
|
||||
const SETTINGS_PATH = "user://audio_settings.save"
|
||||
|
||||
## Used to convert the value of the pitch set on [member PopochiuAudioCue.pitch] to the
|
||||
## corresponding value needed for the [code]pitch_scale[/code] property of the audio stream players.
|
||||
var twelfth_root_of_two := pow(2, (1.0 / 12))
|
||||
## Stores the volume values for each of the audio buses used by the game.
|
||||
var volume_settings := {}
|
||||
|
||||
var _mx_cues := {}
|
||||
var _sfx_cues := {}
|
||||
var _vo_cues := {}
|
||||
var _ui_cues := {}
|
||||
var _active := {}
|
||||
var _all_in_one := {}
|
||||
# Serves as a map that stores an AudioStreamPlayer/AudioStreamPlayer2D and the tween used to fade
|
||||
# its volume
|
||||
var _fading_sounds := {}
|
||||
var _dflt_volumes := {}
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
for bus_idx in range(AudioServer.get_bus_count()):
|
||||
var bus_name = AudioServer.get_bus_name(bus_idx)
|
||||
volume_settings[bus_name] = AudioServer.get_bus_volume_db(bus_idx)
|
||||
|
||||
for arr in ["mx_cues", "sfx_cues", "vo_cues", "ui_cues"]:
|
||||
for rp in PopochiuResources.get_data_value("audio", arr, []):
|
||||
var ac: PopochiuAudioCue = load(rp)
|
||||
|
||||
self["_%s" % arr][ac.resource_name] = ac
|
||||
_all_in_one[ac.resource_name] = ac
|
||||
_dflt_volumes[ac.resource_name] = ac.volume
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Searches for the [PopochiuAudioCue] identified by [param cue_name] among the cues that are NOT
|
||||
## part of the music group and plays it. You can set a [param position_2d] to play it positionally.
|
||||
## If [param wait_to_end] is set to [code]true[/code], the function will pause until the audio
|
||||
## finishes.
|
||||
func play_sound_cue(cue_name := "", position_2d := Vector2.ZERO, wait_to_end = null) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _all_in_one.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _all_in_one[cue_name.to_lower()]
|
||||
stream_player = _play(cue, position_2d)
|
||||
else:
|
||||
PopochiuUtils.print_error("Sound not found: " + cue_name)
|
||||
|
||||
if wait_to_end != null:
|
||||
await get_tree().process_frame
|
||||
|
||||
return null
|
||||
|
||||
if wait_to_end == true and stream_player:
|
||||
await stream_player.finished
|
||||
elif wait_to_end == false:
|
||||
await get_tree().process_frame
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Searches for the [PopochiuAudioCue] identified by [param cue_name] among the cues that are part
|
||||
## of the music group and plays it. It can fade for [param fade_duration] seconds, and you can start
|
||||
## playing it from a given [param music_position] in seconds.
|
||||
func play_music_cue(cue_name: String, fade_duration := 0.0, music_position := 0.0) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _active.has(cue_name):
|
||||
return _active[cue_name].players[0]
|
||||
|
||||
if _mx_cues.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _mx_cues[cue_name.to_lower()]
|
||||
# NOTE: fixes #27 AudioCues were losing the volume set in editor when
|
||||
# played with a fade
|
||||
cue.volume = _dflt_volumes[cue_name.to_lower()]
|
||||
if fade_duration > 0.0:
|
||||
stream_player = _fade_in(
|
||||
cue,
|
||||
Vector2.ZERO,
|
||||
fade_duration,
|
||||
-80.0,
|
||||
cue.volume,
|
||||
music_position
|
||||
)
|
||||
else:
|
||||
stream_player = _play(cue, Vector2.ZERO, music_position)
|
||||
else:
|
||||
PopochiuUtils.print_error("Music not found: " + cue_name)
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Plays the [PopochiuAudioCue] identified by [param cue_name] using a fade that will last
|
||||
## [param duration] seconds. Specify the starting volume with [param from] and the target volume
|
||||
## with [param to]. You can play the audio positionally with [param position_2d] and wait for it
|
||||
## to finish if [param wait_to_end] is set to [code]true[/code].
|
||||
func play_fade_cue(
|
||||
cue_name := "",
|
||||
duration := 1.0,
|
||||
from := -80.0,
|
||||
to := INF,
|
||||
position_2d := Vector2.ZERO,
|
||||
wait_to_end = null
|
||||
) -> Node:
|
||||
var stream_player: Node = null
|
||||
|
||||
if _all_in_one.has(cue_name.to_lower()):
|
||||
var cue: PopochiuAudioCue = _all_in_one[cue_name.to_lower()]
|
||||
stream_player = _fade_in(cue, position_2d, duration, from, to if to != INF else cue.volume)
|
||||
else:
|
||||
PopochiuUtils.print_error("Sound to fade not found " + cue_name)
|
||||
|
||||
if wait_to_end != null:
|
||||
await get_tree().process_frame
|
||||
|
||||
return null
|
||||
|
||||
if wait_to_end == true and stream_player:
|
||||
await stream_player.finished
|
||||
elif wait_to_end == false:
|
||||
await get_tree().process_frame
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
## Stops the [PopochiuAudioCue] identified by [param cue_name]. It can use a fade effect that will
|
||||
## last [param fade_duration] seconds.
|
||||
func stop(cue_name: String, fade_duration := 0.0) -> void:
|
||||
if _active.has(cue_name):
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if is_instance_valid(stream_player):
|
||||
if fade_duration > 0.0:
|
||||
_fade_sound(
|
||||
cue_name, fade_duration, stream_player.volume_db, -80.0
|
||||
)
|
||||
else:
|
||||
stream_player.stop()
|
||||
# Always emit the signal since it won't be emitted if the audio
|
||||
# file haven't reach the end yet
|
||||
stream_player.finished.emit()
|
||||
else:
|
||||
_active.erase(cue_name)
|
||||
|
||||
|
||||
## Returns the playback position of the [PopochiuAudioCue] identified by [param cue_name].
|
||||
func get_cue_playback_position(cue_name: String) -> float:
|
||||
if not _active.has(cue_name): return -1.0
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if is_instance_valid(stream_player):
|
||||
return stream_player.get_playback_position()
|
||||
|
||||
return -1.0
|
||||
|
||||
|
||||
## Changes the [code]pitch_scale[/code] of the [PopochiuAudioCue] identified by [param cue_name] to
|
||||
## the value set (in semitones) in [param pitch].
|
||||
func change_cue_pitch(cue_name: String, pitch := 0.0) -> void:
|
||||
if not _active.has(cue_name): return
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
stream_player.set_pitch_scale(_semitone_to_pitch(pitch))
|
||||
|
||||
|
||||
## Changes the [code]volume_db[/code] of the [PopochiuAudioCue] identified by [param cue_name] to
|
||||
## the value set in [param volume].
|
||||
func change_cue_volume(cue_name: String, volume := 0.0) -> void:
|
||||
if not _active.has(cue_name): return
|
||||
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
stream_player.volume_db = volume
|
||||
|
||||
|
||||
## Sets [param value] as the volume of the audio bus identified with [param bus_name].
|
||||
func set_bus_volume_db(bus_name: String, value: float) -> void:
|
||||
if volume_settings.has(bus_name):
|
||||
volume_settings[bus_name] = value
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(bus_name), volume_settings[bus_name]
|
||||
)
|
||||
|
||||
save_sound_settings()
|
||||
|
||||
|
||||
## Saves in the file at [constant SETTINGS_PATH] the volume values of all the audio buses.
|
||||
func save_sound_settings():
|
||||
var file = FileAccess.open(SETTINGS_PATH, FileAccess.WRITE)
|
||||
|
||||
if file == null:
|
||||
PopochiuUtils.print_error("Error opening file: " + SETTINGS_PATH)
|
||||
else:
|
||||
file.store_var(volume_settings)
|
||||
file.close()
|
||||
|
||||
|
||||
## Loads the volume values stored at [constant SETTINGS_PATH] for all the audio buses.
|
||||
func load_sound_settings():
|
||||
var file = FileAccess.open(SETTINGS_PATH, FileAccess.READ)
|
||||
|
||||
if file:
|
||||
volume_settings = file.get_var(true)
|
||||
file.close()
|
||||
for bus_idx in range(AudioServer.get_bus_count()):
|
||||
var bus_name = AudioServer.get_bus_name(bus_idx)
|
||||
if volume_settings.has(bus_name):
|
||||
AudioServer.set_bus_volume_db(
|
||||
AudioServer.get_bus_index(bus_name),
|
||||
volume_settings[bus_name]
|
||||
)
|
||||
else:
|
||||
volume_settings[bus_name] = AudioServer.get_bus_volume_db(bus_idx)
|
||||
|
||||
|
||||
## Returns [code]true[/code] if the [PopochiuAudioCue] identified by [param cue_name] is playing.
|
||||
func is_playing_cue(cue_name: String) -> bool:
|
||||
return get_cue_playback_position(cue_name) > -1
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
# Calculates the [code]pitch_scale[/code] value of [param pitch], which is in semitones.
|
||||
func _semitone_to_pitch(pitch: float) -> float:
|
||||
return pow(twelfth_root_of_two, pitch)
|
||||
|
||||
|
||||
# Plays the sound and assigns it to a free AudioStreamPlayer, or creates one if there are no more.
|
||||
func _play(
|
||||
cue: PopochiuAudioCue, position := Vector2.ZERO, from_position := 0.0
|
||||
) -> Node:
|
||||
var player: Node = null
|
||||
|
||||
if cue.is_2d:
|
||||
player = _get_free_stream($Positional)
|
||||
|
||||
if not is_instance_valid(player):
|
||||
player = AudioStreamPlayer2D.new()
|
||||
player.set_meta(TEMP_PLAYER, true)
|
||||
$Active.add_child(player)
|
||||
|
||||
(player as AudioStreamPlayer2D).stream = cue.audio
|
||||
(player as AudioStreamPlayer2D).pitch_scale = cue.get_pitch_scale()
|
||||
(player as AudioStreamPlayer2D).volume_db = cue.volume
|
||||
(player as AudioStreamPlayer2D).max_distance = cue.max_distance
|
||||
(player as AudioStreamPlayer2D).position = position
|
||||
else:
|
||||
player = _get_free_stream($Generic)
|
||||
|
||||
if not is_instance_valid(player):
|
||||
player = AudioStreamPlayer.new()
|
||||
player.set_meta(TEMP_PLAYER, true)
|
||||
$Active.add_child(player)
|
||||
|
||||
(player as AudioStreamPlayer).stream = cue.audio
|
||||
(player as AudioStreamPlayer).pitch_scale = cue.get_pitch_scale()
|
||||
(player as AudioStreamPlayer).volume_db = cue.volume
|
||||
|
||||
var cue_name: String = cue.resource_name
|
||||
|
||||
player.bus = cue.bus
|
||||
player.play(from_position)
|
||||
|
||||
if not player.finished.is_connected(_on_audio_stream_player_finished):
|
||||
player.finished.connect(_on_audio_stream_player_finished.bind(player, cue_name, 0))
|
||||
|
||||
if _active.has(cue_name):
|
||||
_active[cue_name].players.append(player)
|
||||
|
||||
# NOTE: Stop the previous stream player created to play the audio cue
|
||||
# that is in loop to avoid having more than one sound playing.
|
||||
if not _active[cue_name].can_play_simultaneous:
|
||||
stop(cue_name)
|
||||
else:
|
||||
_active[cue_name] = {
|
||||
players = [player],
|
||||
loop = cue.loop,
|
||||
can_play_simultaneous = cue.can_play_simultaneous
|
||||
}
|
||||
|
||||
return player
|
||||
|
||||
|
||||
func _get_free_stream(group: Node):
|
||||
return _reparent(group, $Active, 0)
|
||||
|
||||
|
||||
# Reassigns the [AudioStreamPlayer] to its original group when it finishes so it can be available
|
||||
# for being used again.
|
||||
func _on_audio_stream_player_finished(
|
||||
stream_player: Node, cue_name: String, _debug_idx: int
|
||||
) -> void:
|
||||
if stream_player.has_meta(TEMP_PLAYER):
|
||||
stream_player.queue_free()
|
||||
elif stream_player is AudioStreamPlayer:
|
||||
_reparent($Active, $Generic, stream_player.get_index())
|
||||
else:
|
||||
_reparent($Active, $Positional, stream_player.get_index())
|
||||
|
||||
if _active.has(cue_name):
|
||||
var players: Array = _active[cue_name].players
|
||||
for idx in players.size():
|
||||
if players[idx].get_instance_id() == stream_player.get_instance_id():
|
||||
players.remove_at(idx)
|
||||
break
|
||||
|
||||
if players.is_empty():
|
||||
_active.erase(cue_name)
|
||||
|
||||
if not stream_player.finished.is_connected(_on_audio_stream_player_finished):
|
||||
stream_player.finished.connect(_on_audio_stream_player_finished)
|
||||
|
||||
|
||||
func _reparent(source: Node, target: Node, child_idx: int) -> Node:
|
||||
if not is_instance_valid(source) or source.get_children().is_empty():
|
||||
return null
|
||||
|
||||
var node_to_reparent: Node = source.get_child(child_idx)
|
||||
|
||||
if not is_instance_valid(node_to_reparent):
|
||||
return null
|
||||
|
||||
node_to_reparent.reparent(target)
|
||||
|
||||
return node_to_reparent
|
||||
|
||||
|
||||
func _fade_in(
|
||||
cue: PopochiuAudioCue,
|
||||
position: Vector2,
|
||||
duration := 1.0,
|
||||
from := -80.0,
|
||||
to := 0.0,
|
||||
from_position := 0.0
|
||||
) -> Node:
|
||||
if cue.audio.get_instance_id() in _fading_sounds:
|
||||
from = _fading_sounds[cue.audio.get_instance_id()].stream.volume_db
|
||||
|
||||
var tween: Tween = _fading_sounds[cue.audio.get_instance_id()].tween
|
||||
# Stop the tween only of the sound that is fading
|
||||
|
||||
if is_instance_valid(tween) and tween.is_running():
|
||||
tween.kill()
|
||||
|
||||
_fading_sounds[cue.audio.get_instance_id()].finished.emit()
|
||||
_fading_sounds.erase(cue.audio.get_instance_id())
|
||||
|
||||
cue.volume = from
|
||||
|
||||
var stream_player: Node = _play(cue, position, from_position)
|
||||
|
||||
if stream_player:
|
||||
_fade_sound(cue.resource_name, duration, from, to)
|
||||
else:
|
||||
cue.volume = to
|
||||
|
||||
return stream_player
|
||||
|
||||
|
||||
func _fade_sound(cue_name: String, duration = 1, from = 0, to = 0) -> void:
|
||||
var stream_player: Node = (_active[cue_name].players as Array).front()
|
||||
|
||||
if _fading_sounds.has(stream_player.stream.get_instance_id()):
|
||||
_fading_sounds[stream_player.stream.get_instance_id()].tween.kill()
|
||||
|
||||
var t := create_tween().set_ease(Tween.EASE_IN_OUT)
|
||||
t.finished.connect(_fadeout_finished.bind(stream_player, t))
|
||||
t.tween_property(stream_player, "volume_db", to, duration).from(from)
|
||||
|
||||
if from > to :
|
||||
_fading_sounds[stream_player.stream.get_instance_id()] = {
|
||||
stream = stream_player,
|
||||
tween = t
|
||||
}
|
||||
|
||||
|
||||
func _fadeout_finished(stream_player: Node, tween: Tween) -> void:
|
||||
if stream_player.stream.get_instance_id() in _fading_sounds :
|
||||
_fading_sounds.erase(stream_player.stream.get_instance_id())
|
||||
stream_player.stop()
|
||||
tween.finished.disconnect(_fadeout_finished)
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://bngbmwj65yxut
|
34
addons/popochiu/engine/audio_manager/audio_manager.tscn
Normal file
34
addons/popochiu/engine/audio_manager/audio_manager.tscn
Normal file
|
@ -0,0 +1,34 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://ciufytcstpkcr"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/audio_manager/audio_manager.gd" id="1_3dbpw"]
|
||||
|
||||
[node name="AudioManager" type="Node"]
|
||||
script = ExtResource("1_3dbpw")
|
||||
|
||||
[node name="Positional" type="Node" parent="."]
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D2" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D3" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D4" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D5" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="AudioStreamPlayer2D6" type="AudioStreamPlayer2D" parent="Positional"]
|
||||
|
||||
[node name="Generic" type="Node" parent="."]
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer2" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer3" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer4" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="AudioStreamPlayer5" type="AudioStreamPlayer" parent="Generic"]
|
||||
|
||||
[node name="Active" type="Node" parent="."]
|
Loading…
Add table
Add a link
Reference in a new issue