TwovVoip GDExtension

repository·main·Indexed 41 days ago

https://github.com/goatchurchprime/two-voip-godot-4

A high-performance VoIP GDExtension for Godot Engine 4.6 that provides voice communication capabilities for networked games. It integrates Xiph/Opus for audio compression and Xiph/RNNoise for de-noising. The extension includes the voiphelper module for managing jitter buffers, packet re-ordering, and Vox gating, as well as low-level control via TwovoipOpusEncoder.

Tokens
4.5K
Snippets
10
Records
18
Agent score
52%

What's inside TwovVoip

  1. Overview of TwovVoip GDExtension

    main

    TwovVoip is a Godot Engine GDExtension designed for implementing Voice Over IP (VoIP) in networked games. It provides high-quality audio compression and noise reduction through the following core technologies:

    • Xiph/Opus: Industry-standard audio compression.
    • Xiph/RNNoise: Advanced de-noising filter.
    • voiphelper: A high-level component that wraps these technologies to simplify integration into Godot projects.

    The plugin is designed to work across all six platforms supported by Godot Engine.

  2. Use the voiphelper module for VoIP systems

    main

    The voiphelper module is the recommended way to implement VoIP in Godot. It provides essential features that the core opus and rnnoise components do not, including:

    • Vox gating (Voice activation)
    • Jitter buffers
    • Packet re-ordering for unreliable transmissions
    • Dynamic lag management

    Speech is transmitted as binary streams of Opus packets. Each stream uses a two-byte header (with lenchunkprefix=2) and JSON-encoded headers and footers to assist with reliability and debugging.

    Header parameters: { opusframesize, opussamplerate, opuschannels, lenchunkprefix, opusstreamcount, opusframecount, talkingtimestart } Footer parameters: { opusstreamcount, opusframecount, talkingtimeduration, talkingtimeend }

  3. TwoVoIP Third-party Dependencies

    main

    The TwoVoIP extension relies on several submodules for audio processing and GDExtension bindings:

    • godot-cpp: Provides the header files and class definitions for GDExtension. TwoVoIP 6.1 pins to godot-cpp version 10.0.0-rc1 (commit 58d1de720b8ffe9f8ffcdfe3a85148582cfd2e74), which is compatible with Godot 4.6-stable.
    • opus: The Xiph opus library used for voice compression/decompression. It typically processes 20ms chunks of 48kHz stereo audio (960 float pairs) into 20-30 bytes of compressed data.
    • speexdsp: The Xiph SpeexDSP library (pinned to commit 1b28a0f61bc31162979e1f26f3981fc3637095c8). TwoVoIP compiles the resampler and preprocessor sources directly into the extension using floating-point mode.
    • rnnoise: The Xiph RNNoise library (pinned to commit 372f7b4b76cde4ca1ec4605353dd17898a99de38). TwoVoIP uses a CMake adapter in thirdparty/rnnoise to build it and automatically downloads and verifies the RNNoise model (revision 0b50c45) via SHA-256.
  4. Build the TwoVoIP addon locally on NixOS

    main

    To build the TwoVoIP GDExtension from source on a NixOS system, you must use nix-shell to provide the necessary build dependencies (scons, cmake, ninja, and autoreconfHook), then run the scons build sequence. After building, you must copy the resulting shared objects (.so files) to the example directory to ensure the addon is usable in the example project.

    Build Sequence:

    1. Enter the build environment with nix-shell.
    2. Apply portability and size patches using scons apply_patches.
    3. Build the opus dependency using scons build_opus.
    4. Build the rnnoise dependency using scons build_rnnoise.
    5. Build the main library using scons.
    6. Copy the compiled libraries to the example addon path.
    nix-shell -p scons cmake ninja autoreconfHook
    scons apply_patches  # apply the pinned portability and size patches
    scons build_opus     # build opus using cmake
    scons build_rnnoise  # build RNNoise using cmake
    scons                # build this library
    cp addons/twovoip/libs/*.so example/addons/twovoip/libs/
  5. Handle players joining mid-stream

    main

    If a player joins the network while someone is already speaking mid-stream, they will lack the initial header required to decode the subsequent Opus packets.

    To resolve this, call TwoVoipMic.request_audio_json_packet_mid_header(). This generates an intermediate header object with the correct opusframecount so the new player can synchronize with the current stream.

  6. Run the TwovVoip High Level Demo

    main

    To test the features of TwovVoip (Opus compression, RNNoise de-noising, and sample rate settings) without building your own implementation, follow these steps:

    1. Clone or download the repository.
    2. Open the project located in the example/ directory using Godot 4.6.
    3. Open the AssetLib within Godot, search for twovoip, and install the addon.
    4. Run the main scene: radiomqtt.tscn.

    Troubleshooting Microphone Issues: If the application does not detect your microphone, use the official Godot mic_record demo project to verify your hardware and Godot audio settings independently.

  7. Run the VoIP-over-MQTT demo project

    main

    The example directory contains a demo project demonstrating the VoIP addon working with MQTT. This specific example is a lightweight version that does not require native libraries.

    To run this demo, you must manually provide the addon dependency by copying the twovoip addon into the example directory before launching the Godot project.

    cp -r ../twovoip example/
  8. Initialize the TwoVoipMic input player

    main

    Use $TwoVoipMic.init_voip_mic() to set up the microphone input.

    Parameters:

    1. json_packets_as_binary (bool): If true, JSON headers/footers are stringified and emitted via the transmit_audio_packet signal instead of the transmit_audio_json_packet signal. This simplifies networking but prevents intercepting JSON metadata.
    2. MicOn, PTT, Vox, Denoise (optional): UI buttons to control microphone state.
    3. InputOption (OptionButton): An OptionButton populated with AudioServer.get_input_device_list().
    4. voxshader.gdshader (ShaderMaterial): A material used to render an activity waveform.

    Opus Configuration: Configure the encoder using set_opus_values(opussamplerate, opusframedurationms, channels, opusbitrate, opuscomplexity, opusoptimizeforvoice, denoiser, agc_mode):

    • opussamplerate: [48000, 24000, 12000, 8000]
    • opusframedurationms: [5, 10, 20, 40, 60]
    • channels: 1 (mono) or 2 (stereo)
    • opusbitrate: 500 to 64000
    • opuscomplexity: 1 to 10
    • opusoptimizeforvoice: boolean

    Vox Settings:

    • set_voxthreshhold(voxthreshhold): Sets the gating threshold (used by the shader).
    • hangtime: Time the mic stays active after noise falls below threshold.
    • leadtime: (Not implemented) Buffer time captured before threshold is reached.
    # Example initialization concept
    $TwoVoipMic.init_voip_mic(true, mic_on_btn, ptt_btn, vox_btn, denoise_btn, input_option_button, vox_shader_material)
    $TwoVoipMic.set_opus_values(48000, 20, 1, 32000, 5, true, denoiser_mode, agc_mode)
    $TwoVoipMic.set_voxthreshhold(0.5)
  9. Understand TwovVoip Demo Controls and Features

    main

    The provided demo project (radiomqtt.tscn) includes several panels to test VoIP functionality:

    Microphone and Sound Output

    • Mic Enabled: A green indicator shows the microphone is active.
    • PTT (Push to Talk): Manually triggers recording and transmission.
    • Vox (Voice Activated): Enables voice activation. The sensitivity is controlled by the size of the pink area on the blue sound visualizer.
    • De-Noise: Toggles the RNNoise filter on or off.

    LibOpus Compression and Resampling

    You can adjust the following Opus parameters to test bandwidth vs. quality trade-offs:

    • Frame duration: Settings between 2.5ms and 60ms.
    • Bit rate: The target bandwidth.
    • Sample rate
    • Compression Complexity
    • Stereo: Toggle for stereo recording.
    • Optimized for voice: Toggle for voice-specific optimization.

    Note: Changing these values or toggling De-Noise reprocesses the current microphone recording to allow immediate comparison.

    Recording Playback

    • Play: Decodes and plays back the most recent recorded sample.
    • Stats: Displays the number of packets and bytes that would be transmitted for the current clip.

    MQTT Transmission

    This section uses the godot-mqtt GDExtension to push audio packets over a network via a broker.

    • Connect: Connects to the MQTT broker. If running in HTML5, it automatically uses WebSockets.
    • User Table: Shows connected users and their transmission status.
    • Tone Test (T button): Replaces incoming audio with a pure 440Hz tone. This helps distinguish between network packet loss (gaps in audio) and poor source audio quality.
  10. Initialize the TwoVoipSpeaker output player

    main

    The TwoVoipSpeaker module manages incoming VoIP packets and plays them using an AudioStreamOpus object via an AudioStreamPlayer.

    Key Methods & Settings:

    • receive_audio_packet(packet): Processes incoming raw Opus chunks or JSON-encoded headers/footers.
    • external_end_stream(): Manually generates an end stream if network interruption occurs, preventing buffer retention.
    • audio_buffer_lag_time_target (float): Target buffer size in seconds to prevent gaps.
    • audio_buffer_lag_time_target_tolerance (float): Tolerance for speeding up playback if the lag buffer exceeds the target (e.g., after a game stall).
    • set_sinewave_out(): Replaces decoded audio with a 440Hz tone to distinguish between choppy transmission and a choppy microphone.
    • get_chunk_max(): Returns an indicator of incoming audio to help distinguish between a muted player and low local playback volume.
  11. Low-level Opus encoder processing with TwovoipOpusEncoder

    main

    For manual control, use TwovoipOpusEncoder. This allows you to manage the audio pipeline directly.

    Workflow:

    1. Initialize: Call initialize() with sample rate, Opus rate, channels, denoiser mode, AGC mode, and an initial chunk size.
    2. Configure Opus: Call create_opus_encoder() with desired bitrate, frame duration, and voice optimization.
    3. Process Loop:
      • Get required input size via get_required_input_chunk_size().
      • Fetch frames from AudioServer.get_input_frames(required).
      • Call process_chunk(frames) to consume input. Note: You must retain any unconsumed frames (frames.size() - consumed) as the encoder does not buffer them.
      • Call encode_chunk() to get the Opus packet.

    Important Notes:

    • get_current_chunk(): Returns processed PCM as PackedVector2Array (stereo). Mono is duplicated into both channels.
    • get_required_input_chunk_size(): Constant for the object's lifetime; represents the ceiling of the input/output sample ratio.
    • get_speech_probability(): Returns a normalized 0–1 value (RNNoise) or percentage (Speex).
    var encoder := TwovoipOpusEncoder.new()
    var error := encoder.initialize(
        AudioServer.get_input_mix_rate(),
        48000,
        2,
        TwovoipOpusEncoder.DENOISER_DISABLED,
        TwovoipOpusEncoder.AGC_DISABLED,
        960,
    )
    assert(error == OK)
    encoder.create_opus_encoder(12000, 5, true)
    
    var required := encoder.get_required_input_chunk_size()
    var frames := AudioServer.get_input_frames(required)
    var consumed := encoder.process_chunk(frames)
    if consumed >= 0:
        var peak := encoder.get_peak()
        var rms := encoder.get_rms()
        var conditioned_pcm := encoder.get_current_chunk()
        var packet := encoder.encode_chunk()
  12. Monitor audio metrics from TwovoipOpusEncoder

    main

    The encoder provides real-time audio measurements that can be used for UI visualization or logic:

    • get_peak(): Returns the last peak amplitude.
    • get_rms(): Returns the last Root Mean Square (RMS) amplitude.
    • get_speech_probability(): Returns the probability that the current audio contains speech.
    • get_gain(): Returns the current manual gain setting.
    • get_agc_gain(): Returns the current gain applied by the Automatic Gain Control.