Files
godot-interaction-system/Scripts/SpatialAudioPlayer.gd
T

133 lines
5.0 KiB
GDScript

extends AudioStreamPlayer3D
class_name SpatialAudioPlayer
@export var maxRaycastDistance: float = 30.0
@export var updateFrequencySeconds: float = 0.5
@export var maxReverbWetness: float = 0.5
@export var wallLowpassCutoffAmount: int = 600
var raycastArray: Array = []
var distanceArray: Array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
var lastUpdateTime: float = 0.0
var updateDistances: bool = true
var currentRaycastIndex: int = 0
var audioBusIndex = null
var audioBusName: String = ""
var reverbEffect: AudioEffectReverb
var lowpassFilterEffect: AudioEffectLowPassFilter
var targetLowpassCutoff: float = 20000
var targetReverbRoomSize: float = 0.0
var targetReverbWetness: float = 0.0
var targetVolumeDb: float = 0.0
func _ready() -> void:
audioBusIndex = AudioServer.bus_count
audioBusName = "SpatialBus#"+str(audioBusIndex)
AudioServer.add_bus(audioBusIndex)
AudioServer.set_bus_name(audioBusIndex, audioBusName)
AudioServer.set_bus_send(audioBusIndex, bus)
self.bus = audioBusName
AudioServer.add_bus_effect(audioBusIndex, AudioEffectReverb.new(), 0)
reverbEffect = AudioServer.get_bus_effect(audioBusIndex, 0)
AudioServer.add_bus_effect(audioBusIndex, AudioEffectLowPassFilter.new(), 1)
lowpassFilterEffect = AudioServer.get_bus_effect(audioBusIndex, 1)
targetVolumeDb = volume_db
volume_db = -60
$RayCastDown.target_position = Vector3(0, -maxRaycastDistance, 0)
$RayCastLeft.target_position = Vector3(maxRaycastDistance, 0, 0)
$RayCastRight.target_position = Vector3(-maxRaycastDistance, 0, 0)
$RayCastForward.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastForwardLeft.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastForwardRight.target_position = Vector3(0, 0, maxRaycastDistance)
$RayCastBackwardRight.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastBackwardLeft.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastBackward.target_position = Vector3(0, 0, -maxRaycastDistance)
$RayCastUp.target_position = Vector3(0, maxRaycastDistance, 0)
raycastArray.append($RayCastDown)
raycastArray.append($RayCastLeft)
raycastArray.append($RayCastRight)
raycastArray.append($RayCastForward)
raycastArray.append($RayCastForwardLeft)
raycastArray.append($RayCastForwardRight)
raycastArray.append($RayCastBackwardRight)
raycastArray.append($RayCastBackwardLeft)
raycastArray.append($RayCastBackward)
raycastArray.append($RayCastUp)
play()
func onUpdateRaycastDistance(raycast: RayCast3D, raycastIndex: int) -> void:
raycast.force_raycast_update()
var collider = raycast.get_collider()
if collider != null:
distanceArray[raycastIndex] = self.global_position.distance_to(raycast.get_collision_point())
else:
distanceArray[raycastIndex] = -1
raycast.enabled = false
func onUpdateSpatialAudio(player: Node3D) -> void:
onUpdateReverb(player)
onUpdateLowpassFilter(player)
func onUpdateReverb(player: Node3D) -> void:
if reverbEffect != null:
var roomSize: float = 0.0
var wetness: float = 0.0
for distance in distanceArray:
if distance >= 0:
roomSize += (distance / maxRaycastDistance) / (float(distanceArray.size()))
roomSize = min(roomSize, 1.0)
else:
wetness -= 1.0 / float(distanceArray.size())
wetness = max(wetness, 0.0)
targetReverbWetness = wetness
targetReverbRoomSize = roomSize
func onUpdateLowpassFilter(player : Node3D):
if lowpassFilterEffect != null:
var query = PhysicsRayQueryParameters3D.create(self.global_position,self.global_position + (player.global_position - self.global_position).normalized() * maxRaycastDistance, $RayCastForward.get_collision_mask())
var spaceState = get_world_3d().direct_space_state
var result = spaceState.intersect_ray(query)
var lowpassCutoff = 20000
if !result.is_empty():
var rayDistance = self.global_position.distance_to(result["position"])
var distanceToPlayer = self.global_position.distance_to(player.global_position)
var wallToPlayerRatio = rayDistance / max(distanceToPlayer, 0.001)
if rayDistance < distanceToPlayer:
lowpassCutoff = wallLowpassCutoffAmount * wallToPlayerRatio
targetLowpassCutoff = lowpassCutoff
func lerpParameters(delta: float) -> void:
volume_db = lerp(volume_db, targetVolumeDb, delta)
lowpassFilterEffect.cutoff_hz = lerp(lowpassFilterEffect.cutoff_hz, targetLowpassCutoff, delta * 5.0)
reverbEffect.wet = lerp(reverbEffect.wet, targetReverbRoomSize * maxReverbWetness, delta * 5.0)
reverbEffect.room_size = lerp(reverbEffect.room_size, targetReverbRoomSize, delta * 5.0)
func _physics_process(delta: float) -> void:
lastUpdateTime += delta
if updateDistances:
onUpdateRaycastDistance(raycastArray[currentRaycastIndex], currentRaycastIndex)
currentRaycastIndex += 1
if currentRaycastIndex >= distanceArray.size():
currentRaycastIndex = 0
updateDistances = false
if lastUpdateTime > updateFrequencySeconds:
var playerCamera = get_viewport().get_camera_3d()
if playerCamera != null:
onUpdateSpatialAudio(playerCamera)
updateDistances = true
lastUpdateTime = 0.0
lerpParameters(delta)
print("Audio:", global_position)
print("Camera:", get_viewport().get_camera_3d().global_position)