### Complete VoIP Integration Example Setup Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt Sets up the necessary components for a complete Voice over IP integration, including microphone capture, Opus encoding, network simulation, and playback with VOX and noise suppression. ```gdscript extends Node # Encoder side var opusencoder: TwovoipOpusEncoder var opus_chunk_size = 960 var audio_chunk_size: int var packet_header = PackedByteArray([0, 0]) var frame_count = 0 var stream_count = 0 var is_transmitting = false var vox_threshold = 0.15 # Decoder side var audiostreamopus: AudioStreamOpus var playbackopus: AudioStreamPlaybackOpus ``` -------------------------------- ### Get OpusChunked Effect Reference Source: https://github.com/goatchurchprime/two-voip-godot-4/blob/main/README.md Retrieves a reference to the OpusChunked audio effect from a specific audio bus. This is typically used to access methods for reading and managing Opus data chunks. ```GDScript var microphoneidx = AudioServer.get_bus_index("MicrophoneBus") var opuschunked : AudioEffectOpusChunked = AudioServer.get_bus_effect(microphoneidx, 0) ``` -------------------------------- ### Control Opus Audio Playback with AudioStreamPlaybackOpus Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt Manages playback and buffer for Opus audio streams, including forward error correction. It configures the stream, handles incoming audio packets, and adapts playback speed based on buffer levels. ```gdscript extends Node var audiostreamopus: AudioStreamOpus var audiostreamplaybackopus: AudioStreamPlaybackOpus var lenchunkprefix = 2 # Bytes at start of packet for sequencing func _ready(): audiostreamopus = $AudioStreamPlayer.stream $AudioStreamPlayer.play() audiostreamplaybackopus = $AudioStreamPlayer.get_stream_playback() func receive_voice_packet(packet: PackedByteArray): # Check for JSON header/footer packets if packet[0] == 123: # '{' character - JSON packet var json_str = packet.get_string_from_ascii() var header = JSON.parse_string(json_str) if header.has("talkingtimestart"): # New voice stream starting audiostreamopus.opus_sample_rate = header["opussamplerate"] audiostreamopus.opus_channels = header.get("opuschannels", 2) audiostreamplaybackopus.reset_opus_decoder() lenchunkprefix = header["lenchunkprefix"] print("Voice stream started") elif header.has("talkingtimeend"): print("Voice stream ended, duration: %.2f sec" % header["talkingtimeduration"]) return # Audio packet - push to decoder if audiostreamplaybackopus.opus_segment_space_available(): # Parameters: packet, prefix_length, use_fec (1 if previous packet was lost) audiostreamplaybackopus.push_opus_packet(packet, lenchunkprefix, 0) else: print("Buffer full, dropping packet") func _physics_process(_delta): # Monitor playback buffer for adaptive jitter handling var buffer_time = audiostreamplaybackopus.queue_length_frames() / audiostreamopus.opus_sample_rate if buffer_time < 0.3: # Buffer running low - slow down playback slightly $AudioStreamPlayer.pitch_scale = 0.9 elif buffer_time > 0.8: # Buffer filling up - speed up playback slightly $AudioStreamPlayer.pitch_scale = 1.1 else: $AudioStreamPlayer.pitch_scale = 1.0 ``` -------------------------------- ### Godot GDScript: Network Simulation for Voice Communication Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt This GDScript code simulates network behavior for real-time voice communication. It handles microphone input, Opus encoding with optional noise suppression (RNNoise), packet creation with headers and footers, simulated network transmission via a packet queue, and audio playback decoding. It's designed for Godot 4 and utilizes the TwovoipOpusEncoder class. ```gdscript var packet_queue = [] func _ready(): # Setup encoder opusencoder = TwovoipOpusEncoder.new() AudioServer.set_input_device_active(true) var input_rate = AudioServer.get_input_mix_rate() opusencoder.create_sampler(input_rate, 48000, 2, true) # Enable RNNoise opusencoder.create_opus_encoder(16000, 5, true) audio_chunk_size = opusencoder.calc_audio_chunk_size(opus_chunk_size) # Setup decoder audiostreamopus = $AudioStreamPlayer.stream audiostreamopus.opus_sample_rate = 48000 audiostreamopus.opus_channels = 2 $AudioStreamPlayer.play() playbackopus = $AudioStreamPlayer.get_stream_playback() func _process(_delta): process_microphone() process_playback() func process_microphone(): while true: var frames = AudioServer.get_input_frames(audio_chunk_size) # speech_probability=true to get RNNoise speech probability var speech_prob = opusencoder.process_pre_encoded_chunk(frames, opus_chunk_size, true, false) if speech_prob == -1.0: break # VOX using speech probability from RNNoise if speech_prob > vox_threshold: if not is_transmitting: start_transmission() # Encode and send update_packet_header() var opus_packet = opusencoder.encode_chunk(packet_header) send_packet(opus_packet) frame_count += 1 elif is_transmitting: end_transmission() func start_transmission(): is_transmitting = true frame_count = 0 opusencoder.reset_opus_encoder() # Send stream header var header = { "talkingtimestart": Time.get_ticks_msec() / 1000.0, "opussamplerate": 48000, "opuschannels": 2, "opusframesize": opus_chunk_size, "lenchunkprefix": 2, "opusstreamcount": stream_count } send_packet(JSON.stringify(header).to_ascii_buffer()) func end_transmission(): is_transmitting = false # Send stream footer var footer = { "talkingtimeend": Time.get_ticks_msec() / 1000.0, "opusframecount": frame_count, "opusstreamcount": stream_count } send_packet(JSON.stringify(footer).to_ascii_buffer()) stream_count += 1 func update_packet_header(): # 2-byte frame counter with stream ID in high bit packet_header[0] = frame_count % 256 packet_header[1] = (int(frame_count / 256) & 127) + (stream_count % 2) * 128 func send_packet(packet: PackedByteArray): # Simulate network - in real app, send over WebRTC/MQTT/etc packet_queue.append(packet) func process_playback(): while len(packet_queue) > 0 and playbackopus.opus_segment_space_available(): var packet = packet_queue.pop_front() if packet[0] == 123: # JSON header/footer var data = JSON.parse_string(packet.get_string_from_ascii()) if data.has("talkingtimestart"): playbackopus.reset_opus_decoder() else: # Audio packet with 2-byte prefix playbackopus.push_opus_packet(packet, 2, 0) ``` -------------------------------- ### Direct Audio Processing with AudioEffectOpusChunked Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt Enables direct processing of raw audio arrays without relying on Godot's audio bus. This is useful for custom audio pipelines and testing, handling encoding and decoding of Opus packets. ```gdscript extends Node var opuschunked: AudioEffectOpusChunked var audiostreamopuschunked: AudioStreamOpusChunked func _ready(): opuschunked = AudioEffectOpusChunked.new() audiostreamopuschunked = AudioStreamOpusChunked.new() # Configure both ends opuschunked.opussamplerate = 48000 opuschunked.opusframesize = 960 audiostreamopuschunked.opussamplerate = 48000 audiostreamopuschunked.opusframesize = 960 func process_audio_buffer(audio_samples: PackedVector2Array): # Push raw audio samples to encoder opuschunked.push_chunk(audio_samples) while opuschunked.chunk_available(): # Optionally resample and denoise opuschunked.resampled_current_chunk() var speech_prob = opuschunked.denoise_resampled_chunk() # Get the processed audio chunk var processed_chunk = opuschunked.read_chunk(true) # true = resampled # Encode to opus var opus_packet = opuschunked.read_opus_packet(PackedByteArray()) opuschunked.drop_chunk() # Decode back to audio audiostreamopuschunked.push_opus_packet(opus_packet, 0, 0) # Read decoded audio var decoded_audio = audiostreamopuschunked.pop_front_chunk(960) print("Decoded %d frames" % decoded_audio.size()) ``` -------------------------------- ### Opus Encoding via Audio Bus Effect with AudioEffectOpusChunked in GDScript Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt Illustrates the use of the AudioEffectOpusChunked class, an AudioEffect that captures microphone input from an audio bus, buffers it, and performs Opus encoding. This method integrates with Godot's audio bus system and is compatible with older Godot versions. It requires an AudioStreamPlayer with an AudioStreamMicrophone on a 'MicrophoneBus' which should be muted. ```gdscript extends Node var opuschunked: AudioEffectOpusChunked func _ready(): # Get the effect from the audio bus # Requires: AudioStreamPlayer with AudioStreamMicrophone on "MicrophoneBus" # MicrophoneBus should be muted to prevent feedback var microphoneidx = AudioServer.get_bus_index("MicrophoneBus") opuschunked = AudioServer.get_bus_effect(microphoneidx, 0) # Configure encoder properties (optional - has sensible defaults) opuschunked.opussamplerate = 48000 # Opus sample rate (8000,12000,16000,24000,48000) opuschunked.opusframesize = 960 # Frame size (480=10ms, 960=20ms at 48kHz) opuschunked.opusbitrate = 12000 # Bitrate in bits/second opuschunked.opuscomplexity = 5 # Encoder complexity (0-10) opuschunked.opusoptimizeforvoice = true opuschunked.audiosamplerate = 44100 # Input sample rate opuschunked.audiosamplesize = 882 # Input chunk size (20ms at 44.1kHz) opuschunked.audiosamplechunks = 50 # Ring buffer size in chunks func _process(_delta): var prepend = PackedByteArray() # Optional header bytes for packet sequencing while opuschunked.chunk_available(): # Read opus-encoded packet var opusdata: PackedByteArray = opuschunked.read_opus_packet(prepend) opuschunked.drop_chunk() # Transmit opusdata over network transmit(opusdata) func transmit(data: PackedByteArray): # Your network transmission code here pass ``` -------------------------------- ### Implement Voice Activity Detection (VOX) in GDScript Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt This GDScript code implements Voice Activity Detection (VOX) by measuring chunk amplitude. It allows audio transmission only when speech is detected, with a configurable hang time to prevent abrupt cutoffs. It relies on the AudioEffectOpusChunked class for audio processing. ```gdscript extends Node var opuschunked: AudioEffectOpusChunked var vox_threshold = 0.2 var is_talking = false var hang_frames = 25 # Continue for 25 frames after voice stops var hang_counter = 0 func _ready(): var microphoneidx = AudioServer.get_bus_index("MicrophoneBus") opuschunked = AudioServer.get_bus_effect(microphoneidx, 0) func _process(_delta): while opuschunked.chunk_available(): # Get maximum amplitude of current chunk # Parameters: rms (use RMS instead of peak), resampled (use resampled buffer) var chunk_max = opuschunked.chunk_max(false, false) if chunk_max >= vox_threshold: hang_counter = 0 if not is_talking: is_talking = true start_transmission() else: hang_counter += 1 if hang_counter >= hang_frames and is_talking: is_talking = false end_transmission() if is_talking: var opusdata = opuschunked.read_opus_packet(PackedByteArray()) transmit(opusdata) opuschunked.drop_chunk() func start_transmission(): opuschunked.resetencoder(false) # Reset encoder state, keep buffer print("Started talking") func end_transmission(): print("Stopped talking") func transmit(data: PackedByteArray): pass ``` -------------------------------- ### Standalone Opus Encoding with TwovoipOpusEncoder in GDScript Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt Demonstrates how to use the TwovoipOpusEncoder class for standalone Opus encoding. This method captures audio frames directly from the microphone using Godot 4.6+'s AudioServer.get_input_frames() and encodes them without relying on audio buses. It handles resampling, noise suppression, and Opus encoding. ```gdscript extends Node var opusencoder: TwovoipOpusEncoder var opus_chunk_size = 960 # 20ms at 48kHz var chunkprefix = PackedByteArray([0, 0]) # 2-byte packet header for sequencing func _ready(): opusencoder = TwovoipOpusEncoder.new() # Initialize the encoder # Parameters: input_mix_rate, opus_sample_rate, channels, use_rnnoise AudioServer.set_input_device_active(true) opusencoder.create_sampler(AudioServer.get_input_mix_rate(), 48000, 2, false) # Create Opus encoder # Parameters: bit_rate, complexity (0-10), voice_optimal opusencoder.create_opus_encoder(12000, 5, true) func _process(_delta): # Calculate required input frame count based on opus chunk size var audio_chunk_size = opusencoder.calc_audio_chunk_size(opus_chunk_size) while true: # Get audio frames from microphone (Godot 4.6+ required) var frames = AudioServer.get_input_frames(audio_chunk_size) # Process chunk - returns -1.0 if not enough frames, otherwise returns max amplitude # Parameters: audio_frames, opus_chunk_size, speech_probability, rms var chunkmax = opusencoder.process_pre_encoded_chunk(frames, opus_chunk_size, false, false) if chunkmax == -1.0: break # Encode the processed chunk with optional prefix bytes var opusdata: PackedByteArray = opusencoder.encode_chunk(chunkprefix) # Transmit opusdata over network print("Encoded %d bytes, max amplitude: %.3f" % [opusdata.size(), chunkmax]) func start_new_transmission(): # Reset encoder state when starting a new voice transmission opusencoder.reset_opus_encoder() ``` -------------------------------- ### Decode and Playback Opus Streams with AudioStreamOpusChunked in GDScript Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt This GDScript code demonstrates using the `AudioStreamOpusChunked` class to decode incoming Opus packets and play them via an `AudioStreamPlayer`. It handles buffering and resampling, requiring configuration of sample rates and frame sizes to match the encoder settings. ```gdscript extends Node var audiostreamopuschunked: AudioStreamOpusChunked var opuspacketsbuffer = [] # Queue for incoming packets func _ready(): # Get the stream from AudioStreamPlayer audiostreamopuschunked = $AudioStreamPlayer.stream $AudioStreamPlayer.play() # Configure decoder (must match encoder settings) audiostreamopuschunked.opussamplerate = 48000 audiostreamopuschunked.opusframesize = 960 audiostreamopuschunked.audiosamplerate = 44100 audiostreamopuschunked.audiosamplesize = 882 audiostreamopuschunked.audiosamplechunks = 50 func receive_packet(opusdata: PackedByteArray): # Add received packet to buffer opuspacketsbuffer.append(opusdata) func _process(_delta): # Push packets to decoder when space is available while audiostreamopuschunked.chunk_space_available() and len(opuspacketsbuffer) > 0: var packet = opuspacketsbuffer.pop_front() # Parameters: opusbytepacket, prefix_bytes_length, decode_fec audiostreamopuschunked.push_opus_packet(packet, 0, 0) # Monitor buffer status var queue_frames = audiostreamopuschunked.queue_length_frames() print("Buffer: %d frames" % queue_frames) func reset_decoder(): # Call when starting a new voice stream audiostreamopuschunked.resetdecoder() ``` -------------------------------- ### Push Opus Packets to AudioStreamPlayer Source: https://github.com/goatchurchprime/two-voip-godot-4/blob/main/README.md Decodes incoming Opus packets and pushes them into an AudioStreamOpusChunked for playback. This function handles buffering and feeding the decoded audio data to the stream player. ```GDScript var audiostreamopuschunked : AudioStreamOpusChunked = $AudioStreamPlayer.stream var opuspacketsbuffer = [ ] # append incoming packets to this list func _process(delta): while audiostreamopuschunked.chunk_space_available(): audiostreamopuschunked.push_opus_packet(opuspacketsbuffer.pop_front(), 0, 0) ``` -------------------------------- ### Integrate RNNoise for AI Noise Suppression in GDScript Source: https://context7.com/goatchurchprime/two-voip-godot-4/llms.txt This GDScript code integrates RNNoise for AI-based noise suppression. The denoiser operates at 48kHz and provides a speech probability value, which can be used in conjunction with VOX detection. It configures the Opus chunked effect for 48kHz operation and reads denoised chunks. ```gdscript extends Node var opuschunked: AudioEffectOpusChunked func _ready(): var microphoneidx = AudioServer.get_bus_index("MicrophoneBus") opuschunked = AudioServer.get_bus_effect(microphoneidx, 0) # Configure for 48kHz (required for RNNoise) opuschunked.opussamplerate = 48000 opuschunked.opusframesize = 960 func _process(_delta): while opuschunked.chunk_available(): # Check if denoiser is available if opuschunked.denoiser_available(): # Denoise the chunk - returns speech probability (0.0-1.0) var speech_probability = opuschunked.denoise_resampled_chunk() print("Speech probability: %.2f" % speech_probability) # Read the denoised, resampled chunk var audio_chunk: PackedVector2Array = opuschunked.read_chunk(true) else: # Fallback: read without denoising var audio_chunk = opuschunked.read_chunk(false) # Encode to opus (uses denoised buffer if available) var opusdata = opuschunked.read_opus_packet(PackedByteArray()) opuschunked.drop_chunk() transmit(opusdata) func transmit(data: PackedByteArray): pass ``` -------------------------------- ### Convert Opus Packet to Audio Chunk for Playback Source: https://github.com/goatchurchprime/two-voip-godot-4/blob/main/README.md Converts an Opus packet into an audio chunk suitable for playback using AudioStreamGeneratorPlayback. This function also handles potential packet reordering and forward error correction. ```GDScript var audiostreamgeneratorplayback = $AudioStreamPlayer.get_stream_playback() while audiostreamgeneratorplayback.get_frames_available() > audiostreamopuschunked.audiosamplesize: var audiochunk = audiostreamopuschunked.opus_packet_to_chunk(opuspacketsbuffer.pop_front(), prefixbyteslength, fec) audiostreamgeneratorplayback.push_buffer(audiochunk) audiostreamgeneratorplayback.push_buffer(audiopacketsbuffer.pop_front()) ``` -------------------------------- ### Consume and Transmit Opus Chunks Source: https://github.com/goatchurchprime/two-voip-godot-4/blob/main/README.md Processes available Opus data chunks from the OpusChunked effect and transmits them. This function is called per frame and is responsible for reading, dropping, and sending the encoded audio data. ```GDScript func _process(delta): var prepend = PackedByteArray() while opuschunked.chunk_available(): var opusdata : PackedByteArray = opuschunked.read_opus_packet(prepend) opuschunked.drop_chunk() transmit(opusdata) ```