diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index 08054cc..5b29b6e 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -222,6 +222,11 @@ Audio output device\&. Integer value\&. Number corresponds to Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0 .RE .PP +\fBpush_to_talk\fR +.RS 4 +Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false +.RE +.PP \fBconference_audio_channels\fR .RS 4 Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index c4c379c..a1df5e4 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -145,6 +145,9 @@ OPTIONS *chat_audio_channels*;; Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo) + *push_to_talk*;; + Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false + *tox*:: Configuration related to paths. diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index ffa3d07..8573837 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -90,6 +90,9 @@ audio = { // Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo. chat_audio_channels=2; + + // toggle conference push-to-talk + push_to_talk=false; }; tox = { diff --git a/src/conference.c b/src/conference.c index 6c81a5b..b5d6162 100644 --- a/src/conference.c +++ b/src/conference.c @@ -100,6 +100,7 @@ static const char *conference_cmd_list[] = { "/quit", "/requests", #ifdef AUDIO + "/ptt", "/sense", #endif "/status", @@ -200,6 +201,10 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char conferences[i].audio_enabled = false; conferences[i].last_sent_audio = 0; +#ifdef AUDIO + conferences[i].push_to_talk_enabled = user_settings->push_to_talk; +#endif + set_active_window_index(conferences[i].chatwin); conference_set_title(self, conferencenum, title, length); @@ -568,6 +573,22 @@ static ConferencePeer *peer_in_conference(uint32_t conferencenum, uint32_t peern } #ifdef AUDIO + +/* Return true if ptt is disabled or enabled and active. */ +static bool conference_check_push_to_talk(ConferenceChat *chat) +{ + if (!chat->push_to_talk_enabled) { + return true; + } + + return !timed_out(chat->ptt_last_pushed, 1); +} + +static void conference_enable_push_to_talk(ConferenceChat *chat) +{ + chat->ptt_last_pushed = get_unix_time(); +} + static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum) { ConferenceChat *chat = &conferences[conferencenum]; @@ -599,7 +620,7 @@ static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t pee const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2); set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0); } -#endif +#endif // AUDIO static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx) @@ -774,7 +795,6 @@ static int sidebar_offset(uint32_t conferencenum) return 2 + conferences[conferencenum].audio_enabled; } - /* * Return true if input is recognized by handler */ @@ -817,6 +837,15 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) bool input_ret = false; ConferenceChat *chat = &conferences[self->num]; +#ifdef AUDIO + + if (chat->audio_enabled && chat->push_to_talk_enabled && key == KEY_F(2)) { + input_ret = true; + conference_enable_push_to_talk(chat); + } + +#endif // AUDIO + if (key == L'\t') { /* TAB key: auto-completes peer name or command */ input_ret = true; @@ -997,6 +1026,12 @@ static void conference_onDraw(ToxWindow *self, Tox *m) return; } + ConferenceChat *chat = &conferences[self->num]; + + if (!chat->active) { + return; + } + ChatContext *ctx = self->chatwin; pthread_mutex_lock(&Winthread.lock); @@ -1019,8 +1054,8 @@ static void conference_onDraw(ToxWindow *self, Tox *m) mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); pthread_mutex_lock(&Winthread.lock); - const uint32_t num_peers = conferences[self->num].num_peers; - const bool audio = conferences[self->num].audio_enabled; + const uint32_t num_peers = chat->num_peers; + const bool audio = chat->audio_enabled; const int header_lines = sidebar_offset(self->num); pthread_mutex_unlock(&Winthread.lock); @@ -1029,32 +1064,45 @@ static void conference_onDraw(ToxWindow *self, Tox *m) if (audio) { #ifdef AUDIO pthread_mutex_lock(&Winthread.lock); - const bool mic_on = !device_is_muted(input, conferences[self->num].audio_in_idx); + const bool ptt_idle = !conference_check_push_to_talk(chat) && chat->push_to_talk_enabled; + const bool mic_on = !device_is_muted(input, chat->audio_in_idx); const float volume = get_input_volume(); - const float threshold = device_get_VAD_threshold(conferences[self->num].audio_in_idx); + const float threshold = device_get_VAD_threshold(chat->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 && volume > threshold ? GREEN : RED; - wattron(ctx->sidebar, COLOR_PAIR(color)); - if (mic_on) { + if (!mic_on) { + wattron(ctx->sidebar, COLOR_PAIR(RED)); + wprintw(ctx->sidebar, "MUTED"); + wattroff(ctx->sidebar, COLOR_PAIR(RED)); + } else if (ptt_idle) { + wattron(ctx->sidebar, COLOR_PAIR(GREEN)); + wprintw(ctx->sidebar, "PTT"); + wattroff(ctx->sidebar, COLOR_PAIR(GREEN)); + } else { + const int color = volume > threshold ? GREEN : RED; + wattron(ctx->sidebar, COLOR_PAIR(color)); + float v = volume; + if (v <= 0.0f) { + wprintw(ctx->sidebar, "."); + } + 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, COLOR_PAIR(color)); wattroff(ctx->sidebar, A_BOLD); ++line; -#endif +#endif // AUDIO } wmove(ctx->sidebar, line, 1); @@ -1195,7 +1243,13 @@ static void conference_read_device_callback(const int16_t *captured, uint32_t si AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data; - conferences[audio_input_callback_data->conferencenum].last_sent_audio = get_unix_time(); + ConferenceChat *chat = &conferences[audio_input_callback_data->conferencenum]; + + if (!conference_check_push_to_talk(chat)) { + return; + } + + chat->last_sent_audio = get_unix_time(); int channels = user_settings->conference_audio_channels; @@ -1228,6 +1282,19 @@ bool init_conference_audio_input(Tox *tox, uint32_t conferencenum) return success; } +bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled) +{ + ConferenceChat *chat = &conferences[conferencenum]; + + if (!chat->active) { + return false; + } + + chat->push_to_talk_enabled = enabled; + + return true; +} + bool enable_conference_audio(Tox *tox, uint32_t conferencenum) { if (!toxav_groupchat_av_enabled(tox, conferencenum)) { @@ -1312,4 +1379,4 @@ float conference_get_VAD_threshold(uint32_t conferencenum) return device_get_VAD_threshold(chat->audio_in_idx); } -#endif +#endif // AUDIO diff --git a/src/conference.h b/src/conference.h index 726fb36..4f9a88f 100644 --- a/src/conference.h +++ b/src/conference.h @@ -74,6 +74,9 @@ typedef struct { NameListEntry *name_list; uint32_t num_peers; + bool push_to_talk_enabled; + time_t ptt_last_pushed; + bool audio_enabled; time_t last_sent_audio; uint32_t audio_in_idx; @@ -104,6 +107,7 @@ 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); +bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled); 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); diff --git a/src/conference_commands.c b/src/conference_commands.c index e9ad7e9..23e4465 100644 --- a/src/conference_commands.c +++ b/src/conference_commands.c @@ -112,7 +112,8 @@ void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (* } if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) { - print_err(self, enable ? "Enabled conference audio" : "Disabled conference audio"); + print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk." + : "Disabled conference audio"); } else { print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio"); } @@ -150,7 +151,7 @@ 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 { - print_err(self, "No such audio peer"); + print_err(self, "Peer is not on the call"); } } } @@ -185,4 +186,28 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha print_err(self, "Failed to set conference audio input sensitivity."); } } + +void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) +{ + UNUSED_VAR(window); + UNUSED_VAR(m); + + bool enable; + + if (argc == 1 && !strcasecmp(argv[1], "on")) { + enable = true; + } else if (argc == 1 && !strcasecmp(argv[1], "off")) { + enable = false; + } else { + print_err(self, "Please specify: on | off"); + return; + } + + if (!toggle_conference_push_to_talk(self->num, enable)) { + print_err(self, "Failed to toggle push to talk."); + return; + } + + print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled"); +} #endif /* AUDIO */ diff --git a/src/conference_commands.h b/src/conference_commands.h index 744b5c2..edaf18b 100644 --- a/src/conference_commands.h +++ b/src/conference_commands.h @@ -30,5 +30,6 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, 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]); +void cmd_conference_push_to_talk(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 7e41b55..647518f 100644 --- a/src/execute.c +++ b/src/execute.c @@ -105,6 +105,7 @@ static struct cmd_func conference_commands[] = { #ifdef AUDIO { "/audio", cmd_enable_audio }, { "/mute", cmd_conference_mute }, + { "/ptt", cmd_conference_push_to_talk }, { "/sense", cmd_conference_sense }, #endif /* AUDIO */ { NULL, NULL }, diff --git a/src/help.c b/src/help.c index a136cd2..ec5c0fb 100644 --- a/src/help.c +++ b/src/help.c @@ -306,7 +306,7 @@ static void help_draw_conference(ToxWindow *self) wprintw(win, "Conference commands:\n"); wattroff(win, A_BOLD | COLOR_PAIR(RED)); - wprintw(win, " /title : Set conference title (show current title if no msg)\n"); + wprintw(win, " /title : Show/set conference title\n"); #ifdef AUDIO wattron(win, A_BOLD); wprintw(win, "\n Audio:\n"); @@ -314,6 +314,7 @@ static void help_draw_conference(ToxWindow *self) 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"); + wprintw(win, " /ptt or : Toggle audio input Push-To-Talk (F2 to activate)\n"); wprintw(win, " /sense : VAD sensitivity threshold\n\n"); #endif @@ -403,7 +404,7 @@ void help_onKey(ToxWindow *self, wint_t key) case L'o': height = 6; #ifdef AUDIO - height += 5; + height += 7; #endif help_init_window(self, height, 80); self->help->type = HELP_CONFERENCE; diff --git a/src/settings.c b/src/settings.c index a2ef0b0..663abb5 100644 --- a/src/settings.c +++ b/src/settings.c @@ -207,6 +207,7 @@ static const struct audio_strings { const char *VAD_threshold; const char *conference_audio_channels; const char *chat_audio_channels; + const char *push_to_talk; } audio_strings = { "audio", "input_device", @@ -214,6 +215,7 @@ static const struct audio_strings { "VAD_threshold", "conference_audio_channels", "chat_audio_channels", + "push_to_talk", }; static void audio_defaults(struct user_settings *settings) @@ -223,6 +225,7 @@ static void audio_defaults(struct user_settings *settings) settings->VAD_threshold = 5.0; settings->conference_audio_channels = 1; settings->chat_audio_channels = 2; + settings->push_to_talk = 0; } #endif @@ -519,6 +522,8 @@ int settings_load(struct user_settings *s, const char *patharg) config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels); s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels; + + config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk); } #endif diff --git a/src/settings.h b/src/settings.h index c2ebd32..8adf639 100644 --- a/src/settings.h +++ b/src/settings.h @@ -87,6 +87,7 @@ struct user_settings { double VAD_threshold; int conference_audio_channels; int chat_audio_channels; + int push_to_talk; /* boolean */ #endif };