From e4c6ff5e89cff4a2bee09a0f2a56d3314645d5b3 Mon Sep 17 00:00:00 2001 From: blujai831 Date: Tue, 13 Feb 2024 10:26:41 -0800 Subject: [PATCH] Initial untested prototype of AmbienceInterpolator, a class for crossfading between audio streams in an array representing different stages or magnitudes of the same general category of ambience. --- audio/ambience/AmbienceInterpolator.gd | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 audio/ambience/AmbienceInterpolator.gd diff --git a/audio/ambience/AmbienceInterpolator.gd b/audio/ambience/AmbienceInterpolator.gd new file mode 100644 index 0000000..2680b20 --- /dev/null +++ b/audio/ambience/AmbienceInterpolator.gd @@ -0,0 +1,126 @@ +class_name AmbienceInterpolator extends Node +## Interpolates between multiple ambience streams. + +## 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 +## Array of ambience streams to interpolate between. +## +## If an element is a sub-array, its elements in turn play simultaneously. +@export var streams: Array = [] + +## Tween channel for interpolation index. +@onready var _tween_channel := TweenChannel.make_replacing() + +## Array of audio players, one for each ambience stream. +var _players: Array = [] +## Current interpolation index. +## +## This value times streams.size() will be offset by +1 +## from the value whose ceil and ceil-1 +## will actually be used to index the arrays. +## For example, if _ii == 2.5/streams.size(), +## then array indices 1 and 2 will be used, +## not array indices 2 and 3. +## If _ii < 1.0/streams.size(), +## it interpolates between silence and array index 0. +var _ii: float = 0.0 + +## Creates an audio player for each audio stream. +func _ready() -> void: + for elem in streams: + if elem is Array: + var mapping: Array = [] + _players.push_back(mapping) + for stream in elem: + assert(stream is AudioStream) + var player := AudioStreamPlayer.new() + add_child(player) + mapping.push_back(player) + player.bus = bus + player.stream = stream + else: + assert(elem is AudioStream) + var player := AudioStreamPlayer.new() + add_child(player) + _players.push_back(player) + player.bus = bus + player.stream = elem + +## Getter for interpolation index. +func get_interpolation_index() -> float: + return _ii + +## Sets interpolation index and adjusts audio player levels accordingly. +func set_interpolation_index(ii: float) -> void: + # Calculate interpolation factor and array indices. + var scaled: float = ii*_players.size() + var interp_fact: float = scaled - floorf(scaled) + var i_hi: int = ceili(scaled) - 1 + if i_hi >= _players.size(): + i_hi = _players.size() - 1 + var i_lo: int = i_hi - 1 + # Get audio players from array. + var p_hi: Variant = null + var p_lo: Variant = null + if i_hi >= 0: + p_hi = _players[i_hi] + if i_lo >= 0: + p_lo = _players[i_lo] + # Calculate volumes to set. + var v_lo: float = lerpf(0.0, AUDIO_FADE_MIN_VOLUME, interp_fact) + var v_hi: float = lerpf(AUDIO_FADE_MIN_VOLUME, 0.0, interp_fact) + # Set audio player volumes and start audio players. + if p_lo is Array: + for player in p_lo: + assert(player is AudioStreamPlayer) + player.volume_db = v_lo + if not player.playing: + player.play() + elif p_lo != null: + assert(p_lo is AudioStreamPlayer) + p_lo.volume_db = v_lo + if not p_lo.playing: + p_lo.play() + if p_hi is Array: + for player in p_hi: + assert(player is AudioStreamPlayer) + player.volume_db = v_hi + if not player.playing: + player.play() + elif p_hi != null: + assert(p_hi is AudioStreamPlayer) + p_hi.volume_db = v_hi + if not p_hi.playing: + p_hi.play() + # Stop every audio player we have that isn't one of the two requested. + for elem in _players: + if elem is Array: + for player in elem: + assert(player is AudioStreamPlayer) + if player != p_lo and player != p_hi: + player.volume_db = AUDIO_FADE_MIN_VOLUME + if player.playing: + player.stop() + else: + assert(elem is AudioStreamPlayer) + if elem != p_lo and elem != p_hi: + elem.volume_db = AUDIO_FADE_MIN_VOLUME + if elem.playing: + elem.stop() + # Update stored value of interpolation index. + _ii = ii + +## Tweens the interpolation index to the given target over time. +func interpolate(target: float, fade: float = DEFAULT_AUDIO_FADE) -> void: + var tween := await _tween_channel.create_tween(self) + tween.tween_method( + set_interpolation_index, + get_interpolation_index(), + target, fade + ) + await _tween_channel.sync(tween)