Skip to main content

Overview

This is the core client API implemented in Rust and exported to native C libraries. These libraries are the building blocks for our high level SDKs like Unity SDK or Unreal SDK.

Please check out our public Github repository for more information, source-code and a cool CLI application to play around with ODIN right from your command line.

Usage

Quick Start

The following code snippet illustrates how to join a designated room on a specified server using a room token acquired externally (see

odin_room_create

):

#include <stdio.h>
#include "odin.h"

int main(int argc, const char *argv[])
{
odin_startup(ODIN_VERSION);

OdinRoomHandle room = odin_room_create();
odin_room_join(room, "<SERVER_URL>", "<TOKEN>");

getchar();

return 0;
}

Authentication

To enter a room, an authentication token is requisite. ODIN employs the creation of signed JSON Web Tokens (JWT) to ensure a secure authentication pathway. These tokens encapsulate the details of the room(s) you wish to join alongside a customizable identifier for the user, which can be leveraged to reference an existing record within your specific service. See

odin_token_generator_create

or

odin_token_generator_create_token_ex

for more information on this function.

char token[512];

OdinTokenGenerator *generator = odin_token_generator_create("<ACCESS_KEY>");

odin_token_generator_create_token_ex(generator, "<ROOM_ID>", "<USER_ID>", token, sizeof(token));

As ODIN is fully user agnostic, 4Players GmbH does not store any of this information on its servers.

Tokens are signed employing an Ed25519 key pair derived from your distinctive access key. Think of an access key as a singular, unique authentication credential, crucial for generating room tokens to access the ODIN server network. It essentially combines the roles of a username and password into a singular, unobtrusive string of characters, necessitating a comparable degree of protection. For bolstered security, it is strongly recommended to refrain from incorporating an access key in your client-side code. We've created a very basic Node.js server here, to showcase how to issue ODIN tokens to your client apps without exposing your access key.

Event Handling

The ODIN API operates on an event-driven paradigm. In order to manage events, it's necessary to create a callback function in the following manner:

void handle_odin_event(OdinRoomHandle room, const OdinEvent *event, void *data)
{
switch (event->tag)
{
case OdinEvent_RoomConnectionStateChanged:
// Triggered when the room's connectivity state transitions
break;
case OdinEvent_Joined:
// Triggered post successful room entry, rendering the initial state fully accessible
break;
case OdinEvent_RoomUserDataChanged:
// Triggered when the room's arbitrary user data changed
break;
case OdinEvent_PeerJoined:
// Triggered when a new user enters the room
break;
case OdinEvent_PeerLeft:
// Triggered when a user exits the room
break;
case OdinEvent_PeerUserDataChanged:
// Triggered when a peers's arbitrary user data changed
break;
case OdinEvent_MediaAdded:
// Triggered when a peer introduces a media stream into the room
break;
case OdinEvent_MediaRemoved:
// Triggered when a peer withdraws a media stream from the room
break;
case OdinEvent_MediaActiveStateChanged:
// Triggered on change of media stream activity (e.g. user started/stopped talking)
break;
case OdinEvent_MessageReceived:
// Triggered upon receipt of a message containing arbitrary data from a peer
break;
default:
// Optionally handle unexpected events
break;
}
}

To register your callback function as an handler for ODIN events for a room, use the following command ( see

odin_room_set_event_callback

):

odin_room_set_event_callback(room, handle_odin_event, NULL);

Media Streams

Each peer within an ODIN room has the capability to attach media streams for the transmission of voice data. The snippet below illustrates the procedure to establish a new input media stream (see

OdinAudioStreamConfig

,

odin_audio_stream_create

and

odin_room_add_media

):

OdinAudioStreamConfig config = {
.sample_rate = 48000,
.channel_count = 1,
};

OdinMediaStreamHandle stream = odin_audio_stream_create(config);

odin_room_add_media(room, stream);

For the handling of audio data through input/output media streams, employ the

odin_audio_push_data

and

odin_audio_read_data

functions. These functions enable the conveyance of audio data from your local microphone to your audio engine, and the playback of audio data received from other peers, respectively. For a comprehensive working example, refer to our Console Client example, which employs the miniaudio library for cross-platform audio capture and playback.

Audio Processing

Each ODIN room handle is equipped with a dedicated Audio Processing Module (APM) responsible for executing a spectrum of audio filters including, but not limited to, echo cancellation, noise suppression, and sophisticated voice activity detection. This module is designed to accommodate on-the-fly adjustments, empowering you to fine-tune audio settings in real time to suit evolving conditions. The snippet below demonstrates how you might alter the APM settings ( see

OdinApmConfig

and

odin_room_configure_apm

):

OdinApmConfig apm_config = {
.voice_activity_detection = true,
.voice_activity_detection_attack_probability = 0.9,
.voice_activity_detection_release_probability = 0.8,
.volume_gate = false,
.volume_gate_attack_loudness = -30,
.volume_gate_release_loudness = -40,
.echo_canceller = true,
.high_pass_filter = false,
.pre_amplifier = false,
.noise_suppression_level = OdinNoiseSuppressionLevel_Moderate,
.transient_suppressor = false,
.gain_controller = true,
};

odin_room_configure_apm(room, apm_config);

The ODIN APM provides the following features:

Voice Activity Detection (VAD)

When enabled, ODIN will analyze the audio input signal using smart voice detection algorithm to determine the presence of speech. You can define both the probability required to start and stop transmitting.

Input Volume Gate

When enabled, the volume gate will measure the volume of the input audio signal, thus deciding when a user is speaking loud enough to transmit voice data. You can define both the root mean square power (dBFS) for when the gate should engage and disengage.

Acoustic Echo Cancellation (AEC)

When enabled the echo canceller will try to subtract echoes, reverberation, and unwanted added sounds from the audio input signal. Note, that you need to process the reverse audio stream, also known as the loopback data to be used in the ODIN echo canceller.

Noise Suppression

When enbabled, the noise suppressor will remove distracting background noise from the input audio signal. You can control the aggressiveness of the suppression. Increasing the level will reduce the noise level at the expense of a higher speech distortion.

High-Pass Filter (HPF)

When enabled, the high-pass filter will remove low-frequency content from the input audio signal, thus making it sound cleaner and more focused.

Preamplifier

When enabled, the preamplifier will boost the signal of sensitive microphones by taking really weak audio signals and making them louder.

Transient Suppression

When enabled, the transient suppressor will try to detect and attenuate keyboard clicks.

Automatic Gain Control (AGC)

When enabled, the gain controller will bring the input audio signal to an appropriate range when it's either too loud or too quiet.

User Data

Each peer within a room is associated with a unique user data represented as a byte array (uint8_t *). This data is synchronized automatically across all peers, facilitating the storage of bespoke information for every individual peer. Peers have the autonomy to modify their respective user data at any juncture, inclusive of before room entry to set an initial user data value (see

odin_room_update_peer_user_data

).

char *user_data = "{\"foo\":\"bar\"}";

odin_room_update_peer_user_data(room, (uint8_t *)user_data, strlen(user_data));

Messages

ODIN allows you to send arbitrary to every other peer in the room or even individual targets. Just like user data, a message is a byte array (uint8_t *), see

odin_room_send_message

for more information.

Note: Messages are always sent to all targets in the room, even when they moved out of proximity using setPosition.

struct MyMessage msg = {
.foo = 1,
.bar = 2,
};

odin_room_send_message(room, NULL, 0, (uint8_t *)&msg, sizeof(msg));