diff --git a/build/Makefile.am b/build/Makefile.am index 83437e6..e8e8848 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -44,9 +44,13 @@ toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \ # For audio support if BUILD_AV + toxic_SOURCES += $(top_srcdir)/src/audio_call.c \ $(top_srcdir)/src/audio_call.h -toxic_CFLAGS += $(LIBTOXAV_CFLAGS) -toxic_LDADD += $(LIBTOXAV_LIBS) +toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \ + $(OPENAL_CFLAGS) + +toxic_LDADD += $(LIBTOXAV_LIBS) \ + $(OPENAL_LIBS) endif \ No newline at end of file diff --git a/configure.ac b/configure.ac index 28430e3..a58f474 100644 --- a/configure.ac +++ b/configure.ac @@ -419,23 +419,29 @@ AC_ARG_ENABLE([av], # Check for A/V library if test "x$BUILD_AV" = "xyes"; then - export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig + PKG_CHECK_MODULES([OPENAL], [openal], + [ + export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig - PKG_CHECK_EXISTS([libtoxav], - [ - AC_CHECK_HEADER([tox/toxav.h], + PKG_CHECK_MODULES([LIBTOXAV], [libtoxav], [ - # Place define for audio support - AC_DEFINE([_SUPPORT_AUDIO], [], [If audio supported]) - AC_MSG_NOTICE([Building with audio support]) - ], + AC_CHECK_HEADER([tox/toxav.h], + [ + # Place define for audio support + AC_DEFINE([_SUPPORT_AUDIO], [], [If audio supported]) + AC_MSG_NOTICE([Building with audio support]) + ], + [ + AC_MSG_NOTICE([No A/V headers; disabling A/V support]) + BUILD_AV="no" + ],) + ], [ - AC_MSG_NOTICE([No A/V headers; disabling A/V support]) - BUILD_AV="no" - ],) - ], + AC_MSG_NOTICE([No A/V library; disabling A/V support]) + ]) + ], [ - AC_MSG_NOTICE([No A/V library; disabling A/V support]) + AC_MSG_NOTICE([No openal library; disabling A/V support]) ]) fi @@ -443,6 +449,8 @@ AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes") +#debug mode +CFLAGS="-ggdb3 -O0 -pg" TOXIC_VERSION="$PACKAGE_VERSION" AC_PATH_PROG([GIT], [git], [no]) diff --git a/misc/DHTservers b/misc/DHTservers index 5e27942..d82237c 100644 --- a/misc/DHTservers +++ b/misc/DHTservers @@ -1,2 +1 @@ -192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B -2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B +127.0.0.1 33447 25530DE8BF0DACA3F2ECD1A2FF07C6E21E5BEE12993F36157A2846DF8E837E33 diff --git a/src/audio_call.c b/src/audio_call.c index 457d371..e14a343 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -7,8 +7,491 @@ #endif #ifdef _SUPPORT_AUDIO -#include -#error "Get me a kill" + +#include "audio_call.h" +#include "toxic_windows.h" +#include "chat_commands.h" +#include "toxic_windows.h" +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DEVICES 32 +#define _cbend pthread_exit(NULL) + +typedef struct _DeviceIx { + + ALCdevice* dhndl; /* Handle of device selected/opened */ + ALCcontext* ctx; /* Device context */ + const char* devices[MAX_DEVICES]; /* Container */ + int size; + int dix; /* Index of default device */ +} DeviceIx; + +enum _devices +{ + input, + output, +}; + +struct _ASettings { + + DeviceIx device[2]; + + AudioError errors; + + ToxAv* av; + + pthread_t ttid; /* Transmission thread id */ + int ttas; /* Transmission thread active status (0 - stopped, 1- running) */ +} ASettins; + + +void *callback_recv_invite ( void *arg ); +void *callback_recv_ringing ( void *arg ); +void *callback_recv_starting ( void *arg ); +void *callback_recv_ending ( void *arg ); +void *callback_recv_error ( void *arg ); +void *callback_call_started ( void *arg ); +void *callback_call_canceled ( void *arg ); +void *callback_call_rejected ( void *arg ); +void *callback_call_ended ( void *arg ); +void *callback_requ_timeout ( void *arg ); + + + + + +ToxAv* init_audio(ToxWindow* window, Tox* tox) +{ + ASettins.errors = NoError; + ASettins.ttas = 0; /* Not running */ + + /* Capture device */ + const char* stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); + ASettins.device[input].size = 0; + + if ( stringed_device_list ) { + const char* default_device = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); + + for ( ; *stringed_device_list; ++ASettins.device[input].size ) { + ASettins.device[input].devices[ASettins.device[input].size] = stringed_device_list; + + if ( strcmp( default_device , ASettins.device[input].devices[ASettins.device[input].size] ) == 0 ) + ASettins.device[input].dix = ASettins.device[input].size; + + stringed_device_list += strlen( stringed_device_list ) + 1; + } + + ++ASettins.device[input].size; + } + + if ( ASettins.device[input].size ) { /* Have device */ + ASettins.device[input].dhndl = alcCaptureOpenDevice( + ASettins.device[input].devices[ASettins.device[input].dix], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); + + if (alcGetError(ASettins.device[input].dhndl) != AL_NO_ERROR) { + ASettins.errors |= ErrorStartingCaptureDevice; + } + + wprintw(window->window, "Input device: %s\n", ASettins.device[input].devices[ASettins.device[input].dix]); + } else { /* No device */ + wprintw(window->window, "No input device!\n"); + ASettins.device[input].dhndl = NULL; + } + + ASettins.device[input].ctx = NULL; + + + + + /* Output device */ + stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + ASettins.device[output].size = 0; + + if ( stringed_device_list ) { + const char* default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + + for ( ; *stringed_device_list; ++ASettins.device[output].size ) { + ASettins.device[output].devices[ASettins.device[output].size] = stringed_device_list; + + if ( strcmp( default_device , ASettins.device[output].devices[ASettins.device[output].size] ) == 0 ) + ASettins.device[output].dix = ASettins.device[output].size; + + stringed_device_list += strlen( stringed_device_list ) + 1; + } + + ++ASettins.device[output].size; + } + + if ( ASettins.device[output].size ) { /* Have device */ + ASettins.device[output].dhndl = alcOpenDevice(ASettins.device[output].devices[ASettins.device[output].dix]); + + if (alcGetError(ASettins.device[output].dhndl) != AL_NO_ERROR) { + ASettins.errors |= ErrorStartingOutputDevice; + } + + wprintw(window->window, "Output device: %s\n", ASettins.device[output].devices[ASettins.device[output].dix]); + ASettins.device[output].ctx = alcCreateContext(ASettins.device[output].dhndl, NULL); + } else { /* No device */ + wprintw(window->window, "No output device!\n"); + ASettins.device[output].dhndl = NULL; + ASettins.device[output].ctx = NULL; + } + + + /* Streaming stuff from core */ + ASettins.av = toxav_new(tox, window, 0, 0); + + if ( !ASettins.av ) { + ASettins.errors |= ErrorStartingCoreAudio; + return NULL; + } + + toxav_register_callstate_callback(callback_call_started, av_OnStart); + toxav_register_callstate_callback(callback_call_canceled, av_OnCancel); + toxav_register_callstate_callback(callback_call_rejected, av_OnReject); + toxav_register_callstate_callback(callback_call_ended, av_OnEnd); + toxav_register_callstate_callback(callback_recv_invite, av_OnInvite); + + toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging); + toxav_register_callstate_callback(callback_recv_starting, av_OnStarting); + toxav_register_callstate_callback(callback_recv_ending, av_OnEnding); + + toxav_register_callstate_callback(callback_recv_error, av_OnError); + toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout); + + return ASettins.av; +} + +void terminate_audio() +{ + if ( ASettins.device[input].dhndl ) { + alcCaptureCloseDevice(ASettins.device[input].dhndl); + } + + if ( ASettins.device[output].dhndl ) { + alcCloseDevice(ASettins.device[output].dhndl); + alcMakeContextCurrent(NULL); + alcDestroyContext(ASettins.device[output].ctx); + } + + toxav_kill(ASettins.av); +} + + +int errors() +{ + return ASettins.errors; +} + + + +/* + * Transmission + */ + +void* transmission(void* arg) +{ + (void)arg; /* Avoid warning */ + + ASettins.ttas = 1; + + /* Prepare devices */ + alcCaptureStart(ASettins.device[input].dhndl); + alcMakeContextCurrent(ASettins.device[output].ctx); + + int32_t dec_frame_len; + int16_t frame[4096]; + int32_t sample = 0; + uint32_t buffer; + int32_t ready; + int32_t openal_buffers = 5; + uint32_t source, *buffers; + + /* Prepare buffers */ + buffers = calloc(sizeof(uint32_t), openal_buffers); + alGenBuffers(openal_buffers, buffers); + alGenSources((uint32_t)1, &source); + alSourcei(source, AL_LOOPING, AL_FALSE); + + + uint16_t zeros[AUDIO_FRAME_SIZE]; + memset(zeros, 0, AUDIO_FRAME_SIZE); + int16_t PCM[AUDIO_FRAME_SIZE]; + + int32_t i = 0; + for (; i < openal_buffers; ++i) { + alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, AUDIO_FRAME_SIZE, 48000); + } + + alSourceQueueBuffers(source, openal_buffers, buffers); + alSourcePlay(source); + + if (alGetError() != AL_NO_ERROR) { + /* Print something? */ + /*fprintf(stderr, "Error starting audio\n");*/ + goto cleanup; + } + + /* Start transmission */ + while (ASettins.ttas) { + + alcGetIntegerv(ASettins.device[input].dhndl, ALC_CAPTURE_SAMPLES, (int32_t) sizeof(int32_t), &sample); + + /* RECORD AND SEND */ + if (sample >= AUDIO_FRAME_SIZE) { + alcCaptureSamples(ASettins.device[input].dhndl, frame, AUDIO_FRAME_SIZE); + + if (toxav_send_audio(ASettins.av, frame, AUDIO_FRAME_SIZE) < 0) + /*fprintf(stderr, "Could not encode or send audio packet\n")*/; + + } else usleep(1000); + + + + /* PLAYBACK */ + + alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready); + + if (ready <= 0) + continue; + + dec_frame_len = toxav_recv_audio(ASettins.av, AUDIO_FRAME_SIZE, PCM); + + /* Play the packet */ + if (dec_frame_len > 0) { + alSourceUnqueueBuffers(source, 1, &buffer); + alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000); + int32_t error = alGetError(); + + if (error != AL_NO_ERROR) { + /*fprintf(stderr, "Error setting buffer %d\n", error);*/ + break; + } + + alSourceQueueBuffers(source, 1, &buffer); + + if (alGetError() != AL_NO_ERROR) { + /*fprintf(stderr, "Error: could not buffer audio\n");*/ + break; + } + + alGetSourcei(source, AL_SOURCE_STATE, &ready); + + if (ready != AL_PLAYING) alSourcePlay(source); + } + + usleep(1000); + } + +cleanup: + + alDeleteSources(1, &source); + alDeleteBuffers(openal_buffers, buffers); + alcMakeContextCurrent(NULL); + + _cbend; +} + +int start_transmission() +{ + if ( !toxav_audio_decoding(ASettins.av) || + !toxav_audio_encoding(ASettins.av) ) + return -1; + + /* Don't provide support for video */ + toxav_prepare_transmission(ASettins.av, 0); + + if ( 0 != pthread_create(&ASettins.ttid, NULL, transmission, NULL ) && + 0 != pthread_detach(ASettins.ttid) ) { + return -1; + } +} + +int stop_transmission() +{ + ASettins.ttas = 0; +} +/* + * End of transmission + */ + + + + + +/* + * Callbacks + */ + +#define CB_BODY(Arg, onFunc) do { ToxWindow* windows = toxav_get_agent_handler(Arg); int i;\ +for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], Arg); } while (0) + +void *callback_recv_invite ( void* arg ) +{ + CB_BODY(arg, onInvite); + _cbend; +} +void *callback_recv_ringing ( void* arg ) +{ + CB_BODY(arg, onRinging); + _cbend; +} +void *callback_recv_starting ( void* arg ) +{ + CB_BODY(arg, onStarting); + _cbend; +} +void *callback_recv_ending ( void* arg ) +{ + CB_BODY(arg, onEnding); + stop_transmission(); +} +void *callback_recv_error ( void* arg ) +{ + CB_BODY(arg, onError); + _cbend; +} +void *callback_call_started ( void* arg ) +{ + CB_BODY(arg, onStart); + _cbend; +} +void *callback_call_canceled ( void* arg ) +{ + CB_BODY(arg, onCancel); + _cbend; +} +void *callback_call_rejected ( void* arg ) +{ + CB_BODY(arg, onReject); + _cbend; +} +void *callback_call_ended ( void* arg ) +{ + CB_BODY(arg, onEnd); + stop_transmission(); + _cbend; +} + +void *callback_requ_timeout ( void* arg ) +{ + CB_BODY(arg, onTimeout); + _cbend; +} + +/* + * End of Callbacks + */ + + +/* + * Commands from chat_commands.h + */ +void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + const char* error_str; + + if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } + + if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; } + + ToxAvError error = toxav_call(ASettins.av, self->num, TypeAudio, 30); + + if ( error != ErrorNone ) { + if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!"; + else error_str = "Internal error!"; + + goto on_error; + } + + wprintw(window, "Calling...\n"); + + return; +on_error: + wprintw(window, "%s %d\n", error_str, argc); +} + +void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + const char* error_str; + + if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } + + if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; } + + ToxAvError error = toxav_answer(ASettins.av, TypeAudio); + + if ( error != ErrorNone ) { + if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!"; + else if ( error == ErrorNoCall ) error_str = "No incomming call!"; + else error_str = "Internal error!"; + + goto on_error; + } + + /* Callback will print status... */ + + return; +on_error: + wprintw(window, "%s\n", error_str); +} + +void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + const char* error_str; + + if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } + + if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; } + + ToxAvError error = toxav_hangup(ASettins.av); + + if ( error != ErrorNone ) { + if ( error == ErrorInvalidState ) error_str = "Cannot hangup in invalid state!"; + else if ( error == ErrorNoCall ) error_str = "No call!"; + else error_str = "Internal error!"; + + goto on_error; + } + + return; +on_error: + wprintw(window, "%s\n", error_str); +} + +void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + const char* error_str; + + if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; } + + if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; } + + ToxAvError error = toxav_hangup(ASettins.av); + + if ( error != ErrorNone ) { + if ( error == ErrorNoCall ) error_str = "No call!"; + else error_str = "Internal error!"; + + goto on_error; + } + + /* Callback will print status... */ + + return; +on_error: + wprintw(window, "%s\n", error_str); +} + + + + #endif /* _SUPPORT_AUDIO */ \ No newline at end of file diff --git a/src/audio_call.h b/src/audio_call.h index 6ace39d..3fb38b6 100644 --- a/src/audio_call.h +++ b/src/audio_call.h @@ -5,4 +5,30 @@ #ifndef _audio_h #define _audio_h +#ifdef _SUPPORT_AUDIO + + +#include + +typedef struct ToxWindow ToxWindow; + +typedef enum _AudioError +{ + NoError = 0, + ErrorStartingCaptureDevice = 1 << 0, + ErrorStartingOutputDevice = 1 << 1, + ErrorStartingCoreAudio = 1 << 2 +} AudioError; + +/* You will have to pass pointer to first member of 'windows' + * declared in windows.c otherwise undefined behaviour will + */ +ToxAv* init_audio(ToxWindow* window, Tox* tox); +void terminate_audio(); + +int errors(); + +int start_transmission(); + +#endif /* _SUPPORT_AUDIO */ #endif /* _audio_h */ \ No newline at end of file diff --git a/src/chat.c b/src/chat.c index d89bfdd..492ada1 100644 --- a/src/chat.c +++ b/src/chat.c @@ -16,13 +16,17 @@ #include "friendlist.h" #include "toxic_strings.h" +#ifdef _SUPPORT_AUDIO + #include "audio_call.h" +#endif /* _SUPPORT_AUDIO */ + extern char *DATA_FILE; extern int store_data(Tox *m, char *path); extern FileSender file_senders[MAX_FILES]; extern ToxicFriend friends[MAX_FRIENDS_NUM]; -#define AC_NUM_CHAT_COMMANDS 17 +#define AC_NUM_CHAT_COMMANDS 21 /* Array of chat command names used for tab completion. */ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { @@ -43,6 +47,15 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = { { "/savefile" }, { "/sendfile" }, { "/status" }, + +#ifdef _SUPPORT_AUDIO + + { "/call" }, + { "/cancel" }, + { "/answer" }, + { "/hangup" }, + +#endif /* _SUPPORT_AUDIO */ }; static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len) @@ -259,6 +272,123 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_ alert_window(self, WINDOW_ALERT_2, true); } +/* Av Stuff */ +#ifdef _SUPPORT_AUDIO + +void chat_onInvite (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "Incomming audio call!\n" + "Answer: \"/answer\" \"/cancel\"\n"); + + alert_window(self, WINDOW_ALERT_0, true); +} +void chat_onRinging (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "Ringing...\n" + "\"/cancel\" ?\n"); +} +void chat_onStarting (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + if ( 0 != start_transmission() ) {/* YEAH! */ + wprintw(ctx->history, "Error starting transmission!\n"); + return; + } + + wprintw(ctx->history, "Call started! \n" + "Type: \"/hangup\" to end it.\n"); + +} +void chat_onEnding (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + toxav_kill_transmission(av); + + wprintw(ctx->history, "Call ended! \n"); +} +void chat_onError (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "Error! \n"); +} +void chat_onStart (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + if ( 0 != start_transmission() ) {/* YEAH! */ + wprintw(ctx->history, "Error starting transmission!\n"); + return; + } + + wprintw(ctx->history, "Call started! \n" + "Type: \"/hangup\" to end it.\n"); +} +void chat_onCancel (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "Call canceled! \n"); +} +void chat_onReject (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "Rejected! \n"); +} +void chat_onEnd (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + toxav_kill_transmission(av); + + wprintw(ctx->history, "Call ended! \n"); +} +void chat_onTimeout (ToxWindow *self, ToxAv *av) +{ + if (self->num != toxav_get_peer_id(av, 0)) + return; + + ChatContext *ctx = self->chatwin; + + wprintw(ctx->history, "No answer! \n"); +} + +#endif /* _SUPPORT_AUDIO */ + static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) { if (action == NULL) { wprintw(ctx->history, "Invalid syntax.\n"); @@ -619,6 +749,19 @@ ToxWindow new_chat(Tox *m, int friendnum) ret.onFileSendRequest = &chat_onFileSendRequest; ret.onFileControl = &chat_onFileControl; ret.onFileData = &chat_onFileData; + +#ifdef _SUPPORT_AUDIO + ret.onInvite = &chat_onInvite; + ret.onRinging = &chat_onRinging; + ret.onStarting = &chat_onStarting; + ret.onEnding = &chat_onEnding; + ret.onError = &chat_onError; + ret.onStart = &chat_onStart; + ret.onCancel = &chat_onCancel; + ret.onReject = &chat_onReject; + ret.onEnd = &chat_onEnd; + ret.onTimeout = &chat_onTimeout; +#endif /* _SUPPORT_AUDIO */ uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'}; uint16_t len = tox_get_name(m, friendnum, name); diff --git a/src/chat_commands.c b/src/chat_commands.c index 8a9505a..600c07a 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -38,6 +38,16 @@ void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg wprintw(window, " /sendfile : Send a file\n"); wprintw(window, " /savefile : Receive a file\n"); wprintw(window, " /quit or /exit : Exit Toxic\n"); + +#ifdef _SUPPORT_AUDIO + + wprintw(window, " /call : Audio call\n"); + wprintw(window, " /cancel : Cancel call\n"); + wprintw(window, " /answer : Answer incomming call\n"); + wprintw(window, " /hangup : Hangup active call\n"); + +#endif /* _SUPPORT_AUDIO */ + wprintw(window, " /help : Print this message again\n"); wattron(window, COLOR_PAIR(CYAN) | A_BOLD); diff --git a/src/chat_commands.h b/src/chat_commands.h index fd8059a..d52357b 100644 --- a/src/chat_commands.h +++ b/src/chat_commands.h @@ -7,3 +7,11 @@ void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_ST void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); + + +#ifdef _SUPPORT_AUDIO +void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); +#endif /* _SUPPORT_AUDIO */ \ No newline at end of file diff --git a/src/execute.c b/src/execute.c index beac6bb..104af9a 100644 --- a/src/execute.c +++ b/src/execute.c @@ -37,6 +37,13 @@ static struct cmd_func chat_commands[] = { { "/join", cmd_join_group }, { "/savefile", cmd_savefile }, { "/sendfile", cmd_sendfile }, + +#ifdef _SUPPORT_AUDIO + { "/call", cmd_call }, + { "/cancel", cmd_cancel }, + { "/answer", cmd_answer }, + { "/hangup", cmd_hangup }, +#endif /* _SUPPORT_AUDIO */ }; /* Parses input command and puts args into arg array. diff --git a/src/execute.h b/src/execute.h index c720a33..9586410 100644 --- a/src/execute.h +++ b/src/execute.h @@ -4,7 +4,12 @@ #define MAX_NUM_ARGS 4 /* Includes command */ #define GLOBAL_NUM_COMMANDS 13 + +#ifdef _SUPPORT_AUDIO +#define CHAT_NUM_COMMANDS 9 +#else #define CHAT_NUM_COMMANDS 5 +#endif /* _SUPPORT_AUDIO */ enum { GLOBAL_COMMAND_MODE, diff --git a/src/friendlist.c b/src/friendlist.c index 7e22091..c6273b5 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -15,6 +15,7 @@ #include "chat.h" #include "friendlist.h" #include "misc_tools.h" +#include "audio_call.h" extern char *DATA_FILE; extern ToxWindow *prompt; @@ -395,6 +396,36 @@ static void friendlist_onInit(ToxWindow *self, Tox *m) } +#ifdef _SUPPORT_AUDIO +static void friendlist_onAv(ToxWindow *self, ToxAv *av) +{ + int id = toxav_get_peer_id(av, 0); + + if ( id >= max_friends_index) + return; + + Tox* m = toxav_get_tox(av); + + if (friends[id].chatwin == -1) { + if (num_active_windows() < MAX_WINDOWS_NUM) { + friends[id].chatwin = add_window(m, new_chat(m, friends[id].num)); + } else { + uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'}; + tox_get_name(m, id, nick); + nick[TOXIC_MAX_NAME_LENGTH] = '\0'; + wprintw(prompt->window, "Audio action from: %s!\n", nick); + + prep_prompt_win(); + wattron(prompt->window, COLOR_PAIR(RED)); + wprintw(prompt->window, "* Warning: Too many windows are open.\n"); + wattron(prompt->window, COLOR_PAIR(RED)); + + alert_window(prompt, WINDOW_ALERT_0, true); + } + } +} +#endif /* _SUPPORT_AUDIO */ + ToxWindow new_friendlist(void) { ToxWindow ret; @@ -414,6 +445,19 @@ ToxWindow new_friendlist(void) ret.onStatusMessageChange = &friendlist_onStatusMessageChange; ret.onFileSendRequest = &friendlist_onFileSendRequest; ret.onGroupInvite = &friendlist_onGroupInvite; + +#ifdef _SUPPORT_AUDIO + ret.onInvite = &friendlist_onAv; + ret.onRinging = &friendlist_onAv; + ret.onStarting = &friendlist_onAv; + ret.onEnding = &friendlist_onAv; + ret.onError = &friendlist_onAv; + ret.onStart = &friendlist_onAv; + ret.onCancel = &friendlist_onAv; + ret.onReject = &friendlist_onAv; + ret.onEnd = &friendlist_onAv; + ret.onTimeout = &friendlist_onAv; +#endif /* _SUPPORT_AUDIO */ strcpy(ret.name, "friends"); return ret; diff --git a/src/main.c b/src/main.c index 3b9bcc1..cefe2ed 100644 --- a/src/main.c +++ b/src/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #endif #include @@ -40,6 +41,10 @@ #include "prompt.h" #include "misc_tools.h" +#ifdef _SUPPORT_AUDIO + #include "audio_call.h" +#endif /* _SUPPORT_AUDIO */ + #ifndef PACKAGE_DATADIR #define PACKAGE_DATADIR "." #endif @@ -536,6 +541,22 @@ int main(int argc, char *argv[]) prompt = init_windows(m); +#ifdef _SUPPORT_AUDIO + + attron(COLOR_PAIR(RED) | A_BOLD); + wprintw(prompt->window, "Starting audio...\n"); + attroff(COLOR_PAIR(RED) | A_BOLD); + + ToxAv* av = init_audio(prompt, m); + + if ( errors() == NoError ) + wprintw(prompt->window, "Audio started with no problems.\n"); + else /* Get error code and stuff */ + wprintw(prompt->window, "Error starting audio!\n"); + + +#endif /* _SUPPORT_AUDIO */ + if (f_loadfromfile) load_data(m, DATA_FILE); @@ -556,8 +577,11 @@ int main(int argc, char *argv[]) prompt_init_statusbar(prompt, m); sort_friendlist_index(m); - while (true) - do_toxic(m, prompt); - + while (true) do_toxic(m, prompt); + +#ifdef _SUPPORT_AUDIO + terminate_audio(prompt, av); +#endif /* _SUPPORT_AUDIO */ + return 0; } diff --git a/src/toxic_windows.h b/src/toxic_windows.h index 774b790..58fac0f 100644 --- a/src/toxic_windows.h +++ b/src/toxic_windows.h @@ -15,7 +15,11 @@ #include -#define UNKNOWN_NAME "Unknown" +#ifdef _SUPPORT_AUDIO + #include +#endif /* _SUPPORT_AUDIO */ + +#define UNKNOWN_NAME "John Doe" #define MAX_WINDOWS_NUM 32 #define MAX_FRIENDS_NUM 100 @@ -85,6 +89,21 @@ struct ToxWindow { void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t); void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t); +#ifdef _SUPPORT_AUDIO + + void(*onInvite)(ToxWindow *, ToxAv *); + void(*onRinging)(ToxWindow *, ToxAv *); + void(*onStarting)(ToxWindow *, ToxAv *); + void(*onEnding)(ToxWindow *, ToxAv *); + void(*onError)(ToxWindow *, ToxAv *); + void(*onStart)(ToxWindow *, ToxAv *); + void(*onCancel)(ToxWindow *, ToxAv *); + void(*onReject)(ToxWindow *, ToxAv *); + void(*onEnd)(ToxWindow *, ToxAv *); + void(*onTimeout)(ToxWindow *, ToxAv *); + +#endif /* _SUPPORT_AUDIO */ + char name[TOX_MAX_NAME_LENGTH]; int num; bool active;