From 7dead5ec961a52da4fa77537ae72b6a43c750377 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Thu, 7 May 2020 00:00:00 +0000 Subject: [PATCH] Implement simplistic VAD --- cfg/checks/audio.mk | 1 + doc/toxic.conf.5 | 4 +- doc/toxic.conf.5.asc | 6 +- misc/toxic.conf.example | 4 +- src/audio_call.c | 5 +- src/audio_device.c | 108 ++++++++++++++++--- src/audio_device.h | 9 +- src/chat.c | 2 +- src/conference.c | 221 +++++++++++++++++++++++--------------- src/conference.h | 6 +- src/conference_commands.c | 69 ++++++++---- src/conference_commands.h | 1 + src/execute.c | 2 +- src/help.c | 3 +- src/misc_tools.c | 4 +- src/settings.c | 8 +- src/settings.h | 2 +- src/toxic.c | 20 ++-- 18 files changed, 316 insertions(+), 159 deletions(-) diff --git a/cfg/checks/audio.mk b/cfg/checks/audio.mk index 304fd5f..53f77c0 100644 --- a/cfg/checks/audio.mk +++ b/cfg/checks/audio.mk @@ -11,6 +11,7 @@ endif CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") ifneq ($(CHECK_AUDIO_LIBS), error) LIBS += $(AUDIO_LIBS) + LDFLAGS += -lm CFLAGS += $(AUDIO_CFLAGS) OBJ += $(AUDIO_OBJ) else ifneq ($(MAKECMDGOALS), clean) diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index f375105..5b1aebb 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -217,9 +217,9 @@ Audio output device\&. Integer value\&. Number corresponds to /lsdev out .RE .PP -\fBVAD_treshold\fR +\fBVAD_threshold\fR .RS 4 -Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0 +Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0-40\&.0 .RE .RE .PP diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index 1794fa0..c8e5454 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -135,9 +135,9 @@ OPTIONS *output_device*;; Audio output device. Integer value. Number corresponds to `/lsdev out` - *VAD_treshold*;; - Voice Activity Detection treshold. Float value. Recommended values are - around 40.0 + *VAD_threshold*;; + Voice Activity Detection threshold. Float value. Recommended values are + 1.0-40.0 *tox*:: Configuration related to paths. diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index 4f2395b..df3f8a4 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -82,8 +82,8 @@ audio = { // preferred audio output device; numbers correspond to /lsdev out output_device=0; - // default VAD treshold; float (recommended values are around 40) - VAD_treshold=40.0; + // default VAD threshold; float (recommended values are 1.0-40.0) + VAD_threshold=5.0; }; tox = { diff --git a/src/audio_call.c b/src/audio_call.c index a5d4bff..02e0196 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -213,9 +213,8 @@ int start_transmission(ToxWindow *self, Call *call) return -1; } - DeviceError error = open_input_device(&call->in_idx, read_device_callback, &self->num, true, + DeviceError error = open_input_device(&call->in_idx, read_device_callback, &self->num, false, CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels); - /* Set VAD as true for all; TODO: Make it more dynamic */ if (error != de_None) { if (error == de_FailedStart) { @@ -819,7 +818,7 @@ void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[M /* Call must be active */ if (self->is_call) { - device_set_VAD_treshold(CallControl.calls[self->num].in_idx, value); + device_set_VAD_threshold(CallControl.calls[self->num].in_idx, value); self->chatwin->infobox.vad_lvl = value; } diff --git a/src/audio_device.c b/src/audio_device.c index 17f8d56..0d6558d 100644 --- a/src/audio_device.c +++ b/src/audio_device.c @@ -22,10 +22,6 @@ #include "audio_device.h" -#ifdef AUDIO -#include "audio_call.h" -#endif - #include "line_info.h" #include "settings.h" #include "misc_tools.h" @@ -43,6 +39,7 @@ #include #include #include +#include extern struct user_settings *user_settings; extern struct Winthread Winthread; @@ -70,11 +67,8 @@ typedef struct Device { // used only by input devices: DataHandleCallback cb; void *cb_data; - // TODO: implement VAD, and fix the typo, or remove these. - bool enable_VAD; -#ifdef AUDIO - float VAD_treshold; /* 40 is usually recommended value */ -#endif + float VAD_threshold; + uint32_t VAD_samples_remaining; // used only by output devices: uint32_t source; @@ -89,9 +83,11 @@ typedef struct AudioState { uint32_t num_devices[2]; FrameInfo capture_frame_info; + float input_volume; // mutexes to prevent changes to input resp. output devices and al_devices - // during poll_input iterations resp. calls to write_out + // during poll_input iterations resp. calls to write_out; + // mutex[input] also used to lock input_volume which poll_input writes to. pthread_mutex_t mutex[2]; // TODO: unused @@ -118,7 +114,9 @@ static void unlock(DeviceType type) static bool thread_running = true, thread_paused = true; /* Thread control */ +#ifdef AUDIO static void *poll_input(void *); +#endif static uint32_t sound_mode(bool stereo) { @@ -148,6 +146,7 @@ DeviceError init_devices(void) } } +#ifdef AUDIO // Start poll thread pthread_t thread_id; @@ -156,6 +155,8 @@ DeviceError init_devices(void) return de_InternalError; } +#endif + return de_None; } @@ -247,8 +248,7 @@ bool device_is_muted(DeviceType type, uint32_t device_idx) return device->muted; } -#ifdef AUDIO -DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) +DeviceError device_set_VAD_threshold(uint32_t device_idx, float value) { if (device_idx >= MAX_DEVICES) { return de_InvalidSelection; @@ -260,14 +260,32 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) return de_DeviceNotActive; } + if (value <= 0.0f) { + value = 0.0f; + } + lock(input); - device->VAD_treshold = value; + device->VAD_threshold = value; unlock(input); return de_None; } -#endif + +float device_get_VAD_threshold(uint32_t device_idx) +{ + if (device_idx >= MAX_DEVICES) { + return 0.0; + } + + Device *device = &audio_state->devices[input][device_idx]; + + if (!device->active) { + return 0.0; + } + + return device->VAD_threshold; +} DeviceError set_source_position(uint32_t device_idx, float x, float y, float z) { @@ -511,9 +529,10 @@ static DeviceError open_device(DeviceType type, uint32_t *device_idx, if (type == input) { device->cb = cb; device->cb_data = cb_data; - device->enable_VAD = enable_VAD; #ifdef AUDIO - device->VAD_treshold = user_settings->VAD_treshold; + device->VAD_threshold = enable_VAD ? user_settings->VAD_threshold : 0.0f; +#else + device->VAD_threshold = 0.0f; #endif } else { if (open_source(device) != de_None) { @@ -606,7 +625,7 @@ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_ ALuint *bufids = malloc(processed * sizeof(ALuint)); if (bufids == NULL) { - pthread_mutex_unlock(device->mutex); + unlock(output); return de_InternalError; } @@ -639,6 +658,33 @@ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_ return de_None; } +#ifdef AUDIO +/* Adapted from qtox, + * Copyright © 2014-2019 by The qTox Project Contributors + * + * return normalized volume of buffer in range 0.0-100.0 + */ +float volume(int16_t *frame, uint32_t samples) +{ + float sum_of_squares = 0; + + for (uint32_t i = 0; i < samples; i++) { + const float sample = (float)(frame[i]) / INT16_MAX; + sum_of_squares += powf(sample, 2); + } + + const float root_mean_square = sqrtf(sum_of_squares / samples); + const float root_two = 1.414213562; + + // normalizedVolume == 1.0 corresponds to a sine wave of maximal amplitude + const float normalized_volume = root_mean_square * root_two; + + return 100.0f * fminf(1.0f, normalized_volume); +} + +// Time in ms for which we continue to capture audio after VAD is triggered: +#define VAD_TIME 250 + #define FRAME_BUF_SIZE 16000 static void *poll_input(void *arg) @@ -679,9 +725,23 @@ static void *poll_input(void *arg) pthread_mutex_lock(&Winthread.lock); lock(input); + float frame_volume = volume(frame_buf, f_size); + + audio_state->input_volume = frame_volume; + for (int i = 0; i < MAX_DEVICES; i++) { Device *device = &audio_state->devices[input][i]; + if (device->VAD_threshold != 0.0f) { + if (frame_volume >= device->VAD_threshold) { + device->VAD_samples_remaining = VAD_TIME * (audio_state->capture_frame_info.sample_rate / 1000); + } else if (device->VAD_samples_remaining < f_size) { + continue; + } else { + device->VAD_samples_remaining -= f_size; + } + } + if (device->active && !device->muted && device->cb) { device->cb(frame_buf, f_size, device->cb_data); } @@ -697,6 +757,20 @@ static void *poll_input(void *arg) pthread_exit(NULL); } +#endif + +float get_input_volume(void) +{ + float ret = 0.0f; + + if (audio_state->al_device[input] != NULL) { + lock(input); + ret = audio_state->input_volume; + unlock(input); + } + + return ret; +} void print_al_devices(ToxWindow *self, DeviceType type) { diff --git a/src/audio_device.h b/src/audio_device.h index 01fe0e9..0f3fe16 100644 --- a/src/audio_device.h +++ b/src/audio_device.h @@ -66,9 +66,9 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx); bool device_is_muted(DeviceType type, uint32_t device_idx); -#ifdef AUDIO -DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); -#endif +DeviceError device_set_VAD_threshold(uint32_t device_idx, float value); + +float device_get_VAD_threshold(uint32_t device_idx); DeviceError set_source_position(uint32_t device_idx, float x, float y, float z); @@ -88,6 +88,9 @@ DeviceError close_device(DeviceType type, uint32_t device_idx); DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels, uint32_t sample_rate); +/* return current input volume as float in range 0.0-100.0 */ +float get_input_volume(void); + void print_al_devices(ToxWindow *self, DeviceType type); DeviceError selection_valid(DeviceType type, int32_t selection); diff --git a/src/chat.c b/src/chat.c index 9926b4f..15427e5 100644 --- a/src/chat.c +++ b/src/chat.c @@ -950,7 +950,7 @@ static void init_infobox(ToxWindow *self) ctx->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); ctx->infobox.starttime = get_unix_time(); - ctx->infobox.vad_lvl = user_settings->VAD_treshold; + ctx->infobox.vad_lvl = 0.0f; ctx->infobox.active = true; strcpy(ctx->infobox.timestr, "00"); } diff --git a/src/conference.c b/src/conference.c index 901ac43..1e2edd1 100644 --- a/src/conference.c +++ b/src/conference.c @@ -353,12 +353,14 @@ uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *pre return 0; } - const int len = strlen(prefix); + const size_t len = strlen(prefix); if (len == 2 * TOX_PUBLIC_KEY_SIZE) { for (uint32_t i = 0; i < chat->num_peers; ++i) { - if (strcasecmp(prefix, chat->name_list[i].pubkey_str) == 0) { - entries[0] = &chat->name_list[i]; + NameListEntry *entry = &chat->name_list[i]; + + if (strcasecmp(prefix, entry->pubkey_str) == 0) { + entries[0] = entry; return 1; } } @@ -367,9 +369,11 @@ uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *pre uint32_t n = 0; for (uint32_t i = 0; i < chat->num_peers; ++i) { - if (strncmp(prefix, chat->name_list[i].name, len) == 0 - || strncasecmp(prefix, chat->name_list[i].pubkey_str, len) == 0) { - entries[n] = &chat->name_list[i]; + NameListEntry *entry = &chat->name_list[i]; + + if (strncmp(prefix, entry->name, len) == 0 + || strncasecmp(prefix, entry->pubkey_str, len) == 0) { + entries[n] = entry; ++n; if (n == maxpeers) { @@ -418,11 +422,14 @@ static void conference_update_name_list(uint32_t conferencenum) uint32_t count = 0; for (uint32_t i = 0; i < chat->max_idx; ++i) { - if (chat->peer_list[i].active) { - memcpy(chat->name_list[count].name, chat->peer_list[i].name, chat->peer_list[i].name_length + 1); - bin_pubkey_to_string(chat->peer_list[i].pubkey, sizeof(chat->peer_list[i].pubkey), - chat->name_list[count].pubkey_str, sizeof(chat->name_list[count].pubkey_str)); - chat->name_list[count].peernum = i; + const ConferencePeer *peer = &chat->peer_list[i]; + NameListEntry *entry = &chat->name_list[count]; + + if (peer->active) { + memcpy(entry->name, peer->name, peer->name_length + 1); + bin_pubkey_to_string(peer->pubkey, sizeof(peer->pubkey), + entry->pubkey_str, sizeof(entry->pubkey_str)); + entry->peernum = i; ++count; } } @@ -451,7 +458,7 @@ static int realloc_peer_list(ConferenceChat *chat, uint32_t num_peers) return 0; } - struct ConferencePeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct ConferencePeer)); + ConferencePeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(ConferencePeer)); if (!tmp_list) { return -1; @@ -462,13 +469,35 @@ static int realloc_peer_list(ConferenceChat *chat, uint32_t num_peers) return 0; } +/* return NULL if peer or conference doesn't exist */ +static ConferencePeer *peer_in_conference(uint32_t conferencenum, uint32_t peernum) +{ + if (conferencenum >= MAX_CONFERENCE_NUM) { + return NULL; + } + + const ConferenceChat *chat = &conferences[conferencenum]; + + if (!chat->active || peernum > chat->max_idx) { + return NULL; + } + + ConferencePeer *peer = &chat->peer_list[peernum]; + + if (!peer->active) { + return NULL; + } + + return peer; +} + #ifdef AUDIO static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum) { ConferenceChat *chat = &conferences[conferencenum]; ConferencePeer *peer = &chat->peer_list[peernum]; - if (!peer->sending_audio) { + if (peer == NULL || !peer->sending_audio) { return; } @@ -497,10 +526,10 @@ static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t pee #endif -static bool find_peer_by_pubkey(ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx) +static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx) { for (uint32_t i = 0; i < num_peers; ++i) { - ConferencePeer *peer = &list[i]; + const ConferencePeer *peer = &list[i]; if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) { *idx = i; @@ -567,7 +596,7 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, peer->active = true; peer->name_length = length; - peer->peernumber = i; + peer->peernum = i; #ifdef AUDIO set_peer_audio_position(m, conferencenum, i); @@ -628,28 +657,19 @@ static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint3 return; } - ConferenceChat *chat = &conferences[conferencenum]; + const ConferencePeer *peer = peer_in_conference(conferencenum, peernum); - if (!chat->active) { - return; - } + if (peer != NULL && peer->name_length > 0) { + ChatContext *ctx = self->chatwin; + char timefrmt[TIME_STR_SIZE]; + get_time_str(timefrmt, sizeof(timefrmt)); - for (uint32_t i = 0; i < chat->max_idx; ++i) { - ConferencePeer *peer = &chat->peer_list[i]; + char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; + snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name); - if (peer->active && peer->peernumber == peernum && peer->name_length > 0) { - ChatContext *ctx = self->chatwin; - char timefrmt[TIME_STR_SIZE]; - get_time_str(timefrmt, sizeof(timefrmt)); + write_to_log(tmp_event, peer->name, ctx->log, true); + line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); - char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; - snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name); - - write_to_log(tmp_event, peer->name, ctx->log, true); - line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); - - break; - } } conference_onConferenceNameListChange(self, m, conferencenum); @@ -716,6 +736,7 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) } bool input_ret = false; + ConferenceChat *chat = &conferences[self->num]; if (key == L'\t') { /* TAB key: auto-completes peer name or command */ input_ret = true; @@ -725,14 +746,14 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) /* TODO: make this not suck */ if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) { - const char **complete_strs = calloc(conferences[self->num].num_peers, sizeof(const char *)); + const char **complete_strs = calloc(chat->num_peers, sizeof(const char *)); if (complete_strs) { - for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) { - complete_strs[i] = (const char *) conferences[self->num].name_list[i].name; + for (uint32_t i = 0; i < chat->num_peers; ++i) { + complete_strs[i] = (const char *) chat->name_list[i].name; } - diff = complete_line(self, complete_strs, conferences[self->num].num_peers); + diff = complete_line(self, complete_strs, chat->num_peers); free(complete_strs); } } else if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) { @@ -746,20 +767,20 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) #endif else if (wcsncmp(ctx->line, L"/mute ", wcslen(L"/mute ")) == 0) { - const char **complete_strs = calloc(conferences[self->num].num_peers, sizeof(const char *)); + const char **complete_strs = calloc(chat->num_peers, sizeof(const char *)); if (complete_strs) { - for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) { - complete_strs[i] = (const char *) conferences[self->num].name_list[i].name; + for (uint32_t i = 0; i < chat->num_peers; ++i) { + complete_strs[i] = (const char *) chat->name_list[i].name; } - diff = complete_line(self, complete_strs, conferences[self->num].num_peers); + diff = complete_line(self, complete_strs, chat->num_peers); if (diff == -1) { - for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) { - complete_strs[i] = (const char *) conferences[self->num].name_list[i].pubkey_str; + for (uint32_t i = 0; i < chat->num_peers; ++i) { + complete_strs[i] = (const char *) chat->name_list[i].pubkey_str; } - diff = complete_line(self, complete_strs, conferences[self->num].num_peers); + diff = complete_line(self, complete_strs, chat->num_peers); } free(complete_strs); @@ -784,14 +805,14 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) input_ret = true; const int L = y2 - CHATBOX_HEIGHT - sidebar_offset(self->num); - if (conferences[self->num].side_pos < (int64_t) conferences[self->num].num_peers - L) { - ++conferences[self->num].side_pos; + if (chat->side_pos < (int64_t) chat->num_peers - L) { + ++chat->side_pos; } } else if (key == T_KEY_C_UP) { input_ret = true; - if (conferences[self->num].side_pos > 0) { - --conferences[self->num].side_pos; + if (chat->side_pos > 0) { + --chat->side_pos; } } else if (key == L'\r') { input_ret = true; @@ -850,14 +871,14 @@ static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i) if (audio) { #ifdef AUDIO pthread_mutex_lock(&Winthread.lock); - const ConferencePeer *peer = &conferences[self->num].peer_list[peernum]; + const ConferencePeer *peer = peer_in_conference(self->num, peernum); const bool audio_active = is_self ? !timed_out(conferences[self->num].last_sent_audio, 2) - : peer->active && peer->sending_audio && !timed_out(peer->last_audio_time, 2); + : peer != NULL && peer->sending_audio && !timed_out(peer->last_audio_time, 2); const bool mute = audio_active && (is_self ? device_is_muted(input, conferences[self->num].audio_in_idx) - : device_is_muted(output, peer->audio_out_idx)); + : peer != NULL && device_is_muted(output, peer->audio_out_idx)); pthread_mutex_unlock(&Winthread.lock); const int aud_attr = A_BOLD | COLOR_PAIR(audio_active && !mute ? GREEN : RED); @@ -929,14 +950,27 @@ static void conference_onDraw(ToxWindow *self, Tox *m) #ifdef AUDIO pthread_mutex_lock(&Winthread.lock); const bool mic_on = !device_is_muted(input, conferences[self->num].audio_in_idx); + const float volume = get_input_volume(); + const float threshold = device_get_VAD_threshold(conferences[self->num].audio_in_idx); pthread_mutex_unlock(&Winthread.lock); wmove(ctx->sidebar, line, 1); wattron(ctx->sidebar, A_BOLD); wprintw(ctx->sidebar, "Mic: "); - const int color = mic_on ? GREEN : RED; + const int color = mic_on && volume > threshold ? GREEN : RED; wattron(ctx->sidebar, COLOR_PAIR(color)); - wprintw(ctx->sidebar, mic_on ? "ON" : "OFF"); + + if (mic_on) { + float v = volume; + + while (v > 0.0f) { + wprintw(ctx->sidebar, v > 10.0f ? (v > 15.0f ? "*" : "+") : (v > 5.0f ? "-" : ".")); + v -= 20.0f; + } + } else { + wprintw(ctx->sidebar, "OFF"); + } + wattroff(ctx->sidebar, COLOR_PAIR(color)); wattroff(ctx->sidebar, A_BOLD); ++line; @@ -953,10 +987,9 @@ static void conference_onDraw(ToxWindow *self, Tox *m) mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); ++line; - int maxlines = y2 - header_lines - CHATBOX_HEIGHT; - uint32_t i; - - for (i = 0; i < num_peers && i < maxlines; ++i) { + for (uint32_t i = 0; + i < num_peers && i < y2 - header_lines - CHATBOX_HEIGHT; + ++i) { wmove(ctx->sidebar, i + header_lines, 1); draw_peer(self, m, ctx, i); } @@ -1060,40 +1093,32 @@ static ToxWindow *new_conference_chat(uint32_t conferencenum) #define CONFAV_AUDIO_CHANNELS 1 #define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000) -void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernumber, const int16_t *pcm, +void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata) { - ConferenceChat *chat = &conferences[conferencenum]; + ConferencePeer *peer = peer_in_conference(conferencenum, peernum); - if (!chat->active) { + if (peer == NULL) { return; } - for (uint32_t i = 0; i < chat->max_idx; ++i) { - ConferencePeer *peer = &chat->peer_list[i]; - - if (!peer->active || peer->peernumber != peernumber) { - continue; + if (!peer->sending_audio) { + if (open_output_device(&peer->audio_out_idx, + sample_rate, CONFAV_FRAME_DURATION, channels) != de_None) { + // TODO: error message? + return; } - if (!peer->sending_audio) { - if (open_output_device(&peer->audio_out_idx, - sample_rate, CONFAV_FRAME_DURATION, channels) != de_None) { - // TODO: error message? - return; - } + peer->sending_audio = true; - peer->sending_audio = true; - - set_peer_audio_position(tox, conferencenum, i); - } - - write_out(peer->audio_out_idx, pcm, samples, channels, sample_rate); - - peer->last_audio_time = get_unix_time(); - - return; + set_peer_audio_position(tox, conferencenum, peernum); } + + write_out(peer->audio_out_idx, pcm, samples, channels, sample_rate); + + peer->last_audio_time = get_unix_time(); + + return; } static void conference_read_device_callback(const int16_t *captured, uint32_t size, void *data) @@ -1155,12 +1180,12 @@ bool disable_conference_audio(Tox *tox, uint32_t conferencenum) chat->audio_enabled = false; } - return (toxav_groupchat_disable_av(tox, conferencenum) == 0); + return toxav_groupchat_disable_av(tox, conferencenum) == 0; } bool conference_mute_self(uint32_t conferencenum) { - ConferenceChat *chat = &conferences[conferencenum]; + const ConferenceChat *chat = &conferences[conferencenum]; if (!chat->active || !chat->audio_enabled) { return false; @@ -1177,20 +1202,42 @@ bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum return conference_mute_self(conferencenum); } - ConferenceChat *chat = &conferences[conferencenum]; + const ConferenceChat *chat = &conferences[conferencenum]; if (!chat->active || !chat->audio_enabled || peernum > chat->max_idx) { return false; } - const ConferencePeer *peer = &chat->peer_list[peernum]; + const ConferencePeer *peer = peer_in_conference(conferencenum, peernum); - if (!peer->active || !peer->sending_audio) { + if (peer == NULL || !peer->sending_audio) { return false; } device_mute(output, peer->audio_out_idx); return true; } + +bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold) +{ + const ConferenceChat *chat = &conferences[conferencenum]; + + if (!chat->active || !chat->audio_enabled) { + return false; + } + + return (device_set_VAD_threshold(chat->audio_in_idx, threshold) == de_None); +} + +float conference_get_VAD_threshold(uint32_t conferencenum) +{ + const ConferenceChat *chat = &conferences[conferencenum]; + + if (!chat->active || !chat->audio_enabled) { + return 0.0f; + } + + return device_get_VAD_threshold(chat->audio_in_idx); +} #endif diff --git a/src/conference.h b/src/conference.h index b048446..db1ac05 100644 --- a/src/conference.h +++ b/src/conference.h @@ -34,7 +34,7 @@ typedef struct ConferencePeer { bool active; uint8_t pubkey[TOX_PUBLIC_KEY_SIZE]; - uint32_t peernumber; + uint32_t peernum; /* index in chat->peer_list */ char name[TOX_MAX_NAME_LENGTH]; size_t name_length; @@ -96,11 +96,13 @@ uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *pre bool init_conference_audio_input(Tox *tox, uint32_t conferencenum); bool enable_conference_audio(Tox *tox, uint32_t conferencenum); bool disable_conference_audio(Tox *tox, uint32_t conferencenum); -void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernumber, +void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata); bool conference_mute_self(uint32_t conferencenum); bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum); +bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold); +float conference_get_VAD_threshold(uint32_t conferencenum); #endif /* CONFERENCE_H */ diff --git a/src/conference_commands.c b/src/conference_commands.c index 75c5592..d7469fc 100644 --- a/src/conference_commands.c +++ b/src/conference_commands.c @@ -21,6 +21,7 @@ */ #include +#include #include "toxic.h" #include "windows.h" @@ -29,6 +30,11 @@ #include "log.h" #include "conference.h" +static void print_err(ToxWindow *self, const char *error_str) +{ + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); +} + void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { UNUSED_VAR(window); @@ -40,12 +46,12 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, size_t tlen = tox_conference_get_title_size(m, self->num, &err); if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); + print_err(self, "Title is not set"); return; } if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); + print_err(self, "Title is not set"); return; } @@ -81,11 +87,11 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, write_to_log(tmp_event, selfnick, self->chatwin->log, true); } +#ifdef AUDIO void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { UNUSED_VAR(window); -#ifdef AUDIO bool enable; if (argc == 1 && !strcasecmp(argv[1], "on")) { @@ -93,43 +99,38 @@ void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (* } else if (argc == 1 && !strcasecmp(argv[1], "off")) { enable = false; } else { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify: on | off"); + print_err(self, "Please specify: on | off"); return; } - if ((enable ? enable_conference_audio : disable_conference_audio)(m, self->num)) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, enable ? "Enabled conference audio" : "Disabled conference audio"); + if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { + print_err(self, enable ? "Enabled conference audio" : "Disabled conference audio"); } else { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, enable ? "Failed to enable audio" : "Failed to disable audio"); + print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); } - -#endif } void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { UNUSED_VAR(window); -#ifdef AUDIO - if (argc < 1) { if (conference_mute_self(self->num)) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled self audio mute status"); + print_err(self, "Toggled self audio mute status"); } else { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No audio input to mute"); + print_err(self, "No audio input to mute"); } } else { NameListEntry *entries[16]; uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16); if (n == 0) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No such peer"); + print_err(self, "No such peer"); return; } if (n > 1) { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, - "Multiple matching peers (use /mute [public key] to disambiguate):"); + print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); for (uint32_t i = 0; i < n; ++i) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); @@ -141,9 +142,39 @@ void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char if (conference_mute_peer(m, self->num, entries[0]->peernum)) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); } else { - line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No such audio peer"); + print_err(self, "No such audio peer"); } } - -#endif } + +void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + UNUSED_VAR(window); + UNUSED_VAR(m); + + if (argc == 0) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", + (double) conference_get_VAD_threshold(self->num)); + return; + } + + if (argc > 1) { + print_err(self, "Only one argument allowed."); + return; + } + + char *end; + float value = strtof(argv[1], &end); + + if (*end) { + print_err(self, "Invalid input"); + return; + } + + if (conference_set_VAD_threshold(self->num, value)) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); + } else { + print_err(self, "Failed to set conference audio input sensitivity."); + } +} +#endif /* AUDIO */ diff --git a/src/conference_commands.h b/src/conference_commands.h index b8deabf..de08084 100644 --- a/src/conference_commands.h +++ b/src/conference_commands.h @@ -29,5 +29,6 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); +void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); #endif /* CONFERENCE_COMMANDS_H */ diff --git a/src/execute.c b/src/execute.c index 6b76f9a..857b769 100644 --- a/src/execute.c +++ b/src/execute.c @@ -103,7 +103,7 @@ static struct cmd_func conference_commands[] = { #ifdef AUDIO { "/audio", cmd_enable_audio }, { "/mute", cmd_conference_mute }, - { "/sense", cmd_sense }, + { "/sense", cmd_conference_sense }, #endif /* AUDIO */ { NULL, NULL }, }; diff --git a/src/help.c b/src/help.c index 0ab2223..becd109 100644 --- a/src/help.c +++ b/src/help.c @@ -311,7 +311,8 @@ static void help_draw_conference(ToxWindow *self) wattroff(win, A_BOLD); wprintw(win, " /audio or : Enable/disable audio in an audio conference\n"); wprintw(win, " /mute : Toggle self audio mute status\n"); - wprintw(win, " /mute or : Toggle peer audio mute status\n\n"); + wprintw(win, " /mute or : Toggle peer audio mute status\n"); + wprintw(win, " /sense : VAD sensitivity threshold\n\n"); #endif help_draw_bottom_menu(win); diff --git a/src/misc_tools.c b/src/misc_tools.c index f373373..0586eaa 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -197,9 +197,7 @@ int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char return -1; } - size_t i; - - for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) { + for (size_t i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) { snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff); } diff --git a/src/settings.c b/src/settings.c index 0edecc1..6ba2aa8 100644 --- a/src/settings.c +++ b/src/settings.c @@ -204,19 +204,19 @@ static const struct audio_strings { const char *self; const char *input_device; const char *output_device; - const char *VAD_treshold; + const char *VAD_threshold; } audio_strings = { "audio", "input_device", "output_device", - "VAD_treshold", + "VAD_threshold", }; static void audio_defaults(struct user_settings *settings) { settings->audio_in_dev = 0; settings->audio_out_dev = 0; - settings->VAD_treshold = 40.0; + settings->VAD_threshold = 5.0; } #endif @@ -505,7 +505,7 @@ int settings_load(struct user_settings *s, const char *patharg) config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev); s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev; - config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold); + config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold); } #endif diff --git a/src/settings.h b/src/settings.h index fc43999..c0a5211 100644 --- a/src/settings.h +++ b/src/settings.h @@ -84,7 +84,7 @@ struct user_settings { #ifdef AUDIO int audio_in_dev; int audio_out_dev; - double VAD_treshold; + double VAD_threshold; #endif }; diff --git a/src/toxic.c b/src/toxic.c index 8d75191..511fc43 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -1440,20 +1440,10 @@ int main(int argc, char **argv) prompt_init_statusbar(prompt, m, !datafile_exists); load_conferences(m); - /* thread for ncurses stuff */ if (pthread_mutex_init(&Winthread.lock, NULL) != 0) { exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); } - if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) { - exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); - } - - /* thread for message queue */ - if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) { - exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); - } - #ifdef AUDIO av = init_audio(prompt, m); @@ -1479,6 +1469,16 @@ int main(int argc, char **argv) #endif /* AUDIO */ + /* thread for ncurses stuff */ + if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) { + exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); + } + + /* thread for message queue */ + if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) { + exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); + } + #ifdef PYTHON init_python(m);