forked from woofmeow/straywild
fix: quiet distant and replayed chat voices
This commit is contained in:
parent
f0ddcccaad
commit
c59328b524
4 changed files with 95 additions and 15 deletions
|
|
@ -556,7 +556,10 @@ func receive_chat_message(data: Dictionary) -> void:
|
||||||
_apply_message(data)
|
_apply_message(data)
|
||||||
|
|
||||||
|
|
||||||
func _apply_message(data: Dictionary) -> void:
|
func _apply_message(
|
||||||
|
data: Dictionary,
|
||||||
|
emit_live_signals: bool = true,
|
||||||
|
) -> void:
|
||||||
if (
|
if (
|
||||||
not NetworkChatProtocol.validate_message(data)
|
not NetworkChatProtocol.validate_message(data)
|
||||||
or str(data["session_id"]) != _session.get_session_id()
|
or str(data["session_id"]) != _session.get_session_id()
|
||||||
|
|
@ -597,7 +600,7 @@ func _apply_message(data: Dictionary) -> void:
|
||||||
_history.append(stored_message)
|
_history.append(stored_message)
|
||||||
while _history.size() > NetworkChatProtocol.MAX_HISTORY:
|
while _history.size() > NetworkChatProtocol.MAX_HISTORY:
|
||||||
_history.pop_front()
|
_history.pop_front()
|
||||||
if _message_is_visible(stored_message):
|
if emit_live_signals and _message_is_visible(stored_message):
|
||||||
message_received.emit(stored_message.duplicate(true))
|
message_received.emit(stored_message.duplicate(true))
|
||||||
if (
|
if (
|
||||||
kind == NetworkChatProtocol.Kind.PLAYER
|
kind == NetworkChatProtocol.Kind.PLAYER
|
||||||
|
|
@ -653,7 +656,10 @@ func receive_chat_history(values: Array) -> void:
|
||||||
_seen_messages.clear()
|
_seen_messages.clear()
|
||||||
for value: Variant in values:
|
for value: Variant in values:
|
||||||
if NetworkChatProtocol.validate_message(value):
|
if NetworkChatProtocol.validate_message(value):
|
||||||
_apply_message(value)
|
# Late-join history belongs in the scrollback, but it is not a live
|
||||||
|
# event. In particular, do not recreate speech bubbles or replay
|
||||||
|
# animalese for every stored player message when joining a room.
|
||||||
|
_apply_message(value, false)
|
||||||
history_replaced.emit(get_history())
|
history_replaced.emit(get_history())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ extends SceneTree
|
||||||
const MainScene: PackedScene = preload("res://main/main.tscn")
|
const MainScene: PackedScene = preload("res://main/main.tscn")
|
||||||
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
const AnimaleseVoiceType = preload("res://ui/animalese_voice.gd")
|
||||||
const TEST_PORT: int = 18144
|
const TEST_PORT: int = 18144
|
||||||
|
const HOST_HISTORY_MESSAGE: String = "silent history replay"
|
||||||
const HOST_VOICE_MESSAGE: String = "remote voice playback"
|
const HOST_VOICE_MESSAGE: String = "remote voice playback"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ func _initialize() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _run() -> void:
|
func _run() -> void:
|
||||||
|
_validate_voice_distance_attenuation()
|
||||||
var arguments: PackedStringArray = OS.get_cmdline_user_args()
|
var arguments: PackedStringArray = OS.get_cmdline_user_args()
|
||||||
if arguments.has("host"):
|
if arguments.has("host"):
|
||||||
await _run_host()
|
await _run_host()
|
||||||
|
|
@ -22,6 +24,24 @@ func _run() -> void:
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_voice_distance_attenuation() -> void:
|
||||||
|
assert(is_zero_approx(
|
||||||
|
ChatUI.animalese_volume_offset_db_for_distance(0.0)
|
||||||
|
))
|
||||||
|
assert(is_zero_approx(
|
||||||
|
ChatUI.animalese_volume_offset_db_for_distance(4.0)
|
||||||
|
))
|
||||||
|
var middle_distance_volume := (
|
||||||
|
ChatUI.animalese_volume_offset_db_for_distance(14.0)
|
||||||
|
)
|
||||||
|
assert(middle_distance_volume < 0.0)
|
||||||
|
assert(middle_distance_volume > -80.0)
|
||||||
|
assert(is_equal_approx(
|
||||||
|
ChatUI.animalese_volume_offset_db_for_distance(24.0),
|
||||||
|
-80.0,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
func _run_host() -> void:
|
func _run_host() -> void:
|
||||||
var main: Node = await _create_initialized_main()
|
var main: Node = await _create_initialized_main()
|
||||||
var session := main.get_node("%NetworkSession") as NetworkSession
|
var session := main.get_node("%NetworkSession") as NetworkSession
|
||||||
|
|
@ -51,6 +71,11 @@ func _run_host() -> void:
|
||||||
var chat_service := main.get_node(
|
var chat_service := main.get_node(
|
||||||
"%NetworkChatService"
|
"%NetworkChatService"
|
||||||
) as NetworkChatService
|
) as NetworkChatService
|
||||||
|
assert(chat_service.send_local_message(
|
||||||
|
HOST_HISTORY_MESSAGE,
|
||||||
|
profile.voice_id,
|
||||||
|
profile.sample_set_id,
|
||||||
|
))
|
||||||
var voice_messages: Array[Dictionary] = []
|
var voice_messages: Array[Dictionary] = []
|
||||||
chat_service.message_received.connect(func(message: Dictionary) -> void:
|
chat_service.message_received.connect(func(message: Dictionary) -> void:
|
||||||
if str(message.get("body", "")) == "voice profile sync":
|
if str(message.get("body", "")) == "voice profile sync":
|
||||||
|
|
@ -124,6 +149,22 @@ func _run_client() -> void:
|
||||||
assert(session.supports_server_capability(
|
assert(session.supports_server_capability(
|
||||||
NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY
|
NetworkProtocol.APPEARANCE_PREVIEW_CAPABILITY
|
||||||
))
|
))
|
||||||
|
var chat_service := main.get_node(
|
||||||
|
"%NetworkChatService"
|
||||||
|
) as NetworkChatService
|
||||||
|
var history_deadline: int = Time.get_ticks_msec() + 8000
|
||||||
|
while Time.get_ticks_msec() < history_deadline:
|
||||||
|
if chat_service.get_history().any(
|
||||||
|
func(message: Dictionary) -> bool:
|
||||||
|
return str(message.get("body", "")) == HOST_HISTORY_MESSAGE
|
||||||
|
):
|
||||||
|
break
|
||||||
|
await process_frame
|
||||||
|
assert(chat_service.get_history().any(
|
||||||
|
func(message: Dictionary) -> bool:
|
||||||
|
return str(message.get("body", "")) == HOST_HISTORY_MESSAGE
|
||||||
|
))
|
||||||
|
assert(remote_voice_samples.is_empty())
|
||||||
var service := main.get_node(
|
var service := main.get_node(
|
||||||
"%NetworkProfileService"
|
"%NetworkProfileService"
|
||||||
) as NetworkProfileService
|
) as NetworkProfileService
|
||||||
|
|
@ -160,9 +201,6 @@ func _run_client() -> void:
|
||||||
var player := main.get_node("%PlayerSpawnService").get_local_player() as Player
|
var player := main.get_node("%PlayerSpawnService").get_local_player() as Player
|
||||||
assert(player.get_animalese_voice_id() == "deep")
|
assert(player.get_animalese_voice_id() == "deep")
|
||||||
assert(player.get_animalese_sample_set_id() == "kim")
|
assert(player.get_animalese_sample_set_id() == "kim")
|
||||||
var chat_service := main.get_node(
|
|
||||||
"%NetworkChatService"
|
|
||||||
) as NetworkChatService
|
|
||||||
var host_voice_messages: Array[Dictionary] = []
|
var host_voice_messages: Array[Dictionary] = []
|
||||||
for message: Dictionary in chat_service.get_history():
|
for message: Dictionary in chat_service.get_history():
|
||||||
if str(message.get("body", "")) == HOST_VOICE_MESSAGE:
|
if str(message.get("body", "")) == HOST_VOICE_MESSAGE:
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ func speak_text(
|
||||||
voice_profile_id: String = VoiceProfilesType.DEFAULT_ID,
|
voice_profile_id: String = VoiceProfilesType.DEFAULT_ID,
|
||||||
characters_per_second: float = -1.0,
|
characters_per_second: float = -1.0,
|
||||||
sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
|
sample_set_id: String = VoiceProfilesType.DEFAULT_SAMPLE_SET_ID,
|
||||||
|
volume_offset_db: float = 0.0,
|
||||||
) -> Tween:
|
) -> Tween:
|
||||||
var speech_tween := tween_owner.create_tween()
|
var speech_tween := tween_owner.create_tween()
|
||||||
var resolved_characters_per_second := (
|
var resolved_characters_per_second := (
|
||||||
|
|
@ -75,6 +76,7 @@ func speak_text(
|
||||||
voice_key,
|
voice_key,
|
||||||
voice_pitch,
|
voice_pitch,
|
||||||
resolved_sample_set_id,
|
resolved_sample_set_id,
|
||||||
|
volume_offset_db,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return speech_tween
|
return speech_tween
|
||||||
|
|
@ -110,6 +112,7 @@ func _play_character(
|
||||||
voice_key: String,
|
voice_key: String,
|
||||||
voice_pitch: float,
|
voice_pitch: float,
|
||||||
sample_set_id: String,
|
sample_set_id: String,
|
||||||
|
volume_offset_db: float,
|
||||||
) -> void:
|
) -> void:
|
||||||
if _playback == null or character.strip_edges().is_empty():
|
if _playback == null or character.strip_edges().is_empty():
|
||||||
return
|
return
|
||||||
|
|
@ -145,5 +148,10 @@ func _play_character(
|
||||||
0.72,
|
0.72,
|
||||||
1.45,
|
1.45,
|
||||||
)
|
)
|
||||||
_playback.play_stream(sample, 0.0, volume_db, pitch)
|
_playback.play_stream(
|
||||||
|
sample,
|
||||||
|
0.0,
|
||||||
|
volume_db + volume_offset_db,
|
||||||
|
pitch,
|
||||||
|
)
|
||||||
character_sample_played.emit(sample_set_id, normalized)
|
character_sample_played.emit(sample_set_id, normalized)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ const SPEECH_BUBBLE_WIDTH: float = 320.0
|
||||||
const SPEECH_POINTER_HALF_WIDTH: float = 12.0
|
const SPEECH_POINTER_HALF_WIDTH: float = 12.0
|
||||||
const SPEECH_POINTER_HEIGHT: float = 14.0
|
const SPEECH_POINTER_HEIGHT: float = 14.0
|
||||||
const SPEECH_POINTER_OVERLAP: float = 3.0
|
const SPEECH_POINTER_OVERLAP: float = 3.0
|
||||||
|
const ANIMALESE_FULL_VOLUME_DISTANCE: float = 4.0
|
||||||
|
const ANIMALESE_SILENT_DISTANCE: float = 24.0
|
||||||
|
const ANIMALESE_SILENT_VOLUME_DB: float = -80.0
|
||||||
const MOBILE_COMPACT_WIDTH: float = 620.0
|
const MOBILE_COMPACT_WIDTH: float = 620.0
|
||||||
const MOBILE_EXPANDED_WIDTH: float = 820.0
|
const MOBILE_EXPANDED_WIDTH: float = 820.0
|
||||||
const MOBILE_COMPACT_HEIGHT: float = 220.0
|
const MOBILE_COMPACT_HEIGHT: float = 220.0
|
||||||
|
|
@ -1022,14 +1025,17 @@ func _show_speech_bubble(
|
||||||
sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
|
sample_set_id = VoiceProfilesType.sanitized_sample_set_id(
|
||||||
speaker_avatar.get_animalese_sample_set_id()
|
speaker_avatar.get_animalese_sample_set_id()
|
||||||
)
|
)
|
||||||
_animalese_voice.speak_text(
|
var voice_volume_offset_db := _get_voice_volume_offset_db(speaker_avatar)
|
||||||
_animalese_voice,
|
if voice_volume_offset_db > ANIMALESE_SILENT_VOLUME_DB:
|
||||||
label.text,
|
_animalese_voice.speak_text(
|
||||||
str(peer_id),
|
_animalese_voice,
|
||||||
voice_profile_id,
|
label.text,
|
||||||
-1.0,
|
str(peer_id),
|
||||||
sample_set_id,
|
voice_profile_id,
|
||||||
)
|
-1.0,
|
||||||
|
sample_set_id,
|
||||||
|
voice_volume_offset_db,
|
||||||
|
)
|
||||||
_speech[peer_id] = {
|
_speech[peer_id] = {
|
||||||
"bubble": bubble,
|
"bubble": bubble,
|
||||||
"pointer": pointer,
|
"pointer": pointer,
|
||||||
|
|
@ -1042,6 +1048,28 @@ func _show_speech_bubble(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _get_voice_volume_offset_db(speaker_avatar: Player) -> float:
|
||||||
|
if speaker_avatar == null or _player == null:
|
||||||
|
return ANIMALESE_SILENT_VOLUME_DB
|
||||||
|
return animalese_volume_offset_db_for_distance(
|
||||||
|
speaker_avatar.global_position.distance_to(_player.global_position)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
static func animalese_volume_offset_db_for_distance(distance: float) -> float:
|
||||||
|
if not is_finite(distance) or distance >= ANIMALESE_SILENT_DISTANCE:
|
||||||
|
return ANIMALESE_SILENT_VOLUME_DB
|
||||||
|
if distance <= ANIMALESE_FULL_VOLUME_DISTANCE:
|
||||||
|
return 0.0
|
||||||
|
var distance_weight := inverse_lerp(
|
||||||
|
ANIMALESE_FULL_VOLUME_DISTANCE,
|
||||||
|
ANIMALESE_SILENT_DISTANCE,
|
||||||
|
distance,
|
||||||
|
)
|
||||||
|
var amplitude := pow(1.0 - distance_weight, 2.0)
|
||||||
|
return maxf(linear_to_db(amplitude), ANIMALESE_SILENT_VOLUME_DB)
|
||||||
|
|
||||||
|
|
||||||
func _on_history(messages: Array) -> void:
|
func _on_history(messages: Array) -> void:
|
||||||
for peer_id: int in _speech.keys():
|
for peer_id: int in _speech.keys():
|
||||||
var state: Dictionary = _speech[peer_id]
|
var state: Dictionary = _speech[peer_id]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue