class_name AudioFader extends Node ## Controls an AudioStreamPlayer and allows for fading and crossfading. ## Audio fade-out target. const AUDIO_FADE_MIN_VOLUME: float = -20.0 ## Default audio transition time. const DEFAULT_AUDIO_FADE: float = 1.0 ## Audio bus. @export var bus: StringName ## Current AudioStreamPlayer. var player: AudioStreamPlayer = null ## Current TweenChannel. var tween_channel: TweenChannel = null ## Creates an initial AudioStreamPlayer child and TweenChannel. func _ready() -> void: _create_members() ## Creates an AudioStreamPlayer child and TweenChannel. func _create_members() -> void: player = AudioStreamPlayer.new() add_child(player) tween_channel = TweenChannel.make_replacing() ## Fades in the current audio player. func fade_in(stream: AudioStream, fade: float = DEFAULT_AUDIO_FADE) -> void: await fade_out() player.volume_db = AUDIO_FADE_MIN_VOLUME player.stream = stream player.play() var tween := await tween_channel.create_tween(player) tween.tween_property(player, ^'volume_db', 0.0, fade) await tween_channel.sync(tween) ## Fades out an arbitrary audio player on an arbitrary tween channel. static func _fade_out_foreign( p_player: AudioStreamPlayer, p_tween_channel: TweenChannel, fade: float = DEFAULT_AUDIO_FADE ) -> void: if p_player.volume_db > AUDIO_FADE_MIN_VOLUME: var tween := await p_tween_channel.create_tween(p_player) tween.tween_property( p_player, ^'volume_db', AUDIO_FADE_MIN_VOLUME, fade ) await p_tween_channel.sync(tween) p_player.stop() ## Fades out the current audio player. func fade_out(fade: float = DEFAULT_AUDIO_FADE) -> void: await AudioFader._fade_out_foreign(player, tween_channel, fade) ## Plays audio without fading in. func play(stream: AudioStream) -> void: await player.fade_out() player.volume_db = 0.0 player.stream = stream player.play() ## Fades out old audio and fades in new audio at the same time. func crossfade(stream: AudioStream, fade: float = DEFAULT_AUDIO_FADE) -> void: var old_player := player var old_tween_channel := tween_channel var thunk := func() -> void: await AudioFader._fade_out_foreign(old_player, old_tween_channel, fade) old_player.queue_free() _create_members() await Wait.until(player.is_node_ready) var task := Task.new(thunk) await fade_in(stream, fade) await task.sync()