update agc

This commit is contained in:
Anthony Minessale 2010-06-29 09:55:28 -05:00
parent 9361e2ba9d
commit fd5723387e
1 changed files with 131 additions and 74 deletions

View File

@ -176,8 +176,7 @@ typedef enum {
CFLAG_BRIDGE_TO = (1 << 6), CFLAG_BRIDGE_TO = (1 << 6),
CFLAG_WAIT_MOD = (1 << 7), CFLAG_WAIT_MOD = (1 << 7),
CFLAG_VID_FLOOR = (1 << 8), CFLAG_VID_FLOOR = (1 << 8),
CFLAG_WASTE_BANDWIDTH = (1 << 9), CFLAG_WASTE_BANDWIDTH = (1 << 9)
CFLAG_GAIN_CONTROL = (1 << 10)
} conf_flag_t; } conf_flag_t;
typedef enum { typedef enum {
@ -302,11 +301,13 @@ typedef struct conference_obj {
int end_count; int end_count;
uint32_t relationship_total; uint32_t relationship_total;
uint32_t score; uint32_t score;
int mux_loop_count;
int member_loop_count;
int agc_level;
uint32_t avg_score; uint32_t avg_score;
uint32_t avg_itt; uint32_t avg_itt;
uint32_t avg_tally; uint32_t avg_tally;
int mux_loop_count;
int member_loop_count;
} conference_obj_t; } conference_obj_t;
/* Relationship with another member */ /* Relationship with another member */
@ -338,6 +339,7 @@ struct conference_member {
switch_codec_t write_codec; switch_codec_t write_codec;
char *rec_path; char *rec_path;
uint8_t *frame; uint8_t *frame;
uint8_t *last_frame;
uint32_t frame_size; uint32_t frame_size;
uint8_t *mux_frame; uint8_t *mux_frame;
uint32_t read; uint32_t read;
@ -1013,6 +1015,11 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
int x = 0; int x = 0;
int32_t z = 0; int32_t z = 0;
int member_score_sum = 0; int member_score_sum = 0;
int divisor = 0;
if (!(divisor = conference->rate / 8000)) {
divisor = 1;
}
file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
async_file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); async_file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
@ -1034,6 +1041,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
switch_size_t file_sample_len = samples; switch_size_t file_sample_len = samples;
switch_size_t file_data_len = samples * 2; switch_size_t file_data_len = samples * 2;
int has_file_data = 0, members_with_video = 0; int has_file_data = 0, members_with_video = 0;
uint32_t conf_energy = 0;
/* Sync the conference to a single timing source */ /* Sync the conference to a single timing source */
if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) { if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) {
@ -1220,39 +1228,43 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
conference->mux_loop_count = 0; conference->mux_loop_count = 0;
conference->member_loop_count = 0; conference->member_loop_count = 0;
/* Copy audio from every member known to be producing audio into the main frame. */ /* Copy audio from every member known to be producing audio into the main frame. */
for (omember = conference->members; omember; omember = omember->next) { for (omember = conference->members; omember; omember = omember->next) {
if (switch_test_flag(conference, CFLAG_GAIN_CONTROL)) {
member_score_sum += omember->score;
}
conference->member_loop_count++; conference->member_loop_count++;
if (!(switch_test_flag(omember, MFLAG_RUNNING) && switch_test_flag(omember, MFLAG_HAS_AUDIO))) { if (!(switch_test_flag(omember, MFLAG_RUNNING) && switch_test_flag(omember, MFLAG_HAS_AUDIO))) {
continue; continue;
} }
conference->mux_loop_count++;
if (conference->agc_level) {
if (switch_test_flag(omember, MFLAG_TALKING) && switch_test_flag(omember, MFLAG_CAN_SPEAK)) {
member_score_sum += omember->score;
conference->mux_loop_count++;
}
}
bptr = (int16_t *) omember->frame; bptr = (int16_t *) omember->frame;
for (x = 0; x < omember->read / 2; x++) { for (x = 0; x < omember->read / 2; x++) {
main_frame[x] += (int32_t) bptr[x]; main_frame[x] += (int32_t) bptr[x];
} }
} }
if (switch_test_flag(conference, CFLAG_GAIN_CONTROL)) { if (conference->agc_level && conference->member_loop_count) {
if (x && conference->mux_loop_count && conference->member_loop_count > 1) { conf_energy = 0;
int this_avg = member_score_sum / conference->mux_loop_count;
conference->avg_tally += this_avg; for (x = 0; x < bytes / 2; x++) {
conference->avg_score = conference->avg_tally / ++conference->avg_itt; z = abs(main_frame[x]);
switch_normalize_to_16bit(z);
if (conference->avg_itt > (conference->rate / x) * 10) { conf_energy += (int16_t) z;
conference->avg_itt = 0;
conference->avg_tally = 0;
conference->avg_score = this_avg;
}
} }
}
conference->score = conf_energy / ((bytes / 2) / divisor) / conference->member_loop_count;
conference->avg_tally += conference->score;
conference->avg_score = conference->avg_tally / ++conference->avg_itt;
if (!conference->avg_itt) conference->avg_tally = conference->score;
}
/* Create write frame once per member who is not deaf for each sample in the main frame /* Create write frame once per member who is not deaf for each sample in the main frame
check if our audio is involved and if so, subtract it from the sample so we don't hear ourselves. check if our audio is involved and if so, subtract it from the sample so we don't hear ourselves.
@ -1952,11 +1964,14 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v
} }
/* Check for input volume adjustments */ /* Check for input volume adjustments */
if (!switch_test_flag(member->conference, CFLAG_GAIN_CONTROL)) { if (!member->conference->agc_level) {
member->agc_volume_in_level = 0; member->agc_volume_in_level = 0;
member->avg_score = 0;
member->avg_itt = 0;
member->avg_tally = 0;
} }
if (switch_test_flag(member->conference, CFLAG_GAIN_CONTROL) && member->agc_volume_in_level) { if (member->conference->agc_level && member->agc_volume_in_level) {
switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->agc_volume_in_level); switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->agc_volume_in_level);
} else if (member->volume_in_level) { } else if (member->volume_in_level) {
switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level); switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level);
@ -1989,18 +2004,15 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v
member->score = energy / (samples / divisor); member->score = energy / (samples / divisor);
member->avg_tally += member->score; member->avg_tally += member->score;
member->avg_score = member->avg_tally / ++member->avg_itt; member->avg_score = member->avg_tally / ++member->avg_itt;
if (!member->avg_itt) member->avg_tally = member->score;
if (member->avg_itt > one_sec * 10) {
member->avg_itt = 0;
member->avg_tally = 0;
member->avg_score = member->score;
}
} }
if (switch_test_flag(member->conference, CFLAG_GAIN_CONTROL) && member->conference->avg_score && member->score && if (member->conference->agc_level && member->score &&
switch_test_flag(member, MFLAG_TALKING)) { switch_test_flag(member, MFLAG_TALKING) &&
int diff = member->conference->avg_score - member->avg_score; switch_test_flag(member, MFLAG_CAN_SPEAK) &&
member->score > member->energy_level
) {
int diff = member->conference->agc_level - member->score;
if (abs(diff) >= 200) { if (abs(diff) >= 200) {
member->agc_concur++; member->agc_concur++;
@ -2008,50 +2020,53 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v
member->agc_concur = 0; member->agc_concur = 0;
} }
#if 0
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG8,
"conf %s FOO %d %d %d %d %d\n",
member->conference->name,
member->id, diff, member->conference->agc_level,
member->score, member->agc_volume_in_level);
#endif
if (member->agc_concur >= one_sec) { if (member->agc_concur >= one_sec) {
if (diff > 200) { if (member->score < member->conference->agc_level) {
member->agc_volume_in_level++; member->agc_volume_in_level++;
if (diff > 400) {
member->agc_volume_in_level++;
}
switch_normalize_volume(member->agc_volume_in_level); switch_normalize_volume(member->agc_volume_in_level);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7,
"conf %s AGC +++ %d %d %d %d %d\n", "conf %s AGC +++ %d %d %d %d %d\n",
member->conference->name, member->conference->name,
member->id, diff, member->conference->avg_score, member->id, diff, member->conference->agc_level,
member->avg_score, member->agc_volume_in_level); member->score, member->agc_volume_in_level);
} else if (diff < -400 || (member->agc_volume_in_level > 0 && diff < -200)) { } else {
member->agc_volume_in_level--; member->agc_volume_in_level--;
if (diff < -800 || (member->agc_volume_in_level > 0 && diff < -400)) {
member->agc_volume_in_level--;
}
switch_normalize_volume(member->agc_volume_in_level); switch_normalize_volume(member->agc_volume_in_level);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7,
"conf %s AGC +++ %d %d %d %d %d\n", "conf %s AGC --- %d %d %d %d %d\n",
member->conference->name, member->conference->name,
member->id, diff, member->conference->avg_score, member->id, diff, member->conference->agc_level,
member->avg_score, member->agc_volume_in_level); member->score, member->agc_volume_in_level);
} }
member->agc_concur = 0; member->agc_concur = 0;
} }
member->nt_tally = 0; member->nt_tally = 0;
} else { } else {
member->nt_tally++; member->nt_tally++;
member->agc_concur = 0;
if (member->nt_tally > one_sec * 5) { if (member->nt_tally > one_sec * 5) {
member->agc_volume_in_level = 0;
member->nt_tally = 0; member->nt_tally = 0;
member->avg_itt = 0; member->avg_itt = 0;
member->avg_tally = 0; member->avg_tally = 0;
member->avg_score = member->score; member->avg_score = member->score;
} }
} }
member->score_iir = (int) (((1.0 - SCORE_DECAY) * (float) member->score) + (SCORE_DECAY * (float) member->score_iir)); member->score_iir = (int) (((1.0 - SCORE_DECAY) * (float) member->score) + (SCORE_DECAY * (float) member->score_iir));
@ -3389,29 +3404,52 @@ static switch_status_t conf_api_sub_mute(conference_member_t *member, switch_str
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
static switch_status_t conf_api_sub_agc_on(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv) static switch_status_t conf_api_sub_agc(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
{ {
switch_set_flag(conference, CFLAG_GAIN_CONTROL); int level;
int on = 0;
if (stream) { if (argc == 2) {
stream->write_function(stream, "OK AGC ENABLED\n"); stream->write_function(stream, "+OK CURRENT AGC LEVEL IS %d\n", conference->agc_level);
return SWITCH_STATUS_SUCCESS;
} }
if (!(on = !strcasecmp(argv[2], "on"))) {
stream->write_function(stream, "+OK AGC DISABLED\n");
conference->agc_level = 0;
return SWITCH_STATUS_SUCCESS;
}
if (argc > 3) {
level = atoi(argv[3]);
} else {
level = 2000;
}
if (level > conference->energy_level) {
conference->avg_score = 0;
conference->avg_itt = 0;
conference->avg_tally = 0;
conference->agc_level = level;
if (stream) {
stream->write_function(stream, "OK AGC ENABLED %d\n", conference->agc_level);
}
} else {
if (stream) {
stream->write_function(stream, "-ERR invalid level\n");
}
}
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
static switch_status_t conf_api_sub_agc_off(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
{
switch_clear_flag(conference, CFLAG_GAIN_CONTROL);
if (stream) {
stream->write_function(stream, "OK AGC DISABLED\n");
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t conf_api_sub_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data) static switch_status_t conf_api_sub_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
{ {
switch_event_t *event; switch_event_t *event;
@ -3760,8 +3798,10 @@ static void conference_xlist(conference_obj_t *conference, switch_xml_t x_confer
switch_xml_set_attr_d(x_conference, "dynamic", "true"); switch_xml_set_attr_d(x_conference, "dynamic", "true");
} }
if (switch_test_flag(conference, CFLAG_GAIN_CONTROL)) { if (conference->agc_level) {
switch_xml_set_attr_d(x_conference, "agc", "true"); char tmp[30] = "";
switch_snprintf(tmp, sizeof(tmp), "%d", conference->agc_level);
switch_xml_set_attr_d(x_conference, "agc", tmp);
} }
x_members = switch_xml_add_child_d(x_conference, "members", 0); x_members = switch_xml_add_child_d(x_conference, "members", 0);
@ -4507,8 +4547,7 @@ static api_command_t conf_api_sub_commands[] = {
{"relate", (void_fn_t) & conf_api_sub_relate, CONF_API_SUB_ARGS_SPLIT, "<confname> relate <member_id> <other_member_id> [nospeak|nohear|clear]"}, {"relate", (void_fn_t) & conf_api_sub_relate, CONF_API_SUB_ARGS_SPLIT, "<confname> relate <member_id> <other_member_id> [nospeak|nohear|clear]"},
{"lock", (void_fn_t) & conf_api_sub_lock, CONF_API_SUB_ARGS_SPLIT, "<confname> lock"}, {"lock", (void_fn_t) & conf_api_sub_lock, CONF_API_SUB_ARGS_SPLIT, "<confname> lock"},
{"unlock", (void_fn_t) & conf_api_sub_unlock, CONF_API_SUB_ARGS_SPLIT, "<confname> unlock"}, {"unlock", (void_fn_t) & conf_api_sub_unlock, CONF_API_SUB_ARGS_SPLIT, "<confname> unlock"},
{"agc_on", (void_fn_t) & conf_api_sub_agc_on, CONF_API_SUB_ARGS_SPLIT, "<confname> agc_on"}, {"agc", (void_fn_t) & conf_api_sub_agc, CONF_API_SUB_ARGS_SPLIT, "<confname> agc"},
{"agc_off", (void_fn_t) & conf_api_sub_agc_off, CONF_API_SUB_ARGS_SPLIT, "<confname> agc_off"},
{"dial", (void_fn_t) & conf_api_sub_dial, CONF_API_SUB_ARGS_SPLIT, {"dial", (void_fn_t) & conf_api_sub_dial, CONF_API_SUB_ARGS_SPLIT,
"<confname> dial <endpoint_module_name>/<destination> <callerid number> <callerid name>"}, "<confname> dial <endpoint_module_name>/<destination> <callerid number> <callerid name>"},
{"bgdial", (void_fn_t) & conf_api_sub_bgdial, CONF_API_SUB_ARGS_SPLIT, {"bgdial", (void_fn_t) & conf_api_sub_bgdial, CONF_API_SUB_ARGS_SPLIT,
@ -5065,8 +5104,6 @@ static void set_cflags(const char *flags, uint32_t *f)
*f |= CFLAG_VID_FLOOR; *f |= CFLAG_VID_FLOOR;
} else if (!strcasecmp(argv[i], "waste-bandwidth")) { } else if (!strcasecmp(argv[i], "waste-bandwidth")) {
*f |= CFLAG_WASTE_BANDWIDTH; *f |= CFLAG_WASTE_BANDWIDTH;
} else if (!strcasecmp(argv[i], "auto-gain-control")) {
*f |= CFLAG_GAIN_CONTROL;
} }
} }
@ -5987,6 +6024,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_m
char *pin_sound = NULL; char *pin_sound = NULL;
char *bad_pin_sound = NULL; char *bad_pin_sound = NULL;
char *energy_level = NULL; char *energy_level = NULL;
char *auto_gain_level = NULL;
char *caller_id_name = NULL; char *caller_id_name = NULL;
char *caller_id_number = NULL; char *caller_id_number = NULL;
char *caller_controls = NULL; char *caller_controls = NULL;
@ -6092,6 +6130,8 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_m
bad_pin_sound = val; bad_pin_sound = val;
} else if (!strcasecmp(var, "energy-level") && !zstr(val)) { } else if (!strcasecmp(var, "energy-level") && !zstr(val)) {
energy_level = val; energy_level = val;
} else if (!strcasecmp(var, "auto-gain-level") && !zstr(val)) {
auto_gain_level = val;
} else if (!strcasecmp(var, "caller-id-name") && !zstr(val)) { } else if (!strcasecmp(var, "caller-id-name") && !zstr(val)) {
caller_id_name = val; caller_id_name = val;
} else if (!strcasecmp(var, "caller-id-number") && !zstr(val)) { } else if (!strcasecmp(var, "caller-id-number") && !zstr(val)) {
@ -6279,6 +6319,23 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_m
if (!zstr(energy_level)) { if (!zstr(energy_level)) {
conference->energy_level = atoi(energy_level); conference->energy_level = atoi(energy_level);
if (conference->energy_level < 0) {
conference->energy_level = 0;
}
}
if (!zstr(auto_gain_level)) {
int level = 0;
if (switch_true(auto_gain_level)) {
level = 2000;
} else {
level = atoi(auto_gain_level);
}
if (level > 0 && level > conference->energy_level) {
conference->agc_level = level;
}
} }
if (!zstr(maxmember_sound)) { if (!zstr(maxmember_sound)) {