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;
}
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);
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;
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);
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;
+ }
+
}
}
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;
}
}
+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