Last active
May 3, 2025 05:27
-
-
Save bearlikelion/c1c8552e42e995a39148dbd69d7107f6 to your computer and use it in GitHub Desktop.
SurfsUp Voip.gd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class_name Voice | |
extends Node | |
const VOIP = preload("res://Scenes/Player/voip.tscn") | |
const REF_SAMPLE_RATE : int = 44100 | |
@export var use_loopback: bool = false | |
var current_sample_rate: int = 48000 | |
var local_playback: AudioStreamGeneratorPlayback = null | |
var local_voice_buffer: PackedByteArray = PackedByteArray() | |
var network_voice_buffer: PackedByteArray = PackedByteArray() | |
var packet_read_limit: int = 5 | |
var is_speaking: bool = false | |
@onready var network: AudioStreamPlayer = $Network | |
func _ready() -> void: | |
if !is_multiplayer_authority(): | |
queue_free() | |
Events.voip.connect(process_voice_data) | |
for peer: Dictionary in SteamInit.lobby_members: | |
setup_voip(peer["peer_id"]) | |
func _process(_delta: float) -> void: | |
if Input.is_action_just_pressed("push_to_talk") and !Global.is_typing: | |
NetCode.show_speaking.rpc(multiplayer.get_unique_id(), true) | |
if Input.is_action_pressed("push_to_talk") and !Global.is_typing: | |
is_speaking = true | |
record_voice(true) | |
if Input.is_action_just_released("push_to_talk") and !Global.is_typing: | |
is_speaking = false | |
record_voice(false) | |
if is_speaking: | |
check_for_voice() | |
func record_voice(recording: bool) -> void: | |
Steam.setInGameVoiceSpeaking(SteamInit.steam_id, recording) | |
if recording: | |
Steam.startVoiceRecording() | |
else: | |
Steam.stopVoiceRecording() | |
NetCode.show_speaking.rpc(multiplayer.get_unique_id(), false) | |
func check_for_voice() -> void: | |
var available_voice: Dictionary = Steam.getAvailableVoice() | |
# Seems there is voice data | |
if available_voice['result'] == Steam.VOICE_RESULT_OK and available_voice['buffer'] > 0: | |
# Valve's getVoice uses 1024 but GodotSteam's is set at 8192? | |
# Our sizes might be way off; internal GodotSteam notes that Valve suggests 8kb | |
# However, this is not mentioned in the header nor the SpaceWar example but -is- in Valve's docs which are usually wrong | |
var voice_data: Dictionary = Steam.getVoice() | |
if voice_data['result'] == Steam.VOICE_RESULT_OK and voice_data['written']: | |
# print("Voice message has data: %s / %s" % [voice_data['result'], voice_data['written']]) | |
# Here we can pass this voice data off on the network | |
# Networking.send_message(voice_data['buffer']) | |
# SteamInit.peer.send_direct_message(voice_data['buffer']) | |
# print("Send Voice Data: %s" % sent) | |
# If loopback is enable, play it back at this point | |
if use_loopback: | |
# print("Loopback on") | |
process_voice_data(voice_data, multiplayer.get_unique_id()) | |
else: | |
NetCode.send_voice.rpc(voice_data) | |
func get_sample_rate() -> void: | |
current_sample_rate = Steam.getVoiceOptimalSampleRate() | |
# print("Current sample rate: %s" % current_sample_rate) | |
func process_voice_data(voice_data: Dictionary, peer_id: int) -> void: | |
# Our sample rate function above without toggling | |
get_sample_rate() | |
var pitch : float = float(current_sample_rate)/REF_SAMPLE_RATE | |
var network_voip: AudioStreamPlayer = get_node_or_null(str(peer_id)) | |
if network_voip: | |
var network_playback: AudioStreamPlayback = network_voip.get_stream_playback() | |
network_voip.set_pitch_scale(pitch) | |
var decompressed_voice: Dictionary = Steam.decompressVoice(voice_data['buffer'], current_sample_rate) | |
if decompressed_voice['result'] == Steam.VOICE_RESULT_OK and decompressed_voice['size'] > 0: | |
# print("Decompressed voice: %s" % decompressed_voice['size']) | |
local_voice_buffer = decompressed_voice['uncompressed'] | |
local_voice_buffer.resize(decompressed_voice['size']) | |
# We now iterate through the local_voice_buffer and push the samples to the audio generator | |
for i: int in range(0, mini(network_playback.get_frames_available() * 2, local_voice_buffer.size()), 2): | |
# Steam's audio data is represented as 16-bit single channel PCM audio, so we need to convert it to amplitudes | |
# Combine the low and high bits to get full 16-bit value | |
var raw_value: int = local_voice_buffer[0] | (local_voice_buffer[1] << 8) | |
# Make it a 16-bit signed integer | |
raw_value = (raw_value + 32768) & 0xffff | |
# Convert the 16-bit integer to a float on from -1 to 1 | |
var amplitude: float = float(raw_value - 32768) / 32768.0 | |
# push_frame() takes a Vector2. The x represents the left channel and the y represents the right channel | |
network_playback.push_frame(Vector2(amplitude, amplitude)) | |
# Delete the used samples | |
local_voice_buffer.remove_at(0) | |
local_voice_buffer.remove_at(0) | |
else: | |
setup_voip(peer_id) | |
func setup_voip(peer_id: int) -> void: | |
if peer_id > 0: | |
var voip_node: AudioStreamPlayer = VOIP.instantiate() | |
add_child(voip_node) | |
voip_node.play() | |
voip_node.name = str(peer_id) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment