stick-the-quick/audio/ambience/AmbienceInterpolator.gd

127 lines
3.7 KiB
GDScript

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)