Files
ts-mobile-go/docs/teamspeak-sdk-3.5.2/samples/source/client/main.c
T
2026-07-20 19:01:03 +08:00

1625 lines
60 KiB
C

/*
* TeamSpeak SDK client sample
*
* Copyright (c) TeamSpeak Systems GmbH
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
#include <Windows.h>
#include <conio.h>
#else
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <teamspeak/public_definitions.h>
#include <teamspeak/public_errors.h>
#include <teamspeak/clientlib.h>
#define DEFAULT_VIRTUAL_SERVER 1
#define NAME_BUFSIZE 1024
#define CHANNEL_PASSWORD_BUFSIZE 1024
#define CHECK_ERROR(x) if((error = x) != ERROR_ok) { goto on_error; }
#define IDENTITY_BUFSIZE 1024
#ifdef _WIN32
#define snprintf sprintf_s
#define SLEEP(x) Sleep(x)
#else
#define SLEEP(x) usleep(x*1000)
#endif
/* This is a global variable to indicate if sound needs to be recorded.
Normally one would have thread synchronization with locks etc. for
thread safety. In the intterest of simplicity, this sample uses the
most simple way to safely record, using this variable
*/
int recordSound = 0;
/* For voice activation detection demo */
uint64 vadTestscHandlerID;
int vadTestTalkStatus = 0;
/* Enable to use custom encryption */
/* #define USE_CUSTOM_ENCRYPTION
#define CUSTOM_CRYPT_KEY 123 */
/* Uncomment "#define CUSTOM_PASSWORDS" to try custom passwords.
* Please note that you have to do the same on the server demo too */
/* #define CUSTOM_PASSWORDS */
/* a struct used for sound recording */
struct WaveHeader {
/* Riff chunk */
char riffId[4];
unsigned int len;
char riffType[4];
/* Format chunk */
char fmtId[4]; // 'fmt '
unsigned int fmtLen;
unsigned short formatTag;
unsigned short channels;
unsigned int samplesPerSec;
unsigned int avgBytesPerSec;
unsigned short blockAlign;
unsigned short bitsPerSample;
/* Data chunk */
char dataId[4]; // 'data'
unsigned int dataLen;
};
/*
* Callback for connection status change.
* Connection status switches through the states STATUS_DISCONNECTED, STATUS_CONNECTING, STATUS_CONNECTED and STATUS_CONNECTION_ESTABLISHED.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* newStatus - New connection status, see the enum ConnectStatus in clientlib_publicdefinitions.h
* errorNumber - Error code. Should be zero when connecting or actively disconnection.
* Contains error state when losing connection.
*/
void onConnectStatusChangeEvent(uint64 serverConnectionHandlerID, int newStatus, unsigned int errorNumber) {
printf("Connect status changed: %llu %d %d\n", (unsigned long long)serverConnectionHandlerID, newStatus, errorNumber);
/* Failed to connect ? */
if(newStatus == STATUS_DISCONNECTED && errorNumber == ERROR_failed_connection_initialisation) {
printf("Looks like there is no server running.\n");
}
}
/*
* Callback for current channels being announced to the client after connecting to a server.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* channelID - ID of the announced channel
* channelParentID - ID of the parent channel
*/
void onNewChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID) {
/* Query channel name from channel ID */
char* name;
unsigned int error;
printf("onNewChannelEvent: %llu %llu %llu\n", (unsigned long long)serverConnectionHandlerID, (unsigned long long)channelID, (unsigned long long)channelParentID);
if((error = ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name)) == ERROR_ok) {
printf("New channel: %llu %s \n", (unsigned long long)channelID, name);
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
} else {
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error getting channel name in onNewChannelEvent: %s\n", errormsg);
ts3client_freeMemory(errormsg);
}
}
}
/*
* Callback for just created channels.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* channelID - ID of the announced channel
* channelParentID - ID of the parent channel
* invokerID - ID of the client who created the channel
* invokerName - Name of the client who created the channel
*/
void onNewChannelCreatedEvent(uint64 serverConnectionHandlerID, uint64 channelID, uint64 channelParentID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
char* name;
/* Query channel name from channel ID */
if(ts3client_getChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, &name) != ERROR_ok)
return;
printf("New channel created: %s \n", name);
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
}
/*
* Callback when a channel was deleted.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* channelID - ID of the deleted channel
* invokerID - ID of the client who deleted the channel
* invokerName - Name of the client who deleted the channel
*/
void onDelChannelEvent(uint64 serverConnectionHandlerID, uint64 channelID, anyID invokerID, const char* invokerName, const char* invokerUniqueIdentifier) {
printf("Channel ID %llu deleted by %s (%u)\n", (unsigned long long)channelID, invokerName, invokerID);
}
/*
* Called when a client joins, leaves or moves to another channel.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* clientID - ID of the moved client
* oldChannelID - ID of the old channel left by the client
* newChannelID - ID of the new channel joined by the client
* visibility - Visibility of the moved client. See the enum Visibility in clientlib_publicdefinitions.h
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
*/
void onClientMoveEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* moveMessage) {
printf("ClientID %u moves from channel %llu to %llu with message %s\n", clientID, (unsigned long long)oldChannelID, (unsigned long long)newChannelID, moveMessage);
}
/*
* Callback for other clients in current and subscribed channels being announced to the client.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* clientID - ID of the announced client
* oldChannelID - ID of the subscribed channel where the client left visibility
* newChannelID - ID of the subscribed channel where the client entered visibility
* visibility - Visibility of the announced client. See the enum Visibility in clientlib_publicdefinitions.h
* Values: ENTER_VISIBILITY, RETAIN_VISIBILITY, LEAVE_VISIBILITY
*/
void onClientMoveSubscriptionEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility) {
char* name;
/* Query client nickname from ID */
if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
return;
printf("New client: %s \n", name);
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
}
/*
* Called when a client drops his connection.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* clientID - ID of the moved client
* oldChannelID - ID of the channel the leaving client was previously member of
* newChannelID - 0, as client is leaving
* visibility - Always LEAVE_VISIBILITY
* timeoutMessage - Optional message giving the reason for the timeout
*/
void onClientMoveTimeoutEvent(uint64 serverConnectionHandlerID, anyID clientID, uint64 oldChannelID, uint64 newChannelID, int visibility, const char* timeoutMessage) {
printf("ClientID %u timeouts with message %s\n", clientID, timeoutMessage);
}
/*
* This event is called when a client starts or stops talking.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* status - 1 if client starts talking, 0 if client stops talking
* isReceivedWhisper - 1 if this event was caused by whispering, 0 if caused by normal talking
* clientID - ID of the client who announced the talk status change
*/
void onTalkStatusChangeEvent(uint64 serverConnectionHandlerID, int status, int isReceivedWhisper, anyID clientID) {
char* name;
/* If we are currently in configure-microphone mode, remember the status and use it later in the loop in configureMicrophone() */
if(serverConnectionHandlerID == vadTestscHandlerID) {
vadTestTalkStatus = status;
return;
}
/* Query client nickname from ID */
if (ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok)
{
if (status == STATUS_TALKING) {
printf("Client \"%u\" starts talking.\n", clientID);
}
else {
printf("Client \"%u\" stops talking.\n", clientID);
}
return;
}
if(status == STATUS_TALKING) {
printf("Client \"%s\" starts talking.\n", name);
} else {
printf("Client \"%s\" stops talking.\n", name);
}
ts3client_freeMemory(name); /* Release dynamically allocated memory only if function succeeded */
}
/*
* This event is called when another client starts whispering to own client. Own client can decide to accept or deny
* receiving the whisper by adding the sending client to the whisper allow list. If not added, whispering is blocked.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* clientID - ID of the whispering client
*/
void onIgnoredWhisperEvent(uint64 serverConnectionHandlerID, anyID clientID) {
unsigned int error;
/* Add sending client to whisper allow list so own client will hear the voice data.
* It is sufficient to add a clientID only once, not everytime this event is called. However it won't
* hurt to add the same clientID to the allow list repeatedly, but is is not necessary. */
if((error = ts3client_allowWhispersFrom(serverConnectionHandlerID, clientID)) != ERROR_ok) {
printf("Error setting client on whisper allow list: %u\n", error);
}
printf("Added client %d to whisper allow list\n", clientID);
}
void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) {
printf("Error for server %llu: %s (%u) %s\n", (unsigned long long)serverConnectionHandlerID, errorMessage, error, extraMessage);
}
/*
* Callback for user-defined logging.
*
* Parameter:
* logMessage - Log message text
* logLevel - Severity of log message
* logChannel - Custom text to categorize the message channel
* logID - Virtual server ID giving the virtual server source of the log event
* logTime - String with the date and time the log entry occured
* completeLogString - Verbose log message including all previous parameters for convinience
*/
void onUserLoggingMessageEvent(const char* logMessage, int logLevel, const char* logChannel, uint64 logID, const char* logTime, const char* completeLogString) {
/* Your custom error display here... */
/* printf("LOG: %s\n", completeLogString); */
if(logLevel == LogLevel_CRITICAL) {
exit(1); /* Your custom handling of critical errors */
}
}
/*
* Callback allowing to apply custom encryption to outgoing packets.
* Using this function is optional. Do not implement if you want to handle the TeamSpeak SDK encryption.
*
* Parameters:
* dataToSend - Pointer to an array with the outgoing data to be encrypted
* sizeOfData - Pointer to an integer value containing the size of the data array
*
* Apply your custom encryption to the data array. If the encrypted data is smaller than sizeOfData, write
* your encrypted data into the existing memory of dataToSend. If your encrypted data is larger, you need
* to allocate memory and redirect the pointer dataToSend. You need to take care of freeing your own allocated
* memory yourself. The memory allocated by the SDK, to which dataToSend is originally pointing to, must not
* be freed.
*
*/
void onCustomPacketEncryptEvent(char** dataToSend, unsigned int* sizeOfData) {
#ifdef USE_CUSTOM_ENCRYPTION
unsigned int i;
for(i = 0; i < *sizeOfData; i++) {
(*dataToSend)[i] ^= CUSTOM_CRYPT_KEY;
}
#endif
}
/*
* Callback allowing to apply custom encryption to incoming packets
* Using this function is optional. Do not implement if you want to handle the TeamSpeak SDK encryption.
*
* Parameters:
* dataToSend - Pointer to an array with the received data to be decrypted
* sizeOfData - Pointer to an integer value containing the size of the data array
*
* Apply your custom decryption to the data array. If the decrypted data is smaller than dataReceivedSize, write
* your decrypted data into the existing memory of dataReceived. If your decrypted data is larger, you need
* to allocate memory and redirect the pointer dataReceived. You need to take care of freeing your own allocated
* memory yourself. The memory allocated by the SDK, to which dataReceived is originally pointing to, must not
* be freed.
*/
void onCustomPacketDecryptEvent(char** dataReceived, unsigned int* dataReceivedSize) {
#ifdef USE_CUSTOM_ENCRYPTION
unsigned int i;
for(i = 0; i < *dataReceivedSize; i++) {
(*dataReceived)[i] ^= CUSTOM_CRYPT_KEY;
}
#endif
}
/*
* Callback allowing access to voice data after it has been mixed by TeamSpeak
* This event can be used to alter/add to the voice data being played by TeamSpeak.
* But here we use it to record the voice data to a wave file.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID
* samples - Pointer to a buffer containg 16 bit voice data samples at 48000 Hz. Channels are interleaved.
* sampleCount - The number of samples 1 channel of sample data contains.
* channels - The number of channels the sample data contains.
* channelSpeakerArray - A bitmask of the speakers for each channel.
* channelFillMask - A bitmask of channels that actually have valid data.
*
* -The size of the data "samples" points to is: sizeof(short)*sampleCount*channels
* -channelSpeakerArray uses SPEAKER_ defined in public_definitions.h
* -In the interrest of optimizations, a channel only contains data, if there is sound data for it. For example:
* in 5.1 or 7.1 we (almost) never have data for the subwoofer. Teamspeak then leaves the data in this channel
* undefined. This is more efficient for mixing.
* This implementation will record sound to a 2 channel (stereo) wave file. This sample assumes there is only
* 1 connection to a server
* Hint: Normally you would want to defer the writing to an other thread because this callback is very time sensitive
*/
void onEditMixedPlaybackVoiceDataEvent(uint64 serverConnectionHandlerID, short* samples, int sampleCount, int channels, const unsigned int* channelSpeakerArray, unsigned int* channelFillMask){
#define OUTPUTCHANNELS 2
static FILE *pfile = NULL;
static struct WaveHeader header = { {'R','I','F','F'}, 0, {'W','A','V','E'}, {'f','m','t',' '}, 16, 1, 2, 48000, 48000*(16/2)*2, (16/2)*2, 16, {'d','a','t','a'}, 0 };
int currentSampleMix[OUTPUTCHANNELS]; /*a per channel/sample mix buffer*/
int channelCount[OUTPUTCHANNELS] = {0,0}; /*how many input channels does the output channel contain */
int currentInChannel;
int currentOutChannel;
int currentSample;
/*for clipping*/
short shortval;
int intval;
short* outputBuffer;
int leftChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_BACK_CENTER | SPEAKER_SIDE_LEFT | SPEAKER_TOP_CENTER | SPEAKER_TOP_FRONT_LEFT | SPEAKER_TOP_FRONT_CENTER | SPEAKER_TOP_BACK_LEFT | SPEAKER_TOP_BACK_CENTER;
int rightChannelMask = SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_RIGHT_OF_CENTER | SPEAKER_BACK_CENTER | SPEAKER_SIDE_RIGHT | SPEAKER_TOP_CENTER | SPEAKER_TOP_FRONT_RIGHT | SPEAKER_TOP_FRONT_CENTER | SPEAKER_TOP_BACK_RIGHT | SPEAKER_TOP_BACK_CENTER;
/*detect state changes*/
if (recordSound && (pfile == NULL)){
/*start recording*/
header.len = 0;
header.dataLen = 0;
if((pfile = fopen("recordedvoices.wav", "wb")) == NULL) return;
fwrite(&header, sizeof(struct WaveHeader), 1, pfile);
} else if (!recordSound && (pfile != NULL)){
/*stop recording*/
header.len = sizeof(struct WaveHeader)+header.dataLen - 8;
fseek (pfile, 0, SEEK_SET);
fwrite(&header, sizeof(struct WaveHeader), 1, pfile);
fclose(pfile);
pfile = NULL;
}
/*if there is nothing to do, quit*/
if (pfile == NULL || sampleCount == 0 || channels == 0) return;
/* initialize channel mixing */
currentInChannel = 0;
/*loop over all possible speakers*/
for (currentInChannel=0; currentInChannel < channels; ++currentInChannel) {
/*if the speaker has actual data*/
if ((*channelFillMask & (1<<currentInChannel)) != 0){
/*add to the outChannelSpeakerSet*/
if ((channelSpeakerArray[currentInChannel] & leftChannelMask) != 0) channelCount[0]++;
if ((channelSpeakerArray[currentInChannel] & rightChannelMask) != 0) channelCount[1]++;
}
}
/*get the outbut buffer*/
outputBuffer = (short*) malloc( sizeof(short)*sampleCount*2 /*output channels*/);
/* hint: if channelCount is 0 for all channels, we could write a zero buffer and quit here */
/*mix the samples*/
for (currentSample = 0; currentSample < sampleCount; currentSample++){
currentSampleMix[0] = currentSampleMix[1] = 0;
/*loop over all channels in this frame */
for(currentInChannel =0; currentInChannel < channels; currentInChannel++){
if ((channelSpeakerArray[currentInChannel] & leftChannelMask) != 0) currentSampleMix[0] += samples[ (currentSample*channels)+currentInChannel ];
if ((channelSpeakerArray[currentInChannel] & rightChannelMask) != 0) currentSampleMix[1] += samples[ (currentSample*channels)+currentInChannel ];
}
/*collected all samples, store mixed sample */
for (currentOutChannel = 0; currentOutChannel < OUTPUTCHANNELS; currentOutChannel++){
if (channelCount[currentOutChannel] == 0){
outputBuffer[ (currentSample*OUTPUTCHANNELS) + currentOutChannel] = 0;
} else {
/*clip*/
intval = currentSampleMix[currentOutChannel]/channelCount[currentOutChannel];
if (intval >= SHRT_MAX) shortval = SHRT_MAX;
else if (intval <= SHRT_MIN) shortval = SHRT_MIN;
else shortval = (short) intval;
/*store*/
outputBuffer[ (currentSample*OUTPUTCHANNELS) + currentOutChannel] = shortval;
}
}
}
/*write data & update header */
fwrite(outputBuffer, sampleCount*sizeof(short)*OUTPUTCHANNELS, 1, pfile);
header.dataLen += sampleCount*sizeof(short)*OUTPUTCHANNELS;
/*free buffer*/
free(outputBuffer);
}
#ifdef CUSTOM_PASSWORDS
/*
* Called to encrypt channel and server passwords
*
* In this example, we do not do any encryption. That way we have clear
* text passwords on the server.
*
* Parameters:
* serverConnectionHandlerID - Server connection handler ID.
* plaintext - the clear text password
* encryptedText - pointer to a buffer to store the encrypted password
* encryptedTextByteSize - the size of the encryptedText buffer
*/
void onClientPasswordEncrypt(uint64 serverConnectionHandlerID, const char* plaintext, char* encryptedText, int encryptedTextByteSize){
int pt_len;
printf("onClientPasswordEncrypt called\n");
pt_len = strlen(plaintext);
if (encryptedTextByteSize < pt_len-1) pt_len = encryptedTextByteSize-1;
memcpy(encryptedText, plaintext, pt_len);
encryptedText[pt_len]=0;
}
#endif
/*
* Optional event to further adjust 3D sound. Usually this is not needed, setting 3D position of own and other clients as shown
* setClient3DPosition is sufficient to configure 3D sound.
*/
void onCustom3dRolloffCalculationClientEvent(uint64 serverConnectionHandlerID, anyID clientID, float distance, float* volume)
{
printf("onCustom3dRolloffCalculationClientEvent: clientID=%hu distance=%f volume=%f\n", clientID, distance, *volume);
/* Volume can now be modified further to overwrite the value calculated by the SDK */
/* *volume *= 10.0f; */
}
/*
* Print all channels of the given virtual server
*/
void showChannels(uint64 serverConnectionHandlerID) {
uint64 *ids;
int i;
unsigned int error;
printf("\nList of channels on virtual server %llu:\n", (unsigned long long)serverConnectionHandlerID);
if((error = ts3client_getChannelList(serverConnectionHandlerID, &ids)) != ERROR_ok) { /* Get array of channel IDs */
printf("Error getting channel list: %d\n", error);
return;
}
if(!ids[0]) {
printf("No channels\n\n");
ts3client_freeMemory(ids);
return;
}
for(i=0; ids[i]; i++) {
char* name;
if((error = ts3client_getChannelVariableAsString(serverConnectionHandlerID, ids[i], CHANNEL_NAME, &name)) != ERROR_ok) { /* Query channel name */
printf("Error getting channel name: %d\n", error);
break;
}
printf("%llu - %s\n", (unsigned long long)ids[i], name);
ts3client_freeMemory(name);
}
printf("\n");
ts3client_freeMemory(ids); /* Release array */
}
/*
* Print all clients on the given virtual server in the specified channel
*/
void showChannelClients(uint64 serverConnectionHandlerID, uint64 channelID) {
anyID* ids;
anyID ownClientID;
int i;
unsigned int error;
printf("\nList of clients in channel %llu on virtual server %llu:\n", (unsigned long long)channelID, (unsigned long long)serverConnectionHandlerID);
if((error = ts3client_getChannelClientList(serverConnectionHandlerID, channelID, &ids)) != ERROR_ok) { /* Get array of client IDs */
printf("Error getting client list for channel %llu: %d\n", (unsigned long long)channelID, error);
return;
}
if(!ids[0]) {
printf("No clients\n\n");
ts3client_freeMemory(ids);
return;
}
/* Get own clientID as we need to call CLIENT_FLAG_TALKING with getClientSelfVariable for own client */
if((error = ts3client_getClientID(serverConnectionHandlerID, &ownClientID)) != ERROR_ok) {
printf("Error querying own client ID: %d\n", error);
return;
}
for(i=0; ids[i]; i++) {
char* name;
int talkStatus;
if((error = ts3client_getClientVariableAsString(serverConnectionHandlerID, ids[i], CLIENT_NICKNAME, &name)) != ERROR_ok) { /* Query client nickname... */
printf("Error querying client nickname: %d\n", error);
break;
}
if(ids[i] == ownClientID) { /* CLIENT_FLAG_TALKING must be queried with getClientSelfVariable for own client */
if((error = ts3client_getClientSelfVariableAsInt(serverConnectionHandlerID, CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying own client talk status: %d\n", error);
break;
}
} else {
if((error = ts3client_getClientVariableAsInt(serverConnectionHandlerID, ids[i], CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying client talk status: %d\n", error);
break;
}
}
printf("%u - %s (%stalking)\n", ids[i], name, (talkStatus == STATUS_TALKING ? "" : "not "));
ts3client_freeMemory(name);
}
printf("\n");
ts3client_freeMemory(ids); /* Release array */
}
/*
* Print all visible clients on the given virtual server
*/
void showClients(uint64 serverConnectionHandlerID) {
anyID *ids;
anyID ownClientID;
int i;
unsigned int error;
printf("\nList of all visible clients on virtual server %llu:\n", (unsigned long long)serverConnectionHandlerID);
if((error = ts3client_getClientList(serverConnectionHandlerID, &ids)) != ERROR_ok) { /* Get array of client IDs */
printf("Error getting client list: %d\n", error);
return;
}
if(!ids[0]) {
printf("No clients\n\n");
ts3client_freeMemory(ids);
return;
}
/* Get own clientID as we need to call CLIENT_FLAG_TALKING with getClientSelfVariable for own client */
if((error = ts3client_getClientID(serverConnectionHandlerID, &ownClientID)) != ERROR_ok) {
printf("Error querying own client ID: %d\n", error);
return;
}
for(i=0; ids[i]; i++) {
char* name;
int talkStatus;
if((error = ts3client_getClientVariableAsString(serverConnectionHandlerID, ids[i], CLIENT_NICKNAME, &name)) != ERROR_ok) { /* Query client nickname... */
printf("Error querying client nickname: %d\n", error);
break;
}
if(ids[i] == ownClientID) { /* CLIENT_FLAG_TALKING must be queried with getClientSelfVariable for own client */
if((error = ts3client_getClientSelfVariableAsInt(serverConnectionHandlerID, CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying own client talk status: %d\n", error);
break;
}
} else {
if((error = ts3client_getClientVariableAsInt(serverConnectionHandlerID, ids[i], CLIENT_FLAG_TALKING, &talkStatus)) != ERROR_ok) {
printf("Error querying client talk status: %d\n", error);
break;
}
}
printf("%u - %s (%stalking)\n", ids[i], name, (talkStatus == STATUS_TALKING ? "" : "not "));
ts3client_freeMemory(name);
}
printf("\n");
ts3client_freeMemory(ids); /* Release array */
}
void emptyInputBuffer() {
int c;
while((c = getchar()) != '\n' && c != EOF);
}
uint64 enterChannelID() {
uint64 channelID;
int n;
printf("\nEnter channel ID: ");
n = scanf("%llu", (unsigned long long*)&channelID);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return 0;
}
return channelID;
}
void createDefaultChannelName(char *name) {
static int i = 1;
sprintf(name, "Channel_%d", i++);
}
void enterName(char *name) {
char *s;
printf("\nEnter name: ");
fgets(name, NAME_BUFSIZE, stdin);
s = strrchr(name, '\n');
if(s) {
*s = '\0';
}
}
void enterPassword(char *password) {
char *s;
printf("\nEnter password: ");
fgets(password, CHANNEL_PASSWORD_BUFSIZE, stdin);
s = strrchr(password, '\n');
if(s) {
*s = '\0';
}
}
void createChannel(uint64 serverConnectionHandlerID, const char *name, const char* password) {
unsigned int error;
/* Set data of new channel. Use channelID of 0 for creating channels. */
CHECK_ERROR(ts3client_setChannelVariableAsString(serverConnectionHandlerID, 0, CHANNEL_NAME, name));
CHECK_ERROR(ts3client_setChannelVariableAsString(serverConnectionHandlerID, 0, CHANNEL_TOPIC, "Test channel topic"));
CHECK_ERROR(ts3client_setChannelVariableAsString(serverConnectionHandlerID, 0, CHANNEL_DESCRIPTION, "Test channel description"));
CHECK_ERROR(ts3client_setChannelVariableAsInt (serverConnectionHandlerID, 0, CHANNEL_FLAG_PERMANENT, 1));
CHECK_ERROR(ts3client_setChannelVariableAsInt (serverConnectionHandlerID, 0, CHANNEL_FLAG_SEMI_PERMANENT, 0));
CHECK_ERROR(ts3client_setChannelVariableAsInt (serverConnectionHandlerID, 0, CHANNEL_CODEC_QUALITY, 10));
if(password && *password){
CHECK_ERROR(ts3client_setChannelVariableAsString(serverConnectionHandlerID, 0, CHANNEL_PASSWORD, password));
}
/* Flush changes to server */
CHECK_ERROR(ts3client_flushChannelCreation(serverConnectionHandlerID, 0, NULL));
printf("\nCreated channel\n\n");
return;
on_error:
printf("\nError creating channel: %d\n\n", error);
}
void deleteChannel(uint64 serverConnectionHandlerID) {
uint64 channelID;
unsigned int error;
/* Query channel ID from user */
channelID = enterChannelID();
/* Delete channel */
if((error = ts3client_requestChannelDelete(serverConnectionHandlerID, channelID, 0, NULL)) == ERROR_ok) {
printf("Deleted channel %llu\n\n", (unsigned long long)channelID);
} else {
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error requesting channel delete: %s (%d)\n\n", errormsg, error);
ts3client_freeMemory(errormsg);
}
}
}
void renameChannel(uint64 serverConnectionHandlerID) {
uint64 channelID;
unsigned int error;
char name[NAME_BUFSIZE];
/* Query channel ID from user */
channelID = enterChannelID();
/* Query new channel name from user */
enterName(name);
/* Change channel name and flush changes */
CHECK_ERROR(ts3client_setChannelVariableAsString(serverConnectionHandlerID, channelID, CHANNEL_NAME, name));
CHECK_ERROR(ts3client_flushChannelUpdates(serverConnectionHandlerID, channelID, NULL));
printf("Renamed channel %llu\n\n", (unsigned long long)channelID);
return;
on_error:
printf("Error renaming channel: %d\n\n", error);
}
void switchChannel(uint64 serverConnectionHandlerID) {
unsigned int error;
char password[CHANNEL_PASSWORD_BUFSIZE];
#ifndef CUSTOM_PASSWORDS
int hasPassword;
#endif
/* Query channel ID from user */
uint64 channelID = enterChannelID();
/* Query own client ID */
anyID clientID;
if((error = ts3client_getClientID(serverConnectionHandlerID, &clientID)) != ERROR_ok) {
printf("Error querying own client ID: %d\n", error);
return;
}
#ifndef CUSTOM_PASSWORDS
/* Using standard password mechanism */
/* Check if channel has a password set */
if((error = ts3client_getChannelVariableAsInt(serverConnectionHandlerID, channelID, CHANNEL_FLAG_PASSWORD, &hasPassword)) != ERROR_ok) {
printf("Failed to get password flag: %d\n", error);
return;
}
/* Get channel password if channel is password protected */
if(hasPassword) {
enterPassword(password);
} else {
password[0] = '\0';
}
#else
/* Using custom password mechanism, always ask user for password */
enterPassword(password);
#endif
/* Request moving own client into given channel */
anyID ids_to_move[2];
ids_to_move[0] = clientID;
ids_to_move[1] = 0;
if((error = ts3client_requestClientMove(serverConnectionHandlerID, ids_to_move, channelID, password, NULL)) != ERROR_ok) {
printf("Error moving client into channel channel: %d\n", error);
return;
}
printf("Switching into channel %llu\n\n", (unsigned long long)channelID);
}
void setVadMode(uint64 serverConnectionHandlerID)
{
int vad_mode, n;
unsigned int error;
char s[100];
printf("Vad Mode:\n");
printf("0 - likelihood based (default)\n");
printf("1 - power based\n");
printf("2 - likelihood and power based\n\n");
printf("The Vad Level is tied to modes 2 and 3 - the modes using power.\n");
printf("\nEnter VAD mode: ");
n = scanf("%d", &vad_mode);
emptyInputBuffer();
if (n == 0) {
printf("Invalid input. Please enter a number in the range 0 - 2.\n\n");
return;
}
/* Adjust "vad_mode" preprocessor value */
snprintf(s, 100, "%d", vad_mode);
if ((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "vad_mode", s)) != ERROR_ok) {
printf("Error setting VAD mode: %d\n", error);
return;
}
printf("\nSet VAD mode to %s.\n\n", s);
}
void toggleVAD(uint64 serverConnectionHandlerID) {
static short b = 0;
unsigned int error;
/* Adjust "vad" preprocessor value */
if((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "vad", b ? "false" : "true")) != ERROR_ok) {
printf("Error toggling VAD: %d\n", error);
return;
}
b = !b;
printf("\nToggled VAD %s.\n\n", b ? "on" : "off");
}
void setVadLevel(uint64 serverConnectionHandlerID) {
int vad, n;
unsigned int error;
char s[100];
printf("\nEnter VAD level: ");
n = scanf("%d", &vad);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
/* Adjust "voiceactivation_level" preprocessor value */
snprintf(s, 100, "%d", vad);
if((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "voiceactivation_level", s)) != ERROR_ok) {
printf("Error setting VAD level: %d\n", error);
return;
}
printf("\nSet VAD level to %s.\n\n", s);
}
void toggleDenoise(uint64 serverConnectionHandlerID) {
static short b = 1;
unsigned int error;
/* Adjust "vad" preprocessor value */
if ((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "denoise", b ? "false" : "true")) != ERROR_ok) {
printf("Error toggling Denoiser: %d\n", error);
return;
}
b = !b;
printf("\nToggled Denoiser %s.\n\n", b ? "on" : "off");
}
void setDenoiserLevel(uint64 serverConnectionHandlerID) {
int denoiser_level, n;
unsigned int error;
char s[100];
printf("\nEnter Denoiser level: ");
n = scanf("%d", &denoiser_level);
emptyInputBuffer();
if (n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
/* Adjust "voiceactivation_level" preprocessor value */
snprintf(s, 100, "%d", denoiser_level);
if ((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "denoiser_level", s)) != ERROR_ok) {
printf("Error setting Denoiser level: %d\n", error);
return;
}
printf("\nSet Denoiser level to %s.\n\n", s);
}
void toggleTypingSuppression(uint64 serverConnectionHandlerID) {
static short b = 0;
unsigned int error;
/* Adjust "vad" preprocessor value */
if ((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "typing_suppression", b ? "false" : "true")) != ERROR_ok) {
printf("Error toggling typing suppression: %d\n", error);
return;
}
b = !b;
printf("\nToggled typing suppression %s.\n\n", b ? "on" : "off");
}
void toggleAec(uint64 serverConnectionHandlerID)
{
static short b = 0;
unsigned int error;
/* Adjust "aec" preprocessor value */
if ((error = ts3client_setPreProcessorConfigValue(serverConnectionHandlerID, "aec", b ? "false" : "true")) != ERROR_ok) {
printf("Error toggling echo cancellation: %d\n", error);
return;
}
b = !b;
printf("\nToggled echo cancellation %s.\n\n", b ? "on" : "off");
}
void toggleAgc(uint64 serverConnectionHandlerID) {
static short b = 1;
unsigned int error;
/* Adjust "agc" preprocessor value */
if ((error = ts3client_setPlaybackConfigValue(serverConnectionHandlerID, "agc", b ? "false" : "true")) != ERROR_ok) {
printf("Error toggling Automatic Gain Control (AGC): %d\n", error);
return;
}
b = !b;
printf("\nToggled Automatic Gain Control (AGC) %s.\n\n", b ? "on" : "off");
}
void toggleEchoReductionDucking(uint64 serverConnectionHandlerID) {
int vad, n;
unsigned int error;
char s[100];
printf("\nEnter Ducking level (dB): ");
n = scanf("%d", &vad);
emptyInputBuffer();
if (n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
/* Adjust "echo_reduction_ducking" preprocessor value */
snprintf(s, 100, "%d", vad);
if ((error = ts3client_setPlaybackConfigValue(serverConnectionHandlerID, "echo_reduction_ducking", s)) != ERROR_ok) {
printf("Error setting echo_reduction_ducking level: %d\n", error);
return;
}
printf("\nSet echo_reduction_ducking level to %s.\n\n", s);
}
void requestWhisperList(uint64 serverConnectionHandlerID) {
int n;
anyID clientID;
uint64 targetID;
unsigned int error;
uint64 targetChannels[2];
printf("\nEnter ID of the client whose whisper list should be modified (0 for own client): ");
n = scanf("%hu", &clientID);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
printf("\nEnter target channel ID: ");
n = scanf("%llu", (unsigned long long*)&targetID);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
targetChannels[0] = targetID;
targetChannels[1] = 0;
if((error = ts3client_requestClientSetWhisperList(serverConnectionHandlerID, clientID, targetChannels, NULL, NULL)) != ERROR_ok) {
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error requesting whisperlist: %s\n", errormsg);
ts3client_freeMemory(errormsg);
}
return;
}
printf("Whisper list requested for client %d in channel %llu\n", clientID, (unsigned long long)targetID);
}
void requestClearWhisperList(uint64 serverConnectionHandlerID) {
int n;
anyID clientID;
unsigned int error;
printf("\nEnter ID of the client whose whisper list should be cleared (0 for own client): ");
n = scanf("%hu", &clientID);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
return;
}
if((error = ts3client_requestClientSetWhisperList(serverConnectionHandlerID, clientID, NULL, NULL, NULL)) != ERROR_ok) {
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error clearing whisperlist: %s\n", errormsg);
ts3client_freeMemory(errormsg);
}
return;
}
printf("Whisper list cleared for client %u\n", clientID);
}
void setClient3DPosition(uint64 serverConnectionHandlerID)
{
int i, n;
anyID clientID;
float pos[3];
TS3_VECTOR position = { 0 };
char* txt[] = { "x", "y", "z" };
/* Query ID of client whose 3D position we want to change */
printf("\nEnter ID of the client whose 3D position should be changed (0 for own client): ");
n = scanf("%hu", &clientID);
emptyInputBuffer();
if(n == 0)
{
printf("Invalid input. Please enter a number.\n\n");
return;
}
/* Query 3D position */
for(i = 0; i < 3; ++i)
{
printf("Enter %s coordinate: ", txt[i]);
n = scanf("%f", &pos[i]);
emptyInputBuffer();
if(n == 0)
{
printf("Invalid input. Please enter a number.\n\n");
return;
}
}
position.x = pos[0];
position.y = pos[1];
position.z = pos[2];
if(clientID == 0)
{
/* Own client */
TS3_VECTOR zero = { 0 };
unsigned int error = ts3client_systemset3DListenerAttributes(serverConnectionHandlerID, &position, &zero, &zero);
if(error != ERROR_ok)
{
printf("Failed to set 3D position for own client: %d\n", error);
return;
}
printf("Set 3D position for own client to %f, %f, %f\n", position.x, position.y, position.z);
}
else
{
/* Other client */
unsigned int error = ts3client_channelset3DAttributes(serverConnectionHandlerID, clientID, &position);
if(error != ERROR_ok)
{
printf("Failed to set 3D position for other client: %d\n", error);
return;
}
printf("Set 3D position for client %hu to %f, %f, %f\n", clientID, position.x, position.y, position.z);
}
}
int initLocalTestMode() {
unsigned int error;
/* spawn a new server connection handler used as local loopback device */
if((error = ts3client_spawnNewServerConnectionHandler(0, &vadTestscHandlerID)) != ERROR_ok) {
printf("Error spawning server connection handler: %d\n", error);
vadTestscHandlerID = 0;
return 1;
}
/* Open default capture device for the new server connection handler */
if((error = ts3client_openCaptureDevice(vadTestscHandlerID, "", "")) != ERROR_ok) {
printf("Error opening capture device: %d\n", error);
return 1;
}
/* Open default playback device for the new server connection handler */
if((error = ts3client_openPlaybackDevice(vadTestscHandlerID, "", "")) != ERROR_ok) {
printf("Error opening playback device: %d\n", error);
return 1;
}
/* Set the server connection handler as a local loopback device, so we can hear our own voice without connecting to a server. */
/* The original capture device for the current server is automatically deactivated. */
if((error = ts3client_setLocalTestMode(vadTestscHandlerID, 1)) != ERROR_ok){
printf("Error setting local test mode\n");
return 1;
}
return 0;
}
void destroyLocalTestMode(uint64 scHandlerID){
unsigned int error;
/* Close playback device */
if((ts3client_closePlaybackDevice(scHandlerID)) != ERROR_ok){
printf("Unable to close playback device\n");
}
/* Close capture device */
if((ts3client_closeCaptureDevice(scHandlerID)) != ERROR_ok) {
printf("Unable to close capture device\n");
}
/* Unset local test mode */
if((error = ts3client_setLocalTestMode(scHandlerID, 0)) != ERROR_ok) {
printf("Unable to stop local test mode\n");
}
/* Destroy schandlerid */
if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok){
printf("Unable to destroy scHandler\n");
}
/* After closing the local loopback device, reactivate the microphone on our current server connection. */
if((error = ts3client_activateCaptureDevice(DEFAULT_VIRTUAL_SERVER)) != ERROR_ok){
printf("unable to reactivate capture device\n");
}
}
void printVadLevel() {
unsigned int error;
float result;
if((error = ts3client_getPreProcessorInfoValueFloat(vadTestscHandlerID, "decibel_last_period", &result)) != ERROR_ok) {
printf("Error getting vad level\n");
}
printf("%.2f - %s", result, (vadTestTalkStatus == STATUS_TALKING ? "talking" : "not talking"));
printf("\n");
}
/* Set the microphone voice activation detection level */
void configureMicrophone() {
unsigned int error;
int counter = 0;
/* Enable local loopback device */
if(initLocalTestMode() != 0) {
return;
}
/* Local loopback device is setup, now enter loop where the user can change the voice activation level while once per second the
* current volume level is printed. */
printf("\n**********************************\n");
printf("Entering configure microphone mode\n");
printf("[v] - set VAD level\n");
printf("[q] - quit microphone configuration\n\n");
for(;;) {
#ifdef _WIN32
if(_kbhit()) {
int c = _getche();
#else
{ int c = getc(stdin); // No kbhit on posix
#endif
switch(c) {
case 'v': {
int n;
float inputVadLevel;
char vad[128];
printf("Insert value to change voice activations level\n");
n = scanf("%f", &inputVadLevel);
emptyInputBuffer();
if(n == 0) {
printf("Invalid input. Please enter a number.\n\n");
continue;
}
sprintf(vad, "%f", inputVadLevel);
if((error = ts3client_setPreProcessorConfigValue(vadTestscHandlerID, "voiceactivation_level", vad)) != ERROR_ok) {
printf("unable to set vad value\n");
continue;
}
printf("new vad level: %s\n", vad);
continue;
}
case 'q':
destroyLocalTestMode(vadTestscHandlerID);
printf("\n**********************************\n");
printf("Left configure microphone mode\n\n");
vadTestscHandlerID = 0;
return;
}
}
#ifdef _WIN32 /* On Windows we print this once per second, on Unix only on each Return keyboard input due to lack of easy kbhit replacement on Unix */
if(++counter > 9)
#endif
{
printVadLevel();
counter = 0;
}
SLEEP(100);
}
}
void toggleRecordSound(uint64 serverConnectionHandlerID){
unsigned int error;
if (!recordSound){
recordSound = 1;
if((error = ts3client_startVoiceRecording(serverConnectionHandlerID)) != ERROR_ok){
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error notifying server of startVoiceRecording: %s\n", errormsg);
ts3client_freeMemory(errormsg);
return;
}
}
printf("Started recording sound to wav\n");
} else {
recordSound = 0;
if((error = ts3client_stopVoiceRecording(serverConnectionHandlerID)) != ERROR_ok){
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error notifying server of stopVoiceRecording: %s\n", errormsg);
ts3client_freeMemory(errormsg);
return;
}
}
printf("Stopped recording sound to wav\n");
}
}
unsigned int printMyConnectionInfo(uint64 serverConnectionHandlerID) {
anyID my_id;
unsigned int error = ts3client_getClientID(serverConnectionHandlerID, &my_id);
if (error != ERROR_ok) {
return error;
}
double pingResult;
if ((error = ts3client_getConnectionVariableAsDouble(serverConnectionHandlerID, my_id, CONNECTION_PING, &pingResult)) == ERROR_ok) {
double deviationResult;
if ((error = ts3client_getConnectionVariableAsDouble(serverConnectionHandlerID, my_id, CONNECTION_PING_DEVIATION, &deviationResult)) == ERROR_ok) {
printf("Ping: %f ms +/- %f\n", pingResult, deviationResult);
}
}
double loss;
if ((error = ts3client_getConnectionVariableAsDouble(serverConnectionHandlerID, my_id, CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL, &loss)) == ERROR_ok) {
printf("Server to client packet loss (total): %f\n", loss);
}
return error;
}
int readIdentity(char* identity) {
FILE *file;
if((file = fopen("identity.txt", "r")) == NULL) {
printf("Could not open file 'identity.txt' for reading.\n");
return -1;
}
fgets(identity, IDENTITY_BUFSIZE, file);
if(ferror(file) != 0) {
fclose (file);
printf("Error reading identity from file 'identity.txt'.\n");
return -1;
}
fclose (file);
return 0;
}
int writeIdentity(const char* identity) {
FILE *file;
if((file = fopen("identity.txt", "w")) == NULL) {
printf("Could not open file 'identity.txt' for writing.\n");
return -1;
}
fputs(identity, file);
if(ferror(file) != 0) {
fclose (file);
printf("Error writing identity to file 'identity.txt'.\n");
return -1;
}
fclose (file);
return 0;
}
void showHelp() {
printf("\n[q] - Disconnect from server\n[h] - Show this help\n[c] - Show channels\n[s] - Switch to specified channel\n");
printf("[l] - Show all visible clients\n[L] - Show all clients in specific channel\n[n] - Create new channel with generated name\n[N] - Create new channel with custom name\n");
printf("[d] - Delete channel\n[r] - Rename channel\n[R] - Record sound to wav\n[v] - Toggle Voice Activity Detection / Continuous transmission \n[M] - Set Voice Activity Detection Mode\n[V] - Set Voice Activity Detection level\n");
printf("[b] - Toggle Denoiser\n[B] - Set Denoiser Level\n[t] - Toggle Typing Suppression\n[e] - Toggle Echo Reduction\n[a] - Toggle Echo Cancellation\n[A] - Toggle AGC\n");
printf("[w] - Set whisper list\n[W] - Clear whisper list\n[m] - Configure microphone\n[3] - Set 3D position of client\n[i] - Connection info\n\n");
}
char* programPath(char* programInvocation){
char* path;
char* end;
int length;
char pathsep;
if (programInvocation == NULL) return strdup("");
#ifdef _WIN32
pathsep = '\\';
#else
pathsep = '/';
#endif
end = strrchr(programInvocation, pathsep);
if (!end) return strdup("");
length = (end - programInvocation) + 2;
path = (char*)malloc(length);
strncpy(path, programInvocation, length - 1);
path[length - 1] = '\0';
return path;
}
struct ConnectInfo {
char* ip;
unsigned short port;
};
int main(int argc, char* argv[]) {
uint64 scHandlerID;
unsigned int error;
char* mode;
char** device;
char *version;
char identity[IDENTITY_BUFSIZE];
short abort = 0;
char* path;
/* Check for commandline parameters <ip> <port> */
struct ConnectInfo connect_info;
if (argc == 3) {
size_t sz = strlen(argv[1]) + 1;
connect_info.ip = (char*)malloc(sz * sizeof(char));
strcpy(connect_info.ip, argv[1]);
connect_info.ip[sz - 1] = '\0';
connect_info.port = atoi(argv[2]);
} else {
/* No commandline parameters given, use default ip "localhost" and port 9987 */
const char* default_ip = "localhost";
size_t sz = strlen(default_ip) + 1;
connect_info.ip = (char*)malloc(sz * sizeof(char));
strcpy(connect_info.ip, default_ip);
connect_info.port = 9987;
}
/* Create struct for callback function pointers */
struct ClientUIFunctions funcs;
/* Initialize all callbacks with NULL */
memset(&funcs, 0, sizeof(struct ClientUIFunctions));
/* Callback function pointers */
/* It is sufficient to only assign those callback functions you are using. When adding more callbacks, add those function pointers here. */
funcs.onConnectStatusChangeEvent = onConnectStatusChangeEvent;
funcs.onNewChannelEvent = onNewChannelEvent;
funcs.onNewChannelCreatedEvent = onNewChannelCreatedEvent;
funcs.onDelChannelEvent = onDelChannelEvent;
funcs.onClientMoveEvent = onClientMoveEvent;
funcs.onClientMoveSubscriptionEvent = onClientMoveSubscriptionEvent;
funcs.onClientMoveTimeoutEvent = onClientMoveTimeoutEvent;
funcs.onTalkStatusChangeEvent = onTalkStatusChangeEvent;
funcs.onIgnoredWhisperEvent = onIgnoredWhisperEvent;
funcs.onServerErrorEvent = onServerErrorEvent;
funcs.onUserLoggingMessageEvent = onUserLoggingMessageEvent;
funcs.onCustomPacketEncryptEvent = onCustomPacketEncryptEvent;
funcs.onCustomPacketDecryptEvent = onCustomPacketDecryptEvent;
funcs.onEditMixedPlaybackVoiceDataEvent = onEditMixedPlaybackVoiceDataEvent;
#ifdef CUSTOM_PASSWORDS
funcs.onClientPasswordEncrypt = onClientPasswordEncrypt;
#endif
funcs.onCustom3dRolloffCalculationClientEvent = onCustom3dRolloffCalculationClientEvent;
/* Initialize client lib with callbacks */
/* Resource path points to the SDK\bin directory to locate the soundbackends folder when running from Visual Studio. */
/* If you want to run directly from the SDK\bin directory, use an empty string instead to locate the soundbackends folder in the current directory. */
path = programPath(argv[0]);
error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE | LogType_USERLOGGING, NULL, path);
free(path);
path = NULL;
if (error != ERROR_ok) {
char* errormsg;
if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) {
printf("Error initializing clientlib: %s\n", errormsg);
ts3client_freeMemory(errormsg);
}
return 1;
}
/* Spawn a new server connection handler using the default port and store the server ID */
if((error = ts3client_spawnNewServerConnectionHandler(0, &scHandlerID)) != ERROR_ok) {
printf("Error spawning server connection handler: %d\n", error);
return 1;
}
/* Get default capture mode */
if((error = ts3client_getDefaultCaptureMode(&mode)) != ERROR_ok) {
printf("Error getting default capture mode: %d\n", error);
return 1;
}
printf("Default capture mode: %s\n", mode);
/* Get default capture device */
if((error = ts3client_getDefaultCaptureDevice(mode, &device)) != ERROR_ok) {
printf("Error getting default capture device: %d\n", error);
return 1;
}
printf("Default capture device: %s %s\n", device[0], device[1]);
/* Open default capture device */
/* Instead of passing mode and device[1], it would also be possible to pass empty strings to open the default device */
if((error = ts3client_openCaptureDevice(scHandlerID, mode, device[1])) != ERROR_ok) {
printf("Error opening capture device: %d\n", error);
}
/* Adjust "vad" preprocessor value to use vad by default */
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "vad", "true")) != ERROR_ok) {
printf("Error toggling VAD: %d\n", error);
return 1;
}
/* Adjust "vad_mode" value to use hybrid by default */
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "vad_mode", "2")) != ERROR_ok) {
printf("Error setting vad_mode value to hybrid: %d\n", error);
return 1;
}
/* Adjust "voiceactivation_level" value */
if ((error = ts3client_setPreProcessorConfigValue(scHandlerID, "voiceactivation_level", "-20")) != ERROR_ok) {
printf("Error setting voiceactivation_level: %d\n", error);
return 1;
}
/* Get default playback mode */
if((error = ts3client_getDefaultPlayBackMode(&mode)) != ERROR_ok) {
printf("Error getting default playback mode: %d\n", error);
return 1;
}
printf("Default playback mode: %s\n", mode);
/* Get default playback device */
if((error = ts3client_getDefaultPlaybackDevice(mode, &device)) != ERROR_ok) {
printf("Error getting default playback device: %d\n", error);
return 1;
}
printf("Default playback device: %s %s\n", device[0], device[1]);
/* Open default playback device */
/* Instead of passing mode and device[1], it would also be possible to pass empty strings to open the default device */
if((error = ts3client_openPlaybackDevice(scHandlerID, mode, device[1])) != ERROR_ok) {
printf("Error opening playback device: %d\n", error);
}
/* Try reading identity from file, otherwise create new identity */
if(readIdentity(identity) != 0) {
char* id;
if((error = ts3client_createIdentity(&id)) != ERROR_ok) {
printf("Error creating identity: %d\n", error);
return 0;
}
if(strlen(id) >= IDENTITY_BUFSIZE) {
printf("Not enough bufsize for identity string\n");
return 0;
}
strcpy(identity, id);
ts3client_freeMemory(id);
writeIdentity(identity);
}
printf("Using identity: %s\n", identity);
printf("Connecting to %s:%d\n", connect_info.ip, connect_info.port);
/* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */
error = ts3client_startConnection(scHandlerID, identity, connect_info.ip, connect_info.port, "client", NULL, "", "secret");
free(connect_info.ip);
connect_info.ip = NULL;
if (error != ERROR_ok) {
printf("Error connecting to server: %d\n", error);
return 1;
}
printf("Client lib initialized and running\n");
/* Query and print client lib version */
if((error = ts3client_getClientLibVersion(&version)) != ERROR_ok) {
printf("Failed to get clientlib version: %d\n", error);
return 1;
}
printf("Client lib version: %s\n", version);
ts3client_freeMemory(version); /* Release dynamically allocated memory */
version = NULL;
SLEEP(300);
/* Simple commandline interface */
printf("\nTeamSpeak 3 client commandline interface\n");
showHelp();
while(!abort) {
int c = getc(stdin);
switch(c) {
case 'q':
printf("\nDisconnecting from server...\n");
abort = 1;
break;
case 'h':
showHelp();
break;
case 'c':
showChannels(DEFAULT_VIRTUAL_SERVER);
break;
case 'l':
showClients(DEFAULT_VIRTUAL_SERVER);
break;
case 'L':
{
uint64 channelID = enterChannelID();
if(channelID > 0)
showChannelClients(DEFAULT_VIRTUAL_SERVER, channelID);
break;
}
case 'n':
{
char name[NAME_BUFSIZE];
createDefaultChannelName(name);
createChannel(DEFAULT_VIRTUAL_SERVER, name, NULL);
break;
}
case 'N':
{
char name[NAME_BUFSIZE];
char password[CHANNEL_PASSWORD_BUFSIZE];
emptyInputBuffer();
enterName(name);
enterPassword(password);
createChannel(DEFAULT_VIRTUAL_SERVER, name, password);
break;
}
case 'd':
deleteChannel(DEFAULT_VIRTUAL_SERVER);
break;
case 'r':
renameChannel(DEFAULT_VIRTUAL_SERVER);
break;
case 'R':
toggleRecordSound(DEFAULT_VIRTUAL_SERVER);
break;
case 's':
switchChannel(DEFAULT_VIRTUAL_SERVER);
break;
case 'v':
toggleVAD(DEFAULT_VIRTUAL_SERVER);
break;
case 'V':
setVadLevel(DEFAULT_VIRTUAL_SERVER);
break;
case 'M':
setVadMode(DEFAULT_VIRTUAL_SERVER);
break;
case 'b':
toggleDenoise(DEFAULT_VIRTUAL_SERVER);
break;
case 'B':
setDenoiserLevel(DEFAULT_VIRTUAL_SERVER);
break;
case 't':
toggleTypingSuppression(DEFAULT_VIRTUAL_SERVER);
break;
case 'e':
toggleEchoReductionDucking(DEFAULT_VIRTUAL_SERVER);
break;
case 'a':
toggleAec(DEFAULT_VIRTUAL_SERVER);
break;
case 'A':
toggleAgc(DEFAULT_VIRTUAL_SERVER);
break;
case 'w':
requestWhisperList(DEFAULT_VIRTUAL_SERVER);
break;
case 'W':
requestClearWhisperList(DEFAULT_VIRTUAL_SERVER);
break;
case 'm':
configureMicrophone();
showHelp(); /* Display main menu after leaving configure microphone mode */
break;
case '3':
setClient3DPosition(DEFAULT_VIRTUAL_SERVER);
break;
case 'i':
printMyConnectionInfo(scHandlerID);
break;
}
SLEEP(50);
}
/* Disconnect from server. ERROR_not_connected just means the peer is already
gone (e.g. the server was shut down) - nothing to do, so move on. */
if((error = ts3client_stopConnection(scHandlerID, "leaving")) != ERROR_ok && error != ERROR_not_connected)
printf("Error stopping connection: %d\n", error);
SLEEP(200);
/* Destroy server connection handler */
if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok)
printf("Error destroying server connection handler: %d\n", error);
/* Shutdown client lib */
if((error = ts3client_destroyClientLib()) != ERROR_ok)
printf("Failed to destroy clientlib: %d\n", error);
/* This is a small hack, to close an open recording sound file */
recordSound = 0;
onEditMixedPlaybackVoiceDataEvent(DEFAULT_VIRTUAL_SERVER, NULL, 0, 0, NULL, NULL);
return 0;
}