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)