127 lines
3.7 KiB
GDScript
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)
|