ToxMod Client SDK 1.9.0
ToxMod by Modulate
 
Loading...
Searching...
No Matches
main.c
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <time.h>
#include <errno.h>
#endif
#include <opus/opusfile.h>
#include "tox.h"
/*
* This file is Confidential and Proprietary to Modulate, Inc.
* This file may not be shared or distributed without permission from Modulate.
* Email contact@modulate.ai with requests, bug reports, or questions!
* Last updated September 25, 2022.
*/
/*
* @file main.c
* This ToxMod example program reads in a .opus audio file
* in realtime (e.g. reads one second of audio per second) and passes
* it through the ToxMod moderation SDK and platform. You can view the
* data flowing through the ToxMod moderation Platform by logging in
* at https://console.modulate.ai
*/
/* Opus internal sample rate - used to get sleep timings correct */
#define OPUS_DECODE_SAMPLERATE 48000
/* 960 samples = 20ms; reasonable buffer size for voice chat frameworks */
#define BUFFER_SIZE 960
/*
* Game-specific data inputs - help keep track of what game or application
* is involved, who is speaking, and who they are speaking to, etc.
*/
/* Account uuid - received from Modulate */
static const char* account_uuid = TOXMOD_ACCOUNT_UUID;
/* API key - received from Modulate */
static const char* api_key = TOXMOD_API_KEY;
/* player name - typically a player's username, unique per account_uuid */
static const char* player_name = "player1";
/*
* session name - unique per chatroom containing 1 or more players, and should be
* shared between all players in the chatroom. This is typically also the session
* ID used when specifying a voice chat room in your voice chat framework.
* The purpose of the shared session name is so that multiple players speaking
* together can have their chat streams analyzed as a single unified conversation,
* improving moderation performance and understanding, and allowing more context
* to be shown when digging into a particular clip.
*/
static const char* session_name = "chatroom123";
/*
* If using a single-tenant deployment, Modulate will give you a single-tenant prefix string.
* Most customers do not use a single-tenant deployment and may safely ignore this.
*/
static const char* single_tenant_prefix = "";
/*
* By default, Tox sends info logging to stdout and errors to stderr. If different logging is
* required, custom logging callbacks such as these ones can be set.
*/
void custom_logging_callback(const char* message) { printf("[libtox] %s\n", message); }
void custom_error_callback(const char* message) { fprintf(stderr, "[libtox] %s\n", message); }
/*
* Simple sleep function to simulate chatting in "realtime"
* - e.g. read 20ms of audio, then wait 20ms, then read, etc.
*/
int sleepms(long msec) {
#ifdef _WIN32
Sleep(msec);
return 0;
#else
struct timespec ts;
int res;
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
} while(res && errno == EINTR);
return res;
#endif
}
int main(int argc, char* argv[]) {
if(argc != 2) {
fprintf(stderr, "Usage: tox_example.out <file.opus>\n");
return 1;
}
const char* filepath = argv[1];
if(strcmp(account_uuid, "your account uuid here") == 0) {
fprintf(stderr, "Please replace the account uuid value with your account id.\n");
return 1;
}
if(strcmp(api_key, "your api key here") == 0) {
fprintf(stderr, "Please replace the api key value with your key.\n");
return 1;
}
/* Call tox_global_init() once per program, before any other tox functions */
TOX_ERROR tox_error;
tox_error = tox_global_init();
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
/*
* Set custom logging callbacks if required. In this case, just emulate the default behavior of
* printing to stdout and stderr
*/
tox_error = tox_set_log_info_callback(custom_logging_callback);
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
tox_error = tox_set_log_error_callback(custom_error_callback);
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
/*
* Create the tox_instance that will hold ToxMod's internal state.
* This must be accessible when updating the chat session name or
* other parameters around the player, gamestate, chat context, etc.
* as well as during realtime voice chat streams in order to log the audio
*/
tox_instance_t tox_instance;
tox_config_t tox_config = {.account_uuid = account_uuid,
.api_key = api_key,
.player_name_unique = player_name,
.verbose = 1,
.triage_setting = TOX_NONE};
/*
* If Modulate has given you a single-tenant prefix,
* add it to the config object single_tenant_prefix field.
*/
if(strlen(single_tenant_prefix) > 0)
tox_config.single_tenant_prefix = single_tenant_prefix;
tox_error = tox_create_instance(&tox_instance, &tox_config);
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
/*
* Join a session before a player starts submitting audio. When a user changes sessions,
* call `tox_leave_session` and `tox_join_session` to update the session name.
* It is always safe to call `tox_leave_session()`, so if there is any doubt if a player
* is already in a session, try leaving the session before joining a new one.
*/
tox_error = tox_leave_session(&tox_instance);
tox_error = tox_join_session(&tox_instance, session_name);
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
/* Begin reading an opus audio file */
int opus_error;
OggOpusFile* opus_file = op_open_file(filepath, &opus_error);
if(opus_error != 0) {
fprintf(stderr, "Failed to open file %s\n", filepath);
return 1;
}
printf("Processing simulated chat audio in realtime; uploads occur every ~15s\n");
int num_samples_read = 0;
int counter = 0;
float samples_buffer[BUFFER_SIZE];
do {
int link_index;
num_samples_read = op_read_float(opus_file, samples_buffer, BUFFER_SIZE, &link_index);
const int num_channels = op_channel_count(opus_file, link_index);
if(num_samples_read > 0) {
/* Pass the audio to ToxMod for moderation */
tox_error = tox_add_audio_float(&tox_instance, samples_buffer, num_samples_read,
OPUS_DECODE_SAMPLERATE, num_channels);
/*
* During debugging, log errors here. In production, be careful
* logging or printing from the realtime audio thread, which can
* cause latency variability, audio glitches, etc.
*/
if(tox_error)
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
/*
* For this example application, sleep after reading the audio in
* to ensure that audio is flowing through at 1 second per second
* in realtime, the same as normal chat behavior
*/
const unsigned int milliseconds_read =
(unsigned int)(((double)num_samples_read / OPUS_DECODE_SAMPLERATE) * 1000);
counter += num_samples_read;
/* Update the on screen progress bar every ~1s */
if(counter >= OPUS_DECODE_SAMPLERATE) {
counter -= OPUS_DECODE_SAMPLERATE;
printf(".");
fflush(stdout);
}
sleepms(milliseconds_read);
}
} while(num_samples_read > 0);
/*
* num_samples_read will be 0 if finished reading the file
* or < 0 if encountered an error
*/
if(num_samples_read < 0) {
fprintf(stderr, "Opus error %d reading file %s\n", num_samples_read, filepath);
return 1;
}
op_free(opus_file);
/*
* When a player leaves a session (or stops submitting audio in this case), don't forget to leave
* the session. This will signal to ToxMod, as well as human moderators that this player has left,
* or if all players leave it will signal that the session has ended.
*/
tox_error = tox_leave_session(&tox_instance);
/*
* Destroy the ToxMod internal state at the end of the program.
* Finally, perform global cleanup after all tox functions have
* been called.
*/
tox_error = tox_destroy_instance(&tox_instance);
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
tox_error = tox_global_cleanup();
if(tox_error) {
fprintf(stderr, "ToxMod error: %s\n", tox_error_name(tox_error));
return tox_error;
}
return 0;
}
Definition tox.h:90
const char * single_tenant_prefix
Definition tox.h:137
Definition tox.h:54
TOX_ERROR
Definition tox.h:177
TOX_DEF_PREFIX TOX_ERROR tox_join_session(tox_instance_t *tox_instance_ptr, const char *session_name_unique)
TOX_DEF_PREFIX TOX_ERROR tox_set_log_error_callback(void(*log_error_callback)(const char *))
TOX_DEF_PREFIX TOX_ERROR tox_create_instance(tox_instance_t *tox_instance_ptr, const tox_config_t *tox_config_ptr)
TOX_DEF_PREFIX TOX_ERROR tox_destroy_instance(tox_instance_t *tox_instance_ptr)
TOX_DEF_PREFIX TOX_ERROR tox_global_cleanup(void)
TOX_DEF_PREFIX TOX_ERROR tox_set_log_info_callback(void(*log_info_callback)(const char *))
@ TOX_NONE
Definition tox.h:68
TOX_DEF_PREFIX const char * tox_error_name(TOX_ERROR error)
TOX_DEF_PREFIX TOX_ERROR tox_leave_session(tox_instance_t *tox_instance_ptr)
TOX_DEF_PREFIX TOX_ERROR tox_add_audio_float(tox_instance_t *tox_instance_ptr, const float *audio, unsigned int num_samples, unsigned int sample_rate, unsigned int num_channels)
TOX_DEF_PREFIX TOX_ERROR tox_global_init(void)