Python bindings for the 4Players ODIN Voice SDK - a cross-platform solution for integrating real-time voice chat into your applications.
ODIN is a cross-platform SDK that integrates real-time voice chat into multiplayer games, applications and websites. It supports both managed cloud hosting through 4Players and self-hosted deployments, giving developers flexibility in their deployment strategy.
- Real-Time Voice Chat - Low-latency audio transmission with Opus codec compression and automatic sample rate conversion
- Audio Processing - Voice Activity Detection (VAD), echo cancellation, noise suppression and automatic gain control
- Proximity Chat - Distance-based audio filtering that automatically adjusts based on peer positions
- End-to-End Encryption - Secure voice communication with customizable room passwords
- Connection Pooling - Efficient multi-room management for complex applications
- Flexible Pipeline - Extensible audio pipeline architecture supporting custom effects
pip install odin-voiceimport asyncio
import json
import time
from odin_voice import (
OdinConnectionPool,
OdinTokenGenerator,
OdinEncoder,
OdinDecoder,
OdinAudioEncoderConfig,
OdinAudioDecoderConfig,
)
async def main():
# Generate a token (in production, do this server-side)
generator = OdinTokenGenerator.from_access_key("your-access-key")
nbf = int(time.time())
token = generator.sign(json.dumps({
"rid": "my-room",
"uid": "user-123",
"nbf": nbf,
"exp": nbf + 300,
}))
async with OdinConnectionPool() as pool:
room = pool.join_room(
gateway="gateway.odin.4players.io",
token=token,
)
# Wait for connection
own_peer_id, media_ids = await room.joined()
print(f"Joined as peer {own_peer_id} with media ids {media_ids}")
# Create encoder for outgoing audio
encoder = OdinEncoder(OdinAudioEncoderConfig(sample_rate=48000))
# Start media stream for the first media id we got
room.send_rpc([2, "StartMedia", {"media_id": media_ids[0], "properties": {"kind": "audio"}}])
# Create decoder for incoming audio using remote media id
decoder = OdinDecoder(OdinAudioDecoderConfig(media_id=1, sample_rate=48000, stereo=False))
# Handle events and audio in parallel
async def handle_events():
async for rpc in room.rpcs():
print(f"Event: {rpc}")
async def handle_audio():
async for datagram in room.datagrams():
decoder.push(datagram.data)
samples, is_silent = decoder.pop(960) # 20ms at 48kHz mono
# Process samples...
await asyncio.gather(handle_events(), handle_audio())
asyncio.run(main())Tokens authenticate users and grant access to rooms. Generate tokens server-side only - never embed access keys in client code.
import json
import time
from odin_voice import OdinTokenGenerator
# Create from your access key
generator = OdinTokenGenerator.from_access_key("your-access-key")
# Or generate a new key pair (share public_key with 4Players)
generator = OdinTokenGenerator.generate()
print(f"Access Key: {generator.access_key}") # Keep secret!
print(f"Public Key: {generator.public_key}") # Share with 4Players
# Create a token
nbf = int(time.time())
token = generator.sign(json.dumps({
"rid": "my-room", # Room ID
"uid": "user-id", # User ID
"aud": "gateway", # Audience: "gateway" or "sfu"
"nbf": nbf, # Not before (Unix timestamp)
"exp": nbf + 300, # Expiration (Unix timestamp)
}))Enable E2EE by creating a cipher with a shared password. All peers must use the same password.
from odin_voice import OdinCipher, OdinCryptoPeerStatus
# Create cipher with password
cipher = OdinCipher("shared-secret")
# Join room with encryption
room = pool.join_room(gateway=gateway, token=token, cipher=cipher)
# Check peer encryption status
status = cipher.peer_status(peer_id)
match status:
case OdinCryptoPeerStatus.Encrypted:
print("Peer is using the same password - secure!")
case OdinCryptoPeerStatus.PasswordMismatch:
print("Peer is using a different password")
case OdinCryptoPeerStatus.Unencrypted:
print("Peer is not using encryption")
# Change password at runtime
cipher.set_password("new-password")Add effects like VAD and noise suppression to encoders/decoders:
from odin_voice import (
OdinEncoder,
OdinAudioEncoderConfig,
OdinVadConfig,
OdinApmConfig,
OdinNoiseSuppressionLevel,
OdinGainControllerVersion
)
encoder = OdinEncoder(OdinAudioEncoderConfig())
pipeline = encoder.pipeline
# Add Voice Activity Detection with default settings
vad_config = OdinVadConfig()
vad_id = pipeline.insert_vad(0, vad_config)
# Add automatic gain control, noise suppression and echo cancellation
apm_config = OdinApmConfig(
gain_controller_version=OdinGainControllerVersion.V2,
noise_suppression_level=OdinNoiseSuppressionLevel.High,
echo_canceller=True,
)
apm_id = pipeline.insert_apm(1, 48000, False, apm_config)
# Remove effects when needed
pipeline.remove_effect(vad_id)Monitor connection quality:
stats = room.stats
if stats:
print(f"RTT: {stats.rtt:.1f}ms")
print(f"Packet Loss: {stats.udp_tx_loss:.1f}%")
print(f"RX Datagrams: {stats.udp_rx_datagrams}")
print(f"TX Datagrams: {stats.udp_tx_datagrams}")For full documentation, visit docs.4players.io.