static void friendlist_onAv(ToxWindow *self, ToxAv *av) { int id = toxav_get_peer_id(av, 0); id++; if ( id >= max_friends_index) return; Tox* m = toxav_get_tox(av); if (friends[id].chatwin == -1) { if (get_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); } } }
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index) { int id = toxav_get_peer_id(av, call_index, 0); if ( id != av_ErrorUnknown && id >= Friends.max_idx) return; Tox *m = toxav_get_tox(av); if (Friends.list[id].chatwin == -1) { if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */ Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num)); } } else { char nick[TOX_MAX_NAME_LENGTH]; get_nick_truncate(m, nick, Friends.list[id].num); line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); const char *errmsg = "* Warning: Too many windows are open."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg); sound_notify(prompt, error, NT_WNDALERT_1, NULL); } } }
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) { assert(0); if( friend_number >= Friends.max_idx) return; assert(0); Tox *m = toxav_get_tox(av); if (Friends.list[friend_number].chatwin == -1) { if (get_num_active_windows() < MAX_WINDOWS_NUM) { if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) { Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); set_active_window(Friends.list[friend_number].chatwin); } } else { char nick[TOX_MAX_NAME_LENGTH]; get_nick_truncate(m, nick, Friends.list[friend_number].num); line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); const char *errmsg = "* Warning: Too many windows are open."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); } } }
void Core::sendGroupCallAudio(int groupId, ToxAv* toxav) { if (!groupCalls[groupId].active) return; if (groupCalls[groupId].muteMic || !Audio::isInputReady()) { groupCalls[groupId].sendAudioTimer->start(); return; } const int framesize = (groupCalls[groupId].codecSettings.audio_frame_duration * groupCalls[groupId].codecSettings.audio_sample_rate) / 1000 * av_DefaultSettings.audio_channels; const int bufsize = framesize * 2 * av_DefaultSettings.audio_channels; uint8_t buf[bufsize]; if (Audio::tryCaptureSamples(buf, framesize)) { if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, (int16_t*)buf, framesize, av_DefaultSettings.audio_channels, av_DefaultSettings.audio_sample_rate) < 0) { qDebug() << "Core: toxav_group_send_audio error"; groupCalls[groupId].sendAudioTimer->start(); return; } } groupCalls[groupId].sendAudioTimer->start(); }
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index) { int id = toxav_get_peer_id(av, call_index, 0); /*id++;*/ if ( id != ErrorInternal && id >= max_friends_index) return; Tox *m = toxav_get_tox(av); if (friends[id].chatwin == -1) { if (get_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'}; int n_len = tox_get_name(m, id, nick); n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); nick[n_len] = '\0'; uint8_t msg[MAX_STR_SIZE]; snprintf(msg, sizeof(msg), "Audio action from: %s!", nick); line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0); uint8_t *errmsg = "* Warning: Too many windows are open."; line_info_add(prompt, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED); alert_window(prompt, WINDOW_ALERT_0, true); } } }
bool CoreAV::sendGroupCallAudio(int groupId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) { if (!groupCalls.contains(groupId)) return false; ToxGroupCall& call = groupCalls[groupId]; if (call.inactive || call.muteMic) return true; if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, pcm, samples, chans, rate) != 0) qDebug() << "toxav_group_send_audio error"; return true; }
bool CoreAV::sendGroupCallAudio(int groupId, const int16_t* pcm, size_t samples, uint8_t chans, uint32_t rate) const { std::map<int, ToxGroupCall>::const_iterator it = groupCalls.find(groupId); if (it == groupCalls.end()) { return false; } if (!it->second.isActive() || it->second.getMuteMic()) { return true; } if (toxav_group_send_audio(toxav_get_tox(toxav), groupId, pcm, samples, chans, rate) != 0) qDebug() << "toxav_group_send_audio error"; return true; }
void utox_audio_thread(void *args) { ToxAV *av = args; const char *device_list, *output_device = NULL; void *audio_device = NULL; _Bool call[MAX_CALLS] = {0}, preview = 0; _Bool groups_audio[MAX_NUM_GROUPS] = {0}; int perframe = (UTOX_DEFAULT_FRAME_A * UTOX_DEFAULT_SAMPLE_RATE_A) / 1000; uint8_t buf[perframe * 2 * UTOX_DEFAULT_AUDIO_CHANNELS]; //, dest[perframe * 2 * UTOX_DEFAULT_AUDIO_CHANNELS]; memset(buf, 0, sizeof(buf)); uint8_t audio_count = 0; _Bool record_on = 0; #ifdef AUDIO_FILTERING debug("Audio Filtering"); #ifdef ALC_LOOPBACK_CAPTURE_SAMPLES debug(" and Echo cancellation"); #endif debug(" enabled in this build\n"); #endif debug("frame size: %u\n", perframe); device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); if (device_list) { audio_device = (void*)device_list; debug("uTox audio input device list:\n"); while(*device_list) { debug("\t%s\n", device_list); postmessage(AUDIO_IN_DEVICE, UI_STRING_ID_INVALID, 0, (void*)device_list); device_list += strlen(device_list) + 1; } } postmessage(AUDIO_IN_DEVICE, STR_AUDIO_IN_NONE, 0, NULL); audio_detect(); if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) { device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); } else { device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); } if(device_list) { output_device = device_list; debug("uTox audio output device list:\n"); while(*device_list) { debug("\t%s\n", device_list); postmessage(AUDIO_OUT_DEVICE, 0, 0, (void*)device_list); device_list += strlen(device_list) + 1; } } device_out = alcOpenDevice(output_device); if(!device_out) { debug("alcOpenDevice() failed\n"); return; } int attrlist[] = { ALC_FREQUENCY, UTOX_DEFAULT_SAMPLE_RATE_A, ALC_INVALID }; context = alcCreateContext(device_out, attrlist); if(!alcMakeContextCurrent(context)) { debug("alcMakeContextCurrent() failed\n"); alcCloseDevice(device_out); return; } alGenSources(countof(source), source); /* TODO hacky fix. This source list should be a VLA with a way to link sources to friends. * NO SRSLY don't leave this like this! */ static ALuint ringSrc[UTOX_MAX_NUM_FRIENDS]; alGenSources(UTOX_MAX_NUM_FRIENDS, ringSrc); /* Create buffer to store samples */ ALuint RingBuffer; alGenBuffers(1, &RingBuffer); { /* wrapped to keep this data on the stack... I think... */ float frequency1 = 441.f; float frequency2 = 882.f; int seconds = 4; unsigned sample_rate = 22050; size_t buf_size = seconds * sample_rate * 2; //16 bit (2 bytes per sample) int16_t *samples = malloc(buf_size * sizeof(int16_t)); if (!samples) return; /*Generate an electronic ringer sound that quickly alternates between two frequencies*/ int index = 0; for(index = 0; index < buf_size; ++index) { if ((index / (sample_rate)) % 4 < 2 ) {//4 second ring cycle, first 2 secondsring, the rest(2 seconds) is silence if((index / 1000) % 2 == 1) { samples[index] = 5000 * sin((2.0 * 3.1415926 * frequency1) / sample_rate * index); //5000=amplitude(volume level). It can be from zero to 32700 } else { samples[index] = 5000 * sin((2.0 * 3.1415926 * frequency2) / sample_rate * index); } } else { samples[index] = 0; } } alBufferData(RingBuffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate); free(samples); } { unsigned int i; for (i = 0; i < UTOX_MAX_NUM_FRIENDS; ++i) { alSourcei(ringSrc[i], AL_LOOPING, AL_TRUE); alSourcei(ringSrc[i], AL_BUFFER, RingBuffer); } } Filter_Audio *f_a = NULL; audio_thread_init = 1; int16_t *preview_buffer = NULL; unsigned int preview_buffer_index = 0; #define PREVIEW_BUFFER_SIZE (UTOX_DEFAULT_SAMPLE_RATE_A / 2) while(1) { if(audio_thread_msg) { TOX_MSG *m = &audio_msg; if(!m->msg) { break; } switch(m->msg) { case AUDIO_SET_INPUT: { audio_device = m->data; if(record_on) { alccapturestop(device_in); alccaptureclose(device_in); } if(audio_count) { device_in = alcopencapture(audio_device); if(!device_in) { record_on = 0; } else { alccapturestart(device_in); record_on = 1; } } debug("set audio in\n"); break; } case AUDIO_SET_OUTPUT: { output_device = m->data; ALCdevice *device = alcOpenDevice(output_device); if(!device) { debug("alcOpenDevice() failed\n"); break; } ALCcontext *con = alcCreateContext(device, NULL); if(!alcMakeContextCurrent(con)) { debug("alcMakeContextCurrent() failed\n"); alcCloseDevice(device); break; } alcDestroyContext(context); alcCloseDevice(device_out); context = con; device_out = device; alGenSources(countof(source), source); alGenSources(MAX_CALLS, ringSrc); Tox *tox = toxav_get_tox(av); uint32_t num_chats = tox_count_chatlist(tox); if (num_chats != 0) { int32_t chats[num_chats]; uint32_t max = tox_get_chatlist(tox, chats, num_chats); unsigned int i; for (i = 0; i < max; ++i) { if (tox_group_get_type(tox, chats[i]) == TOX_GROUPCHAT_TYPE_AV) { GROUPCHAT *g = &group[chats[i]]; alGenSources(g->peers, g->source); } } } debug("set audio out\n"); break; } case AUDIO_PREVIEW_START: { preview = 1; audio_count++; preview_buffer = calloc(PREVIEW_BUFFER_SIZE, 2); preview_buffer_index = 0; if(!record_on) { device_in = alcopencapture(audio_device); if(device_in) { alccapturestart(device_in); record_on = 1; debug("Starting Audio Preview\n"); } } break; } case AUDIO_START: { audio_count++; if(!record_on) { device_in = alcopencapture(audio_device); if(device_in) { alccapturestart(device_in); record_on = 1; debug("Listening to audio\n"); yieldcpu(20); } } break; } case GROUP_AUDIO_CALL_START: { break; // TODO, new groups API audio_count++; groups_audio[m->param1] = 1; if(!record_on) { device_in = alcopencapture(audio_device); if(device_in) { alccapturestart(device_in); record_on = 1; debug("Starting Audio GroupCall\n"); } } break; } case AUDIO_PREVIEW_END: { preview = 0; audio_count--; free(preview_buffer); preview_buffer = NULL; if(!audio_count && record_on) { alccapturestop(device_in); alccaptureclose(device_in); record_on = 0; debug("Audio Preview Stopped\n"); } break; } case AUDIO_END: { if(!call[m->param1]) { break; } call[m->param1] = 0; audio_count--; if(!audio_count && record_on) { alccapturestop(device_in); alccaptureclose(device_in); record_on = 0; debug("stop\n"); } break; } case GROUP_AUDIO_CALL_END: { break; // TODO, new groups API if(!groups_audio[m->param1]) { break; } audio_count--; groups_audio[m->param1] = 0; if(!audio_count && record_on) { alccapturestop(device_in); alccaptureclose(device_in); record_on = 0; debug("stop\n"); } break; } case AUDIO_PLAY_RINGTONE: { if(!audible_notifications_enabled) { break; } alSourcePlay(ringSrc[m->param1]); break; } case AUDIO_STOP_RINGTONE: { ALint state; alGetSourcei(ringSrc[m->param1], AL_SOURCE_STATE, &state); if(state == AL_PLAYING) { alSourceStop(ringSrc[m->param1]); } break; } } audio_thread_msg = 0; } // TODO move this code to filter_audio.c #ifdef AUDIO_FILTERING if (!f_a && audio_filtering_enabled) { f_a = new_filter_audio(UTOX_DEFAULT_SAMPLE_RATE_A); if (!f_a) { audio_filtering_enabled = 0; debug("filter audio failed\n"); } else { debug("filter audio on\n"); } } else if (f_a && !audio_filtering_enabled) { kill_filter_audio(f_a); f_a = NULL; debug("filter audio off\n"); } #else if (audio_filtering_enabled) { audio_filtering_enabled = 0; } #endif _Bool sleep = 1; if(record_on) { ALint samples; _Bool frame = 0; /* If we have a device_in we're on linux so we can just call OpenAL, otherwise we're on something else so * we'll need to call audio_frame() to add to the buffer for us. */ if (device_in == (void*)1) { frame = audio_frame((void*)buf); if (frame) { /* We have an audio frame to use, continue without sleeping. */ sleep = 0; } } else { alcGetIntegerv(device_in, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); if(samples >= perframe) { alcCaptureSamples(device_in, buf, perframe); frame = 1; if (samples >= perframe * 2) { sleep = 0; } } } #ifdef AUDIO_FILTERING #ifdef ALC_LOOPBACK_CAPTURE_SAMPLES if (f_a && audio_filtering_enabled) { alcGetIntegerv(device_out, ALC_LOOPBACK_CAPTURE_SAMPLES, sizeof(samples), &samples); if(samples >= perframe) { int16_t buffer[perframe]; alcCaptureSamplesLoopback(device_out, buffer, perframe); pass_audio_output(f_a, buffer, perframe); set_echo_delay_ms(f_a, UTOX_DEFAULT_FRAME_A); if (samples >= perframe * 2) { sleep = 0; } } } #endif #endif if (frame) { _Bool voice = 1; #ifdef AUDIO_FILTERING if (f_a) { int ret = filter_audio(f_a, (int16_t*)buf, perframe); if (ret == -1) { debug("filter audio error\n"); } if (ret == 0) { voice = 0; } } #endif /* If push to talk, we don't have to do anything */ if (!check_ptt_key()) { voice = 0; //PTT is up, send nothing. } if (preview) { if (preview_buffer_index + perframe > PREVIEW_BUFFER_SIZE) { preview_buffer_index = 0; } sourceplaybuffer(0, preview_buffer + preview_buffer_index, perframe, UTOX_DEFAULT_AUDIO_CHANNELS, UTOX_DEFAULT_SAMPLE_RATE_A); if (voice) { memcpy(preview_buffer + preview_buffer_index, buf, perframe * sizeof(int16_t)); } else { memset(preview_buffer + preview_buffer_index, 0, perframe * sizeof(int16_t)); } preview_buffer_index += perframe; } if (voice) { int i, active_call_count = 0; for(i = 0; i < UTOX_MAX_NUM_FRIENDS; i++) { if( UTOX_SEND_AUDIO(i) ) { active_call_count++; TOXAV_ERR_SEND_FRAME error = 0; toxav_audio_send_frame(av, friend[i].number, (const int16_t *)buf, perframe, UTOX_DEFAULT_AUDIO_CHANNELS, UTOX_DEFAULT_SAMPLE_RATE_A, &error); if (error) { debug("toxav_send_audio error friend == %i, error == %i\n", i, error); } else { // debug("Send a frame to friend %i\n",i); if (active_call_count >= UTOX_MAX_CALLS) { debug("We're calling more peers than allowed by UTOX_MAX_CALLS, This is a bug\n"); break; } } } } // TODO REMOVED until new groups api can be implemented. /*Tox *tox = toxav_get_tox(av); uint32_t num_chats = tox_count_chatlist(tox); if (num_chats != 0) { int32_t chats[num_chats]; uint32_t max = tox_get_chatlist(tox, chats, num_chats); for (i = 0; i < max; ++i) { if (groups_audio[chats[i]]) { toxav_group_send_audio(tox, chats[i], (int16_t *)buf, perframe, UTOX_DEFAULT_AUDIO_CHANNELS, UTOX_DEFAULT_SAMPLE_RATE_A); } } }*/ } } } if (sleep) { yieldcpu(5); } } utox_filter_audio_kill(f_a); //missing some cleanup ? alDeleteSources(MAX_CALLS, ringSrc); alDeleteSources(countof(source), source); alDeleteBuffers(1, &RingBuffer); if(device_in) { if(record_on) { alcCaptureStop(device_in); } alcCaptureCloseDevice(device_in); } alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device_out); audio_thread_msg = 0; audio_thread_init = 0; debug("UTOX AUDIO:\tClean thread exit!\n"); }