CFLAG_BRIDGE_TO = (1 << 6),
CFLAG_WAIT_MOD = (1 << 7),
CFLAG_VID_FLOOR = (1 << 8),
- CFLAG_WASTE_BANDWIDTH = (1 << 9),
- CFLAG_GAIN_CONTROL = (1 << 10)
+ CFLAG_WASTE_BANDWIDTH = (1 << 9)
} conf_flag_t;
typedef enum {
int end_count;
uint32_t relationship_total;
uint32_t score;
+ int mux_loop_count;
+ int member_loop_count;
+ int agc_level;
+
uint32_t avg_score;
uint32_t avg_itt;
uint32_t avg_tally;
- int mux_loop_count;
- int member_loop_count;
} conference_obj_t;
/* Relationship with another member */
switch_codec_t write_codec;
char *rec_path;
uint8_t *frame;
+ uint8_t *last_frame;
uint32_t frame_size;
uint8_t *mux_frame;
uint32_t read;
int x = 0;
int32_t z = 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);
async_file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
switch_size_t file_sample_len = samples;
switch_size_t file_data_len = samples * 2;
int has_file_data = 0, members_with_video = 0;
+ uint32_t conf_energy = 0;
/* Sync the conference to a single timing source */
if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) {
conference->mux_loop_count = 0;
conference->member_loop_count = 0;
+
/* Copy audio from every member known to be producing audio into the main frame. */
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++;
-
+
if (!(switch_test_flag(omember, MFLAG_RUNNING) && switch_test_flag(omember, MFLAG_HAS_AUDIO))) {
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;
for (x = 0; x < omember->read / 2; x++) {
main_frame[x] += (int32_t) bptr[x];
}
}
+
+ if (conference->agc_level && conference->member_loop_count) {
+ conf_energy = 0;
- if (switch_test_flag(conference, CFLAG_GAIN_CONTROL)) {
- if (x && conference->mux_loop_count && conference->member_loop_count > 1) {
- int this_avg = member_score_sum / conference->mux_loop_count;
-
- conference->avg_tally += this_avg;
- conference->avg_score = conference->avg_tally / ++conference->avg_itt;
-
- if (conference->avg_itt > (conference->rate / x) * 10) {
- conference->avg_itt = 0;
- conference->avg_tally = 0;
- conference->avg_score = this_avg;
- }
+ for (x = 0; x < bytes / 2; x++) {
+ z = abs(main_frame[x]);
+ switch_normalize_to_16bit(z);
+ conf_energy += (int16_t) z;
}
- }
-
+
+ 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
check if our audio is involved and if so, subtract it from the sample so we don't hear ourselves.
Since main frame was 32 bit int, we did not lose any detail, now that we have to convert to 16 bit we can
goto do_continue;
}
-
+
/* 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->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);
} else if (member->volume_in_level) {
switch_change_sln_volume(read_frame->data, read_frame->datalen / 2, member->volume_in_level);
member->score = energy / (samples / divisor);
member->avg_tally += member->score;
member->avg_score = member->avg_tally / ++member->avg_itt;
-
- if (member->avg_itt > one_sec * 10) {
- member->avg_itt = 0;
- member->avg_tally = 0;
- member->avg_score = member->score;
- }
-
+ if (!member->avg_itt) member->avg_tally = member->score;
}
- if (switch_test_flag(member->conference, CFLAG_GAIN_CONTROL) && member->conference->avg_score && member->score &&
- switch_test_flag(member, MFLAG_TALKING)) {
- int diff = member->conference->avg_score - member->avg_score;
+ if (member->conference->agc_level && member->score &&
+ switch_test_flag(member, MFLAG_TALKING) &&
+ switch_test_flag(member, MFLAG_CAN_SPEAK) &&
+ member->score > member->energy_level
+ ) {
+ int diff = member->conference->agc_level - member->score;
if (abs(diff) >= 200) {
member->agc_concur++;
} else {
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 (diff > 200) {
+ if (member->score < member->conference->agc_level) {
member->agc_volume_in_level++;
- if (diff > 400) {
- member->agc_volume_in_level++;
- }
-
switch_normalize_volume(member->agc_volume_in_level);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7,
"conf %s AGC +++ %d %d %d %d %d\n",
member->conference->name,
- member->id, diff, member->conference->avg_score,
- member->avg_score, member->agc_volume_in_level);
+ member->id, diff, member->conference->agc_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--;
- 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_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->id, diff, member->conference->avg_score,
- member->avg_score, member->agc_volume_in_level);
+ member->id, diff, member->conference->agc_level,
+ member->score, member->agc_volume_in_level);
}
member->agc_concur = 0;
}
member->nt_tally = 0;
} else {
member->nt_tally++;
+ member->agc_concur = 0;
if (member->nt_tally > one_sec * 5) {
+ member->agc_volume_in_level = 0;
member->nt_tally = 0;
member->avg_itt = 0;
member->avg_tally = 0;
member->avg_score = member->score;
}
+
}
member->score_iir = (int) (((1.0 - SCORE_DECAY) * (float) member->score) + (SCORE_DECAY * (float) member->score_iir));
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) {
- stream->write_function(stream, "OK AGC ENABLED\n");
+ if (argc == 2) {
+ stream->write_function(stream, "+OK CURRENT AGC LEVEL IS %d\n", conference->agc_level);
+ 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 (!(on = !strcasecmp(argv[2], "on"))) {
+ stream->write_function(stream, "+OK AGC DISABLED\n");
+ conference->agc_level = 0;
+ return SWITCH_STATUS_SUCCESS;
+ }
- if (stream) {
- stream->write_function(stream, "OK AGC DISABLED\n");
+ 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;
+
}
static switch_status_t conf_api_sub_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
switch_xml_set_attr_d(x_conference, "dynamic", "true");
}
- if (switch_test_flag(conference, CFLAG_GAIN_CONTROL)) {
- switch_xml_set_attr_d(x_conference, "agc", "true");
+ if (conference->agc_level) {
+ 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);
{"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"},
{"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_off", (void_fn_t) & conf_api_sub_agc_off, CONF_API_SUB_ARGS_SPLIT, "<confname> agc_off"},
+ {"agc", (void_fn_t) & conf_api_sub_agc, CONF_API_SUB_ARGS_SPLIT, "<confname> agc"},
{"dial", (void_fn_t) & conf_api_sub_dial, CONF_API_SUB_ARGS_SPLIT,
"<confname> dial <endpoint_module_name>/<destination> <callerid number> <callerid name>"},
{"bgdial", (void_fn_t) & conf_api_sub_bgdial, CONF_API_SUB_ARGS_SPLIT,
*f |= CFLAG_VID_FLOOR;
} else if (!strcasecmp(argv[i], "waste-bandwidth")) {
*f |= CFLAG_WASTE_BANDWIDTH;
- } else if (!strcasecmp(argv[i], "auto-gain-control")) {
- *f |= CFLAG_GAIN_CONTROL;
}
}
char *pin_sound = NULL;
char *bad_pin_sound = NULL;
char *energy_level = NULL;
+ char *auto_gain_level = NULL;
char *caller_id_name = NULL;
char *caller_id_number = NULL;
char *caller_controls = NULL;
char *verbose_events = NULL;
char *auto_record = NULL;
char *terminate_on_silence = NULL;
-
+
/* Validate the conference name */
if (zstr(name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Record! no name.\n");
bad_pin_sound = val;
} else if (!strcasecmp(var, "energy-level") && !zstr(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)) {
caller_id_name = val;
} else if (!strcasecmp(var, "caller-id-number") && !zstr(val)) {
if (!zstr(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)) {