From 96a8267305051061e32bc4ee4bdef3bdc19ba13c Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 18 Jan 2017 13:19:41 -0600 Subject: [PATCH] FS-9958: [freeswitch-core,mod_local_stream] Add agc object and use it in mod_local_stream #resolve --- src/include/switch_resample.h | 5 +- src/include/switch_types.h | 1 + .../mod_local_stream/mod_local_stream.c | 61 +++++++++ src/switch_resample.c | 120 ++++++++++++++++++ 4 files changed, 186 insertions(+), 1 deletion(-) diff --git a/src/include/switch_resample.h b/src/include/switch_resample.h index b3fb436c86..252f156ed7 100644 --- a/src/include/switch_resample.h +++ b/src/include/switch_resample.h @@ -177,7 +177,10 @@ SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, u #define switch_resample_calc_buffer_size(_to, _from, _srclen) ((uint32_t)(((float)_to / (float)_from) * (float)_srclen) * 2) - +SWITCH_DECLARE(switch_status_t) switch_agc_create(switch_agc_t **agcP, uint32_t energy_avg, uint32_t margin, uint32_t change_factor, uint32_t period_len); +SWITCH_DECLARE(void) switch_agc_destroy(switch_agc_t **agcP); +SWITCH_DECLARE(switch_status_t) switch_agc_feed(switch_agc_t *agc, int16_t *data, uint32_t samples, uint32_t channels); +SWITCH_DECLARE(void) switch_agc_set_energy_avg(switch_agc_t *agc, uint32_t energy_avg); SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_types.h b/src/include/switch_types.h index b2f91735a5..0bcb120284 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -2658,6 +2658,7 @@ typedef enum { struct switch_rtp_text_factory_s; typedef struct switch_rtp_text_factory_s switch_rtp_text_factory_t; +typedef struct switch_agc_s switch_agc_t; SWITCH_END_EXTERN_C #endif diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.c b/src/mod/formats/mod_local_stream/mod_local_stream.c index 3dcb6bf65e..5ab74f4950 100644 --- a/src/mod/formats/mod_local_stream/mod_local_stream.c +++ b/src/mod/formats/mod_local_stream/mod_local_stream.c @@ -93,6 +93,9 @@ struct local_stream_source { char *timer_name; local_stream_context_t *context_list; int total; + int vol; + switch_agc_t *agc; + int energy_avg; int first; switch_dir_t *dir_handle; switch_mutex_t *mutex; @@ -571,6 +574,13 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void } if (source->total) { + + if (source->energy_avg && source->agc) { + switch_agc_feed(source->agc, (int16_t *)source->abuf, olen, source->channels); + } else if (source->vol) { + switch_change_sln_volume_granular((int16_t *)source->abuf, olen * source->channels, source->vol); + } + switch_buffer_write(audio_buffer, source->abuf, olen * 2 * source->channels); } else { switch_buffer_zero(audio_buffer); @@ -720,6 +730,15 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); } + } else if (!strcasecmp(var, "volume")) { + source->vol = atoi(val); + switch_normalize_volume_granular(source->vol); + } else if (!strcasecmp(var, "auto-volume")) { + source->energy_avg = atoi(val); + + if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) { + source->energy_avg = 0; + } } if (source->chime_max) { source->chime_max *= source->rate; @@ -773,6 +792,10 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void switch_core_hash_delete(globals.source_hash, source->name); switch_mutex_unlock(globals.mutex); + if (source->agc) { + switch_agc_destroy(&source->agc); + } + switch_thread_rwlock_wrlock(source->rwlock); switch_thread_rwlock_unlock(source->rwlock); @@ -1247,6 +1270,16 @@ static void launch_thread(const char *name, const char *path, switch_xml_t direc if (source->text_opacity < 0 && source->text_opacity > 100) { source->text_opacity = 0; } + } else if (!strcasecmp(var, "volume")) { + source->vol = atoi(val); + switch_normalize_volume_granular(source->vol); + } else if (!strcasecmp(var, "auto-volume")) { + source->energy_avg = atoi(val); + + if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) { + source->energy_avg = 0; + } + } } @@ -1330,6 +1363,34 @@ SWITCH_STANDARD_API(local_stream_function) stream->write_function(stream, "+OK hup stream: %s", source->name); switch_thread_rwlock_unlock(source->rwlock); } + } else if (!strcasecmp(argv[0], "vol") && local_stream_name) { + if ((source = get_source(local_stream_name))) { + if (argv[2]) { + if (!strncasecmp(argv[2], "auto:", 5)) { + source->energy_avg = atoi(argv[2] + 5); + if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) { + source->energy_avg = 0; + stream->write_function(stream, "-ERR invalid auto-volume level for stream: %s\n", source->name); + } else { + if (!source->agc) { + switch_agc_create(&source->agc, source->energy_avg, 500, 3, (1000 / source->interval) * 2); + } else { + switch_agc_set_energy_avg(source->agc, source->energy_avg); + } + } + } else { + source->vol = atoi(argv[2]); + switch_normalize_volume_granular(source->vol); + } + } + + if (source->energy_avg) { + stream->write_function(stream, "+OK Auto-Volume stream: %s is %d", source->name, source->energy_avg); + } else { + stream->write_function(stream, "+OK vol stream: %s is %d", source->name, source->vol); + } + switch_thread_rwlock_unlock(source->rwlock); + } } else if (!strcasecmp(argv[0], "stop") && local_stream_name) { if ((source = get_source(local_stream_name))) { source->stopped = 1; diff --git a/src/switch_resample.c b/src/switch_resample.c index d9f041b55f..db099cd264 100644 --- a/src/switch_resample.c +++ b/src/switch_resample.c @@ -399,6 +399,126 @@ SWITCH_DECLARE(void) switch_change_sln_volume(int16_t *data, uint32_t samples, i } } +struct switch_agc_s { + switch_memory_pool_t *pool; + uint32_t energy_avg; + uint32_t margin; + uint32_t change_factor; + + int vol; + uint32_t score; + uint32_t score_count; + uint32_t score_sum; + uint32_t score_avg; + uint32_t score_over; + uint32_t score_under; + uint32_t period_len; +}; + + + +SWITCH_DECLARE(switch_status_t) switch_agc_create(switch_agc_t **agcP, uint32_t energy_avg, uint32_t margin, uint32_t change_factor, uint32_t period_len) +{ + switch_agc_t *agc; + switch_memory_pool_t *pool; + + switch_assert(agcP); + + switch_core_new_memory_pool(&pool); + + agc = switch_core_alloc(pool, sizeof(*agc)); + agc->pool = pool; + agc->energy_avg = energy_avg; + agc->margin = margin; + agc->change_factor = change_factor; + agc->period_len = period_len; + + *agcP = agc; + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(void) switch_agc_destroy(switch_agc_t **agcP) +{ + switch_agc_t *agc; + + switch_assert(agcP); + + agc = *agcP; + *agcP = NULL; + + if (agc) { + switch_memory_pool_t *pool = agc->pool; + switch_core_destroy_memory_pool(&pool); + } +} + +SWITCH_DECLARE(void) switch_agc_set_energy_avg(switch_agc_t *agc, uint32_t energy_avg) +{ + switch_assert(agc); + + agc->energy_avg = energy_avg; +} + +SWITCH_DECLARE(switch_status_t) switch_agc_feed(switch_agc_t *agc, int16_t *data, uint32_t samples, uint32_t channels) +{ + + if (!channels) channels = 1; + + if (agc->vol) { + switch_change_sln_volume_granular(data, samples * channels, agc->vol); + } + + if (agc->energy_avg) { + uint32_t energy = 0; + int i; + + for (i = 0; i < samples * channels; i++) { + energy += abs(data[i]); + } + + agc->score = energy / samples * channels; + agc->score_sum += agc->score; + agc->score_count++; + + if (agc->score_count > agc->period_len) { + + agc->score_avg = (int)((double)agc->score_sum / agc->score_count); + agc->score_count = 0; + agc->score_sum = 0; + + if (agc->score_avg > agc->energy_avg + agc->margin) { + agc->score_over++; + } else { + agc->score_over = 0; + } + + if (agc->score_avg < agc->energy_avg - agc->margin) { + agc->score_under++; + } else { + agc->score_under = 0; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "AVG %d over: %d under: %d\n", agc->score_avg, agc->score_over, agc->score_under); + + if (agc->score_over > agc->change_factor) { + agc->vol--; + switch_normalize_volume_granular(agc->vol); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "VOL DOWN %d\n", agc->vol); + } else if (agc->score_under > agc->change_factor) { + agc->vol++; + switch_normalize_volume_granular(agc->vol); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "VOL UP %d\n", agc->vol); + } + + } + + } + + return SWITCH_STATUS_SUCCESS; +} + + /* For Emacs: * Local Variables: * mode:c