/* * TeamSpeak SDK client minimal sample with custom * capture and record * * Copyright (c) TeamSpeak-Systems */ /* This example connects to a server and plays a wave file, while recording incomming sound to output.wav */ #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable : 4996) #include #else #include #include #include #endif #include #include #include #include #ifdef _WIN32 #define SLEEP(x) Sleep(x) #else #define SLEEP(x) usleep(x*1000) #endif #include "wave.h" /*The client lib works at 48Khz internally. It is therefore advisable to use the same for your project */ #define PLAYBACK_FREQUENCY 48000 #define PLAYBACK_CHANNELS 2 #define AUDIO_PROCESS_SECONDS 500 /* * 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 %u\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; /* Query client nickname from ID */ if(ts3client_getClientVariableAsString(serverConnectionHandlerID, clientID, CLIENT_NICKNAME, &name) != ERROR_ok) 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 */ } void onServerErrorEvent(uint64 serverConnectionHandlerID, const char* errorMessage, unsigned int error, const char* returnCode, const char* extraMessage) { printf("Error for server %llu: %s %s\n", (unsigned long long)serverConnectionHandlerID, errorMessage, extraMessage); } 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; } int main(int argc, char** argv) { uint64 scHandlerID; unsigned int error; char *version; char *identity; int captureFrequency; int captureChannels; short* captureBuffer; int captureBufferSamples; int audioPeriodCounter; int captureAudioOffset; int capturePeriodSize; short* playbackBuffer; int playbackAudioOffset; int playbackPeriodSize; char* path; /* Create struct for callback function pointers */ struct ClientUIFunctions funcs; /* Initialize all callbacks with NULL */ memset(&funcs, 0, sizeof(struct ClientUIFunctions)); /* Now assign the used callback function pointers */ 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.onServerErrorEvent = onServerErrorEvent; /* Read in the wave we are going to stream to the server */ if (!readWave("welcome_to_teamspeak.wav", &captureFrequency, &captureChannels, &captureBuffer, &captureBufferSamples)) return 1; /* allocate AUDIO_PROCESS_SECONDS seconds worth of PLAYBACK_FREQUENCY 16bit PLAYBACK_CHANNELS channels */ playbackBuffer = (short*) malloc(AUDIO_PROCESS_SECONDS * PLAYBACK_FREQUENCY * sizeof(short) * PLAYBACK_CHANNELS); if (!playbackBuffer){ printf("error: could not allocate memory for output wave\n"); return 1; } /* Initialize client lib with callbacks */ path = programPath(argv[0]); if((error = ts3client_initClientLib(&funcs, NULL, LogType_FILE | LogType_CONSOLE, NULL, path)) != ERROR_ok) { char* errormsg; if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) { printf("Error initialzing serverlib: %s\n", errormsg); ts3client_freeMemory(errormsg); } return 1; } /* register a new custom sound device, that captures at read wave freq+channels and plays PLAYBACK_CHANNELS channels at PLAYBACK_FREQUENCY */ if ((error = ts3client_registerCustomDevice("customWaveDeviceId", "Nice displayable wave device name", captureFrequency, captureChannels, PLAYBACK_FREQUENCY, PLAYBACK_CHANNELS)) != ERROR_ok) { char* errormsg; if(ts3client_getErrorMessage(error, &errormsg) == ERROR_ok) { printf("Error registering custom sound device: %s\n", errormsg); ts3client_freeMemory(errormsg); } } /* 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; } /* Open capture device we created earlier */ if((error = ts3client_openCaptureDevice(scHandlerID, "custom", "customWaveDeviceId")) != ERROR_ok) { printf("Error opening capture device: %d\n", error); } /* Open playback device we created earlier */ if((error = ts3client_openPlaybackDevice(scHandlerID, "custom", "customWaveDeviceId")) != ERROR_ok) { printf("Error opening playback device: %d\n", error); } /* Create a new client identity */ /* In your real application you should do this only once, store the assigned identity locally and then reuse it. */ if((error = ts3client_createIdentity(&identity)) != ERROR_ok) { printf("Error creating identity: %d\n", error); return 1; } /* Connect to server on localhost:9987 with nickname "client", no default channel, no default channel password and server password "secret" */ if((error = ts3client_startConnection(scHandlerID, identity, "localhost", 9987, "client", NULL, "", "secret")) != ERROR_ok) { printf("Error connecting to server: %d\n", error); return 1; } ts3client_freeMemory(identity); /* Release dynamically allocated memory */ identity = NULL; 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(500); printf("\n--- processing audio for %d seconds ---\n", AUDIO_PROCESS_SECONDS); /*the clientlib works with 20ms packets internaly. So the best is to feed is 20ms worth of sound at a time */ capturePeriodSize = (captureFrequency*20)/1000; playbackPeriodSize = (PLAYBACK_FREQUENCY*20)/1000; captureAudioOffset = 0; playbackAudioOffset = 0; for(audioPeriodCounter = 0; audioPeriodCounter < 50*AUDIO_PROCESS_SECONDS; ++audioPeriodCounter){ /*50*20=1000*/ /* wait 20 ms */ SLEEP(20); /* make sure we dont stream past the end of our wave sample */ if (captureAudioOffset + capturePeriodSize > captureBufferSamples) captureAudioOffset = 0; /* stream capture data to the client lib */ if((error = ts3client_processCustomCaptureData("customWaveDeviceId", captureBuffer + captureAudioOffset*captureChannels, capturePeriodSize)) != ERROR_ok){ printf("Failed to get stream capture data: %d\n", error); return 1; } /* get playback data from the client lib */ if((error = ts3client_acquireCustomPlaybackData("customWaveDeviceId", playbackBuffer + playbackAudioOffset*PLAYBACK_CHANNELS, playbackPeriodSize))!= ERROR_ok){ if(error != ERROR_sound_no_data) { //this error signals us to play silence printf("Failed to get acquire playback data: %d\n", error); return 1; } memset(playbackBuffer + playbackAudioOffset * PLAYBACK_CHANNELS, 0, playbackPeriodSize * PLAYBACK_CHANNELS * sizeof(short)); } /*update buffer offsets */ captureAudioOffset += capturePeriodSize; playbackAudioOffset += playbackPeriodSize; } /* 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); /* Destroy server connection handler */ if((error = ts3client_destroyServerConnectionHandler(scHandlerID)) != ERROR_ok) printf("Error destroying ServerConnectionHandler: %d\n", error); /* unregister the custom sound device */ if ((error = ts3client_unregisterCustomDevice("customWaveDeviceId")) != ERROR_ok) printf("Error unregisterring custom sound device: %d\n", error); /* Shutdown client lib */ if((error = ts3client_destroyClientLib()) != ERROR_ok) printf("Failed to destroy clientlib: %d\n", error); /* save the playback data */ writeWave("output.wav", PLAYBACK_FREQUENCY, PLAYBACK_CHANNELS, playbackBuffer, PLAYBACK_FREQUENCY*AUDIO_PROCESS_SECONDS); /* release allocated memory */ free(captureBuffer); free(playbackBuffer); return 0; }