diff --git a/conf/vanilla/autoload_configs/avmd.conf.xml b/conf/vanilla/autoload_configs/avmd.conf.xml index 2366195621..1afd67d14f 100644 --- a/conf/vanilla/autoload_configs/avmd.conf.xml +++ b/conf/vanilla/autoload_configs/avmd.conf.xml @@ -37,13 +37,16 @@ - + - + + + + + + + + diff --git a/src/mod/applications/mod_avmd/avmd_buffer.h b/src/mod/applications/mod_avmd/avmd_buffer.h index b7a6cbb233..3910fa1876 100644 --- a/src/mod/applications/mod_avmd/avmd_buffer.h +++ b/src/mod/applications/mod_avmd/avmd_buffer.h @@ -39,7 +39,7 @@ extern size_t next_power_of_2(size_t v); { \ (b)->pos++; \ (b)->pos &= (b)->mask; \ - (b)->lpos++; \ + (b)->lpos + 1 < 2 * (b)->buf_len ? (b)->lpos++ : (b)->lpos = (b)->buf_len; \ if ((b)->backlog < (b)->buf_len) (b)->backlog++; \ } diff --git a/src/mod/applications/mod_avmd/avmd_desa2_tweaked.c b/src/mod/applications/mod_avmd/avmd_desa2_tweaked.c index 6bf0b41348..2aa8785491 100644 --- a/src/mod/applications/mod_avmd/avmd_desa2_tweaked.c +++ b/src/mod/applications/mod_avmd/avmd_desa2_tweaked.c @@ -63,14 +63,14 @@ avmd_desa2_tweaked(circ_buffer_t *b, size_t i, double *amplitude) { we do simplified, modified for speed version : */ result = n/d; - if (ISINF(result)) { +/* if (ISINF(result)) { *amplitude = 0.0; if (n < 0.0) { return -10.0; } else { return 10.0; } - } + }*/ *amplitude = 2.0 * PSI_Xn / sqrt(PSI_Yn); return result; } diff --git a/src/mod/applications/mod_avmd/avmd_fir.h b/src/mod/applications/mod_avmd/avmd_fir.h index 23860f5887..25a2c23bb3 100644 --- a/src/mod/applications/mod_avmd/avmd_fir.h +++ b/src/mod/applications/mod_avmd/avmd_fir.h @@ -13,9 +13,9 @@ #define __AVMD_FIR_H__ -#define DESA_MAX(a, b) (a) > (b) ? (a) : (b) -#define MEDIAN_FILTER(a, b, c) (a) > (b) ? ((a) > (c) ? \ - DESA_MAX((b), (c)) : a) : ((b) > (c) ? DESA_MAX((a), (c)) : (b)) +#define AVMD_MAX(a, b) (a) > (b) ? (a) : (b) +#define AVMD_MEDIAN_FILTER(a, b, c) (a) > (b) ? ((a) > (c) ? \ + AVMD_MAX((b), (c)) : a) : ((b) > (c) ? AVMD_MAX((a), (c)) : (b)) #endif diff --git a/src/mod/applications/mod_avmd/avmd_sma_buf.h b/src/mod/applications/mod_avmd/avmd_sma_buf.h index 5c5c6a108a..e3051c9731 100644 --- a/src/mod/applications/mod_avmd/avmd_sma_buf.h +++ b/src/mod/applications/mod_avmd/avmd_sma_buf.h @@ -46,7 +46,7 @@ typedef struct { #define INC_SMA_POS(b) \ { \ - (b)->lpos++; \ + ((b)->lpos + 1 < 2 * (b)->len) ? ((b)->lpos++) : ((b)->lpos = (b)->len); \ (b)->pos = (b)->lpos % (b)->len; \ } diff --git a/src/mod/applications/mod_avmd/conf/autoload_configs/avmd.conf.xml b/src/mod/applications/mod_avmd/conf/autoload_configs/avmd.conf.xml index 2366195621..1afd67d14f 100644 --- a/src/mod/applications/mod_avmd/conf/autoload_configs/avmd.conf.xml +++ b/src/mod/applications/mod_avmd/conf/autoload_configs/avmd.conf.xml @@ -37,13 +37,16 @@ - + - + + + + + + + + diff --git a/src/mod/applications/mod_avmd/conf/avmd_test_dialplan.xml b/src/mod/applications/mod_avmd/conf/avmd_test_dialplan.xml new file mode 100644 index 0000000000..59099777ba --- /dev/null +++ b/src/mod/applications/mod_avmd/conf/avmd_test_dialplan.xml @@ -0,0 +1,782 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_avmd/mod_avmd.c b/src/mod/applications/mod_avmd/mod_avmd.c index a058f9e061..5bc48120a2 100644 --- a/src/mod/applications/mod_avmd/mod_avmd.c +++ b/src/mod/applications/mod_avmd/mod_avmd.c @@ -29,12 +29,14 @@ * beeps) using modified DESA-2 algorithm. */ + #include #include #include #include #include #include +#include #ifdef WIN32 #include @@ -52,32 +54,35 @@ int __isnan(double); #include "avmd_desa2_tweaked.h" #include "avmd_sma_buf.h" #include "avmd_options.h" +#include "avmd_fir.h" #include "avmd_fast_acosf.h" /*! Calculate how many audio samples per ms based on the rate */ -#define SAMPLES_PER_MS(r, m) ((r) / (1000/(m))) +#define AVMD_SAMPLES_PER_MS(r, m) ((r) / (1000/(m))) /*! Minimum beep length */ -#define BEEP_TIME (2) +#define AVMD_BEEP_TIME (4) /*! How often to evaluate the output of DESA-2 in ms */ -#define SINE_TIME (2*0.125) +#define AVMD_SINE_TIME (1*0.125) /*! How long in samples does DESA-2 results get evaluated */ -#define SINE_LEN(r) SAMPLES_PER_MS((r), SINE_TIME) +#define AVMD_SINE_LEN(r) AVMD_SAMPLES_PER_MS((r), AVMD_SINE_TIME) /*! How long in samples is the minimum beep length */ -#define BEEP_LEN(r) SAMPLES_PER_MS((r), BEEP_TIME) +#define AVMD_BEEP_LEN(r) AVMD_SAMPLES_PER_MS((r), AVMD_BEEP_TIME) /*! Number of points in DESA-2 sample */ -#define P (5) +#define AVMD_P (5) /*! Guesstimate frame length in ms */ -#define FRAME_TIME (20) +#define AVMD_FRAME_TIME (20) /*! Length in samples of the frame (guesstimate) */ -#define FRAME_LEN(r) SAMPLES_PER_MS((r), FRAME_TIME) +#define AVMD_FRAME_LEN(r) AVMD_SAMPLES_PER_MS((r), AVMD_FRAME_TIME) /*! Conversion to Hertz */ -#define TO_HZ(r, f) (((r) * (f)) / (2.0 * M_PI)) +#define AVMD_TO_HZ(r, f) (((r) * (f)) / (2.0 * M_PI)) +/*! Minimum absolute pressure/amplitude */ +#define AVMD_MIN_AMP (5.0) /*! Minimum beep frequency in Hertz */ -#define MIN_FREQUENCY (300.0) +#define AVMD_MIN_FREQUENCY (400.0) /*! Minimum frequency as digital normalized frequency */ -#define MIN_FREQUENCY_R(r) ((2.0 * M_PI * MIN_FREQUENCY) / (r)) +#define AVMD_MIN_FREQUENCY_R(r) ((2.0 * M_PI * AVMD_MIN_FREQUENCY) / (r)) /*! * Maximum beep frequency in Hertz * Note: The maximum frequency the DESA-2 algorithm can uniquely @@ -92,11 +97,12 @@ int __isnan(double); * In case of DESA-1, frequencies up to 0.5 sampling rate are * identified uniquely. */ -#define MAX_FREQUENCY (2500.0) +#define AVMD_MAX_FREQUENCY (2000.0) /*! Maximum frequency as digital normalized frequency */ -#define MAX_FREQUENCY_R(r) ((2.0 * M_PI * MAX_FREQUENCY) / (r)) -/* decrease this value to eliminate false positives */ -#define VARIANCE_THRESHOLD (0.00025) +#define AVMD_MAX_FREQUENCY_R(r) ((2.0 * M_PI * AVMD_MAX_FREQUENCY) / (r)) +#define AVMD_VARIANCE_RSD_THRESHOLD (0.000025) +#define AVMD_AMPLITUDE_RSD_THRESHOLD (0.015) +#define AVMD_DETECTORS_N 45 /*! Syntax of the API call. */ @@ -118,10 +124,11 @@ enum avmd_event AVMD_EVENT_SESSION_STOP = 2 }; /* This array MUST be NULL terminated! */ -const char* avmd_events_str[] = { [AVMD_EVENT_BEEP] = "avmd::beep", - [AVMD_EVENT_SESSION_START] = "avmd::start", - [AVMD_EVENT_SESSION_STOP] = "avmd::stop", - NULL /* MUST be last and always here */ +const char* avmd_events_str[] = { + [AVMD_EVENT_BEEP] = "avmd::beep", + [AVMD_EVENT_SESSION_START] = "avmd::start", + [AVMD_EVENT_SESSION_STOP] = "avmd::stop", + NULL /* MUST be last and always here */ }; #define AVMD_CHAR_BUF_LEN 20u @@ -134,6 +141,14 @@ enum avmd_app AVMD_APP_START_FUNCTION = 2 /* deprecated since version 1.6.8 */ }; +enum avmd_detection_mode +{ + AVMD_DETECT_AMP = 0, + AVMD_DETECT_FREQ = 1, + AVMD_DETECT_BOTH = 2, + AVMD_DETECT_NONE = 3 +}; + /* Prototypes */ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown); SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load); @@ -150,43 +165,76 @@ struct avmd_settings { uint8_t require_continuous_streak; uint16_t sample_n_continuous_streak; uint16_t sample_n_to_skip; + uint8_t require_continuous_streak_amp; + uint16_t sample_n_continuous_streak_amp; uint8_t simplified_estimation; uint8_t inbound_channnel; uint8_t outbound_channnel; + enum avmd_detection_mode mode; }; /*! Status of the beep detection */ typedef enum { - BEEP_DETECTED, - BEEP_NOTDETECTED + BEEP_DETECTED, + BEEP_NOTDETECTED } avmd_beep_state_t; /*! Data related to the current status of the beep */ typedef struct { - avmd_beep_state_t beep_state; - size_t last_beep; + avmd_beep_state_t beep_state; + size_t last_beep; } avmd_state_t; +struct avmd_session; +typedef struct avmd_session avmd_session_t; + +struct avmd_buffer { + sma_buffer_t sma_b; + sma_buffer_t sqa_b; + + sma_buffer_t sma_b_fir; + sma_buffer_t sqa_b_fir; + + sma_buffer_t sma_amp_b; + sma_buffer_t sqa_amp_b; + + uint8_t resolution; + uint8_t offset; + double amplitude_max; + size_t samples_streak, samples_streak_amp; /* number of DESA samples in single streak without reset needed to validate SMA estimator */ +}; + +struct avmd_detector { + switch_thread_t *thread; + switch_mutex_t *mutex; + uint8_t flag_processing_done; + uint8_t flag_should_exit; + enum avmd_detection_mode result; + switch_thread_cond_t *cond_start_processing; + struct avmd_buffer buffer; + avmd_session_t *s; + size_t samples; + uint8_t idx; +}; + /*! Type that holds session information pertinent to the avmd module. */ -typedef struct { - /*! Internal FreeSWITCH session. */ - switch_core_session_t *session; +struct avmd_session { + switch_core_session_t *session; switch_mutex_t *mutex; struct avmd_settings settings; - uint32_t rate; - circ_buffer_t b; - sma_buffer_t sma_b; - sma_buffer_t sqa_b; - sma_buffer_t sma_amp_b; - sma_buffer_t sqa_amp_b; - size_t pos; - double f; - /* freq_table_t ft; */ - avmd_state_t state; - switch_time_t start_time, stop_time, detection_start_time, detection_stop_time; - size_t samples_streak; /* number of DESA samples in single streak without reset needed to validate SMA estimator */ - size_t sample_count; -} avmd_session_t; + uint32_t rate; + circ_buffer_t b; + size_t pos; + double f; + avmd_state_t state; + switch_time_t start_time, stop_time, detection_start_time, detection_stop_time; + size_t sample_count; + uint8_t frame_n_to_skip; + + switch_mutex_t *mutex_detectors_done; + switch_thread_cond_t *cond_detectors_done; + struct avmd_detector detectors[AVMD_DETECTORS_N]; +}; struct avmd_globals { @@ -202,10 +250,11 @@ static switch_status_t avmd_register_all_events(void); static void avmd_unregister_all_events(void); -static void -avmd_fire_event(enum avmd_event type, switch_core_session_t *fs_s, double freq, double v_freq, double amp, double v_amp, avmd_beep_state_t beep_status, uint8_t info, +static void avmd_fire_event(enum avmd_event type, switch_core_session_t *fs_s, double freq, double v_freq, double amp, double v_amp, avmd_beep_state_t beep_status, uint8_t info, switch_time_t detection_start_time, switch_time_t detection_stop_time, switch_time_t start_time, switch_time_t stop_time); +static enum avmd_detection_mode avmd_process_sample(avmd_session_t *s, circ_buffer_t *b, size_t sample_n, size_t pos, struct avmd_detector *d); + /* API [set default], reset to factory settings */ static void avmd_set_xml_default_configuration(switch_mutex_t *mutex); /* API [set inbound], set inbound = 1, outbound = 0 */ @@ -226,16 +275,87 @@ static void avmd_reloadxml_event_handler(switch_event_t *event); /* API command */ static void avmd_show(switch_stream_handle_t *stream, switch_mutex_t *mutex); +static void* +avmd_detector_func(switch_thread_t *thread, void *arg); + +static uint8_t +avmd_detection_in_progress(avmd_session_t *s); + +static switch_status_t avmd_launch_threads(avmd_session_t *s) { + uint8_t idx; + switch_threadattr_t *thd_attr = NULL; + + idx = 0; + while (idx < AVMD_DETECTORS_N) { + s->detectors[idx].flag_processing_done = 1; + s->detectors[idx].flag_should_exit = 0; + s->detectors[idx].result = AVMD_DETECT_NONE; + switch_threadattr_create(&thd_attr, avmd_globals.pool); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + if (switch_thread_create(&s->detectors[idx].thread, thd_attr, avmd_detector_func, (void *)&s->detectors[idx], switch_core_session_get_pool(s->session)) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + ++idx; + } + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t avmd_init_buffer(struct avmd_buffer *b, size_t buf_sz, uint8_t resolution, uint8_t offset, switch_core_session_t *fs_session) { + INIT_SMA_BUFFER(&b->sma_b, buf_sz, fs_session); + if (b->sma_b.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sma_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + INIT_SMA_BUFFER(&b->sqa_b, buf_sz, fs_session); + if (b->sqa_b.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sqa_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + INIT_SMA_BUFFER(&b->sma_b_fir, buf_sz, fs_session); + if (b->sma_b_fir.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sma_b_fir.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + INIT_SMA_BUFFER(&b->sqa_b_fir, buf_sz, fs_session); + if (b->sqa_b_fir.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sqa_b_fir.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + INIT_SMA_BUFFER(&b->sma_amp_b, buf_sz, fs_session); + if (b->sma_amp_b.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sma_amp_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + INIT_SMA_BUFFER(&b->sqa_amp_b, buf_sz, fs_session); + if (b->sqa_amp_b.data == NULL) { + return SWITCH_STATUS_FALSE; + } + memset(b->sqa_amp_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); + + b->amplitude_max = 0.0; + b->samples_streak = 0; + b->samples_streak_amp = 0; + b->resolution = resolution; + b->offset = offset; + + return SWITCH_STATUS_SUCCESS; +} /*! \brief The avmd session data initialization function. * @param avmd_session A reference to a avmd session. * @param fs_session A reference to a FreeSWITCH session. * @details Avmd globals mutex must be locked. */ -static switch_status_t -init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session, switch_mutex_t *mutex) +static switch_status_t init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session, switch_mutex_t *mutex) { + uint8_t idx, resolution, offset; size_t buf_sz; + struct avmd_detector *d; switch_status_t status = SWITCH_STATUS_SUCCESS; if (mutex != NULL) @@ -243,58 +363,53 @@ init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_s switch_mutex_lock(mutex); } - /*! This is a worst case sample rate estimate */ - avmd_session->rate = 48000; - INIT_CIRC_BUFFER(&avmd_session->b, (size_t)BEEP_LEN(avmd_session->rate), (size_t)FRAME_LEN(avmd_session->rate), fs_session); + /*! This is a worst case sample rate estimate */ + avmd_session->rate = 48000; + INIT_CIRC_BUFFER(&avmd_session->b, (size_t) AVMD_BEEP_LEN(avmd_session->rate), (size_t) AVMD_FRAME_LEN(avmd_session->rate), fs_session); if (avmd_session->b.buf == NULL) { - status = SWITCH_STATUS_MEMERR; - goto end; + status = SWITCH_STATUS_MEMERR; + goto end; } - avmd_session->session = fs_session; - avmd_session->pos = 0; - avmd_session->f = 0.0; - avmd_session->state.last_beep = 0; - avmd_session->state.beep_state = BEEP_NOTDETECTED; - avmd_session->samples_streak = 0; + avmd_session->session = fs_session; + avmd_session->pos = 0; + avmd_session->f = 0.0; + avmd_session->state.last_beep = 0; + avmd_session->state.beep_state = BEEP_NOTDETECTED; memcpy(&avmd_session->settings, &avmd_globals.settings, sizeof(struct avmd_settings)); - switch_mutex_init(&avmd_session->mutex, SWITCH_MUTEX_DEFAULT, switch_core_session_get_pool(fs_session)); + switch_mutex_init(&avmd_session->mutex, SWITCH_MUTEX_DEFAULT, switch_core_session_get_pool(fs_session)); avmd_session->sample_count = 0; avmd_session->detection_start_time = 0; avmd_session->detection_stop_time = 0; + avmd_session->frame_n_to_skip = 0; - buf_sz = BEEP_LEN((uint32_t)avmd_session->rate) / (uint32_t)SINE_LEN(avmd_session->rate); + buf_sz = AVMD_BEEP_LEN((uint32_t)avmd_session->rate) / (uint32_t) AVMD_SINE_LEN(avmd_session->rate); if (buf_sz < 1) { - status = SWITCH_STATUS_MORE_DATA; - goto end; + status = SWITCH_STATUS_MORE_DATA; + goto end; } - - INIT_SMA_BUFFER(&avmd_session->sma_b, buf_sz, fs_session); - if (avmd_session->sma_b.data == NULL) { - status = SWITCH_STATUS_FALSE; - goto end; + idx = 0; + resolution = 0; + while (idx < AVMD_DETECTORS_N) { + ++resolution; + offset = 0; + while ((offset < resolution) && (idx < AVMD_DETECTORS_N)) { + d = &avmd_session->detectors[idx]; + if (avmd_init_buffer(&d->buffer, buf_sz, resolution, offset, fs_session) != SWITCH_STATUS_SUCCESS) { + status = SWITCH_STATUS_FALSE; + goto end; + } + d->s = avmd_session; + d->flag_processing_done = 1; + d->flag_should_exit = 1; + d->idx = idx; + switch_mutex_init(&d->mutex, SWITCH_MUTEX_DEFAULT, switch_core_session_get_pool(fs_session)); + switch_thread_cond_create(&d->cond_start_processing, switch_core_session_get_pool(fs_session)); + ++offset; + ++idx; + } } - memset(avmd_session->sma_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); - - INIT_SMA_BUFFER(&avmd_session->sqa_b, buf_sz, fs_session); - if (avmd_session->sqa_b.data == NULL) { - status = SWITCH_STATUS_FALSE; - goto end; - } - memset(avmd_session->sqa_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); - - INIT_SMA_BUFFER(&avmd_session->sma_amp_b, buf_sz, fs_session); - if (avmd_session->sma_amp_b.data == NULL) { - status = SWITCH_STATUS_FALSE; - goto end; - } - memset(avmd_session->sma_amp_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); - - INIT_SMA_BUFFER(&avmd_session->sqa_amp_b, buf_sz, fs_session); - if (avmd_session->sqa_amp_b.data == NULL) { - status = SWITCH_STATUS_FALSE; - goto end; - } - memset(avmd_session->sqa_amp_b.data, 0, sizeof(BUFF_TYPE) * buf_sz); + switch_mutex_init(&avmd_session->mutex_detectors_done, SWITCH_MUTEX_DEFAULT, switch_core_session_get_pool(fs_session)); + switch_thread_cond_create(&avmd_session->cond_detectors_done, switch_core_session_get_pool(fs_session)); end: if (mutex != NULL) { @@ -303,6 +418,37 @@ end: return status; } +static void avmd_session_close(avmd_session_t *s) { + uint8_t idx; + struct avmd_detector *d; + + switch_mutex_lock(s->mutex); + + switch_mutex_lock(s->mutex_detectors_done); + while (avmd_detection_in_progress(s) == 1) { + switch_thread_cond_wait(s->cond_detectors_done, s->mutex_detectors_done); + } + switch_mutex_unlock(s->mutex_detectors_done); + + idx = 0; + while (idx < AVMD_DETECTORS_N) { + d = &s->detectors[idx]; + switch_mutex_lock(d->mutex); + d = &s->detectors[idx]; + d->flag_processing_done = 0; + d->flag_should_exit = 1; + d->samples = 0; + switch_thread_cond_signal(d->cond_start_processing); + switch_mutex_unlock(d->mutex); + switch_mutex_destroy(d->mutex); + switch_thread_cond_destroy(d->cond_start_processing); + ++idx; + } + switch_mutex_unlock(s->mutex); + switch_mutex_destroy(s->mutex_detectors_done); + switch_thread_cond_destroy(s->cond_detectors_done); + switch_mutex_destroy(s->mutex); +} /*! \brief The callback function that is called when new audio data becomes available. * @param bug A reference to the media bug. @@ -310,99 +456,99 @@ end: * @param type The switch callback type. * @return The success or failure of the function. */ -static switch_bool_t -avmd_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type) -{ - avmd_session_t *avmd_session; - switch_codec_t *read_codec; +static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type) { + avmd_session_t *avmd_session; + switch_codec_t *read_codec; switch_codec_t *write_codec; - switch_frame_t *frame; + switch_frame_t *frame; switch_core_session_t *fs_session; - avmd_session = (avmd_session_t *) user_data; - if (avmd_session == NULL) { + avmd_session = (avmd_session_t *) user_data; + if (avmd_session == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No avmd session assigned!\n"); - return SWITCH_FALSE; - } - if (type != SWITCH_ABC_TYPE_INIT) { + return SWITCH_FALSE; + } + if ((type != SWITCH_ABC_TYPE_INIT) && (type != SWITCH_ABC_TYPE_CLOSE)) { switch_mutex_lock(avmd_session->mutex); } - fs_session = avmd_session->session; - if (fs_session == NULL) { + fs_session = avmd_session->session; + if (fs_session == NULL) { if (type != SWITCH_ABC_TYPE_INIT) { - switch_mutex_lock(avmd_session->mutex); + switch_mutex_unlock(avmd_session->mutex); } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No FreeSWITCH session assigned!\n"); - return SWITCH_FALSE; - } + return SWITCH_FALSE; + } - switch (type) { + switch (type) { - case SWITCH_ABC_TYPE_INIT: - if (avmd_session->settings.outbound_channnel == 1) { - read_codec = switch_core_session_get_read_codec(fs_session); - if (read_codec == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No read codec assigned, default session rate to 8000 samples/s\n"); - avmd_session->rate = 8000; - } else { - if (read_codec->implementation == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No read codec implementation assigned, default session rate to 8000 samples/s\n"); + case SWITCH_ABC_TYPE_INIT: + if (avmd_session->settings.outbound_channnel == 1) { + read_codec = switch_core_session_get_read_codec(fs_session); + if (read_codec == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No read codec assigned, default session rate to 8000 samples/s\n"); avmd_session->rate = 8000; } else { - avmd_session->rate = read_codec->implementation->samples_per_second; + if (read_codec->implementation == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No read codec implementation assigned, default session rate to 8000 samples/s\n"); + avmd_session->rate = 8000; + } else { + avmd_session->rate = read_codec->implementation->samples_per_second; + } } } - } - if (avmd_session->settings.inbound_channnel == 1) { - write_codec = switch_core_session_get_write_codec(fs_session); - if (write_codec == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No write codec assigned, default session rate to 8000 samples/s\n"); - avmd_session->rate = 8000; - } else { - if (write_codec->implementation == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No write codec implementation assigned, default session rate to 8000 samples/s\n"); + if (avmd_session->settings.inbound_channnel == 1) { + write_codec = switch_core_session_get_write_codec(fs_session); + if (write_codec == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No write codec assigned, default session rate to 8000 samples/s\n"); avmd_session->rate = 8000; } else { - avmd_session->rate = write_codec->implementation->samples_per_second; + if (write_codec->implementation == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_WARNING, "No write codec implementation assigned, default session rate to 8000 samples/s\n"); + avmd_session->rate = 8000; + } else { + avmd_session->rate = write_codec->implementation->samples_per_second; + } } } - } - avmd_session->start_time = switch_micro_time_now(); - /* avmd_session->vmd_codec.channels = read_codec->implementation->number_of_channels; */ - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session),SWITCH_LOG_INFO, "Avmd session initialized, [%u] samples/s\n", avmd_session->rate); - break; + avmd_session->start_time = switch_micro_time_now(); + /* avmd_session->vmd_codec.channels = read_codec->implementation->number_of_channels; */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session),SWITCH_LOG_INFO, "Avmd session initialized, [%u] samples/s\n", avmd_session->rate); + break; - case SWITCH_ABC_TYPE_READ_REPLACE: - frame = switch_core_media_bug_get_read_replace_frame(bug); - avmd_process(avmd_session, frame); - break; + case SWITCH_ABC_TYPE_READ_REPLACE: + frame = switch_core_media_bug_get_read_replace_frame(bug); + avmd_process(avmd_session, frame); + break; - case SWITCH_ABC_TYPE_WRITE_REPLACE: - frame = switch_core_media_bug_get_write_replace_frame(bug); - avmd_process(avmd_session, frame); - break; + case SWITCH_ABC_TYPE_WRITE_REPLACE: + frame = switch_core_media_bug_get_write_replace_frame(bug); + avmd_process(avmd_session, frame); + break; - default: - break; - } + case SWITCH_ABC_TYPE_CLOSE: + avmd_session_close(avmd_session); + break; - if (type != SWITCH_ABC_TYPE_INIT) { + default: + break; + } + + if ((type != SWITCH_ABC_TYPE_INIT) && (type != SWITCH_ABC_TYPE_CLOSE)) { switch_mutex_unlock(avmd_session->mutex); } - return SWITCH_TRUE; + return SWITCH_TRUE; } -static switch_status_t -avmd_register_all_events(void) -{ +static switch_status_t avmd_register_all_events(void) { size_t idx = 0; const char *e = avmd_events_str[0]; while (e != NULL) { if (switch_event_reserve_subclass(e) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass [%s]!\n", e); - return SWITCH_STATUS_TERM; + return SWITCH_STATUS_TERM; } ++idx; e = avmd_events_str[idx]; @@ -410,9 +556,7 @@ avmd_register_all_events(void) return SWITCH_STATUS_SUCCESS; } -static void -avmd_unregister_all_events(void) -{ +static void avmd_unregister_all_events(void) { size_t idx = 0; const char *e = avmd_events_str[0]; while (e != NULL) @@ -424,8 +568,7 @@ avmd_unregister_all_events(void) return; } -static void -avmd_fire_event(enum avmd_event type, switch_core_session_t *fs_s, double freq, double v_freq, double amp, double v_amp, avmd_beep_state_t beep_status, uint8_t info, +static void avmd_fire_event(enum avmd_event type, switch_core_session_t *fs_s, double freq, double v_freq, double amp, double v_amp, avmd_beep_state_t beep_status, uint8_t info, switch_time_t detection_start_time, switch_time_t detection_stop_time, switch_time_t start_time, switch_time_t stop_time) { int res; switch_event_t *event; @@ -519,11 +662,8 @@ avmd_fire_event(enum avmd_event type, switch_core_session_t *fs_s, double freq, return; } -int -avmd_parse_u8_user_input(const char *input, uint8_t *output, - uint8_t min, uint8_t max) -{ - char *pCh; +int avmd_parse_u8_user_input(const char *input, uint8_t *output, uint8_t min, uint8_t max) { + char *pCh; unsigned long helper; helper = strtoul(input, &pCh, 10); if (helper < min || helper > UINT8_MAX || helper > max || (pCh == input) || (*pCh != '\0')) { @@ -533,11 +673,8 @@ avmd_parse_u8_user_input(const char *input, uint8_t *output, return 0; } -int -avmd_parse_u16_user_input(const char *input, uint16_t *output, - uint16_t min, uint16_t max) -{ - char *pCh; +int avmd_parse_u16_user_input(const char *input, uint16_t *output, uint16_t min, uint16_t max) { + char *pCh; unsigned long helper; if (min > max) { return -1; @@ -550,8 +687,7 @@ avmd_parse_u16_user_input(const char *input, uint16_t *output, return 0; } -static void avmd_set_xml_default_configuration(switch_mutex_t *mutex) -{ +static void avmd_set_xml_default_configuration(switch_mutex_t *mutex) { if (mutex != NULL) { switch_mutex_lock(mutex); } @@ -560,20 +696,22 @@ static void avmd_set_xml_default_configuration(switch_mutex_t *mutex) avmd_globals.settings.report_status = 1; avmd_globals.settings.fast_math = 0; avmd_globals.settings.require_continuous_streak = 1; - avmd_globals.settings.sample_n_continuous_streak = 15; - avmd_globals.settings.sample_n_to_skip = 15; + avmd_globals.settings.sample_n_continuous_streak = 5; + avmd_globals.settings.sample_n_to_skip = 0; + avmd_globals.settings.require_continuous_streak_amp = 1; + avmd_globals.settings.sample_n_continuous_streak_amp = 5; avmd_globals.settings.simplified_estimation = 1; avmd_globals.settings.inbound_channnel = 0; avmd_globals.settings.outbound_channnel = 1; + avmd_globals.settings.mode = AVMD_DETECT_BOTH; if (mutex != NULL) { switch_mutex_unlock(avmd_globals.mutex); } - return; + return; } -static void -avmd_set_xml_inbound_configuration(switch_mutex_t *mutex) +static void avmd_set_xml_inbound_configuration(switch_mutex_t *mutex) { if (mutex != NULL) { switch_mutex_lock(mutex); @@ -585,12 +723,10 @@ avmd_set_xml_inbound_configuration(switch_mutex_t *mutex) if (mutex != NULL) { switch_mutex_unlock(avmd_globals.mutex); } - return; + return; } -static void -avmd_set_xml_outbound_configuration(switch_mutex_t *mutex) -{ +static void avmd_set_xml_outbound_configuration(switch_mutex_t *mutex) { if (mutex != NULL) { switch_mutex_lock(mutex); } @@ -601,77 +737,87 @@ avmd_set_xml_outbound_configuration(switch_mutex_t *mutex) if (mutex != NULL) { switch_mutex_unlock(avmd_globals.mutex); } - return; + return; } -static switch_status_t -avmd_load_xml_configuration(switch_mutex_t *mutex) -{ - switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL; - switch_status_t status = SWITCH_STATUS_FALSE; +static switch_status_t avmd_load_xml_configuration(switch_mutex_t *mutex) { + switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, cfg = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; - if (mutex != NULL) { + if (mutex != NULL) { switch_mutex_lock(mutex); } - if ((xml = switch_xml_open_cfg("avmd.conf", &cfg, NULL)) == NULL) { + if ((xml = switch_xml_open_cfg("avmd.conf", &cfg, NULL)) == NULL) { status = SWITCH_STATUS_TERM; } else { - status = SWITCH_STATUS_SUCCESS; + status = SWITCH_STATUS_SUCCESS; - if ((x_lists = switch_xml_child(cfg, "settings"))) { - for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) { - const char *name = switch_xml_attr(x_list, "name"); - const char *value = switch_xml_attr(x_list, "value"); + if ((x_lists = switch_xml_child(cfg, "settings"))) { + for (x_list = switch_xml_child(x_lists, "param"); x_list; x_list = x_list->next) { + const char *name = switch_xml_attr(x_list, "name"); + const char *value = switch_xml_attr(x_list, "value"); - if (zstr(name)) { - continue; - } - if (zstr(value)) { - continue; - } + if (zstr(name)) { + continue; + } + if (zstr(value)) { + continue; + } - if (!strcmp(name, "debug")) { - avmd_globals.settings.debug = switch_true(value) ? 1 : 0; + if (!strcmp(name, "debug")) { + avmd_globals.settings.debug = switch_true(value) ? 1 : 0; } else if (!strcmp(name, "report_status")) { - avmd_globals.settings.report_status = switch_true(value) ? 1 : 0; - } else if (!strcmp(name, "fast_math")) { - avmd_globals.settings.fast_math = switch_true(value) ? 1 : 0; - } else if (!strcmp(name, "require_continuous_streak")) { - avmd_globals.settings.require_continuous_streak = switch_true(value) ? 1 : 0; - } else if (!strcmp(name, "sample_n_continuous_streak")) { + avmd_globals.settings.report_status = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "fast_math")) { + avmd_globals.settings.fast_math = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "require_continuous_streak")) { + avmd_globals.settings.require_continuous_streak = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "sample_n_continuous_streak")) { if(avmd_parse_u16_user_input(value, &avmd_globals.settings.sample_n_continuous_streak, 0, UINT16_MAX) == -1) { status = SWITCH_STATUS_TERM; goto done; } - } else if (!strcmp(name, "sample_n_to_skip")) { + } else if (!strcmp(name, "sample_n_to_skip")) { if(avmd_parse_u16_user_input(value, &avmd_globals.settings.sample_n_to_skip, 0, UINT16_MAX) == -1) { status = SWITCH_STATUS_TERM; goto done; } - } else if (!strcmp(name, "simplified_estimation")) { - avmd_globals.settings.simplified_estimation = switch_true(value) ? 1 : 0; - } else if (!strcmp(name, "inbound_channel")) { - avmd_globals.settings.inbound_channnel = switch_true(value) ? 1 : 0; - } else if (!strcmp(name, "outbound_channel")) { - avmd_globals.settings.outbound_channnel = switch_true(value) ? 1 : 0; - } - } - } + } else if (!strcmp(name, "require_continuous_streak_amp")) { + avmd_globals.settings.require_continuous_streak_amp = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "sample_n_continuous_streak_amp")) { + if(avmd_parse_u16_user_input(value, &avmd_globals.settings.sample_n_continuous_streak_amp, 0, UINT16_MAX) == -1) { + status = SWITCH_STATUS_TERM; + goto done; + } + } else if (!strcmp(name, "simplified_estimation")) { + avmd_globals.settings.simplified_estimation = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "inbound_channel")) { + avmd_globals.settings.inbound_channnel = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "outbound_channel")) { + avmd_globals.settings.outbound_channnel = switch_true(value) ? 1 : 0; + } else if (!strcmp(name, "detection_mode")) { + if(avmd_parse_u8_user_input(value, (uint8_t*)&avmd_globals.settings.mode, 0, 2) == -1) { + status = SWITCH_STATUS_TERM; + goto done; + } + } + } + } - done: +done: - switch_xml_free(xml); - } + switch_xml_free(xml); + } if (mutex != NULL) { switch_mutex_unlock(mutex); } - return status; + return status; } -static switch_status_t avmd_load_xml_inbound_configuration(switch_mutex_t *mutex) -{ + +static switch_status_t avmd_load_xml_inbound_configuration(switch_mutex_t *mutex) { if (avmd_load_xml_configuration(mutex) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; } @@ -686,31 +832,28 @@ static switch_status_t avmd_load_xml_inbound_configuration(switch_mutex_t *mutex if (mutex != NULL) { switch_mutex_unlock(avmd_globals.mutex); } - return SWITCH_STATUS_SUCCESS; + return SWITCH_STATUS_SUCCESS; } -static switch_status_t avmd_load_xml_outbound_configuration(switch_mutex_t *mutex) -{ +static switch_status_t avmd_load_xml_outbound_configuration(switch_mutex_t *mutex) { if (avmd_load_xml_configuration(mutex) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_TERM; } - if (mutex != NULL) { + if (mutex != NULL) { switch_mutex_lock(mutex); } avmd_globals.settings.inbound_channnel = 0; avmd_globals.settings.outbound_channnel = 1; - if (mutex != NULL) { + if (mutex != NULL) { switch_mutex_unlock(avmd_globals.mutex); } - return SWITCH_STATUS_SUCCESS; + return SWITCH_STATUS_SUCCESS; } -static void -avmd_show(switch_stream_handle_t *stream, switch_mutex_t *mutex) -{ +static void avmd_show(switch_stream_handle_t *stream, switch_mutex_t *mutex) { const char *line = "================================================================================================="; if (stream == NULL) { return; @@ -723,15 +866,18 @@ avmd_show(switch_stream_handle_t *stream, switch_mutex_t *mutex) stream->write_function(stream, "\n\n"); stream->write_function(stream, "%s\n\n", line); stream->write_function(stream, "%s\n", "Avmd global settings\n\n"); - stream->write_function(stream, "debug \t%u\n", avmd_globals.settings.debug); - stream->write_function(stream, "report status \t%u\n", avmd_globals.settings.report_status); - stream->write_function(stream, "fast_math \t%u\n", avmd_globals.settings.fast_math); - stream->write_function(stream, "require continuous streak \t%u\n", avmd_globals.settings.require_continuous_streak); - stream->write_function(stream, "sample n continuous streak\t%u\n", avmd_globals.settings.sample_n_continuous_streak); - stream->write_function(stream, "sample n to skip \t%u\n", avmd_globals.settings.sample_n_to_skip); - stream->write_function(stream, "simplified estimation \t%u\n", avmd_globals.settings.simplified_estimation); - stream->write_function(stream, "inbound channel \t%u\n", avmd_globals.settings.inbound_channnel); - stream->write_function(stream, "outbound channel \t%u\n", avmd_globals.settings.outbound_channnel); + stream->write_function(stream, "debug \t%u\n", avmd_globals.settings.debug); + stream->write_function(stream, "report status \t%u\n", avmd_globals.settings.report_status); + stream->write_function(stream, "fast_math \t%u\n", avmd_globals.settings.fast_math); + stream->write_function(stream, "require continuous streak \t%u\n", avmd_globals.settings.require_continuous_streak); + stream->write_function(stream, "sample n continuous streak \t%u\n", avmd_globals.settings.sample_n_continuous_streak); + stream->write_function(stream, "sample n to skip \t%u\n", avmd_globals.settings.sample_n_to_skip); + stream->write_function(stream, "require continuous streak amp \t%u\n", avmd_globals.settings.require_continuous_streak_amp); + stream->write_function(stream, "sample n continuous streak amp \t%u\n", avmd_globals.settings.sample_n_continuous_streak_amp); + stream->write_function(stream, "simplified estimation \t%u\n", avmd_globals.settings.simplified_estimation); + stream->write_function(stream, "inbound channel \t%u\n", avmd_globals.settings.inbound_channnel); + stream->write_function(stream, "outbound channel \t%u\n", avmd_globals.settings.outbound_channnel); + stream->write_function(stream, "detection mode \t%u\n", avmd_globals.settings.mode); stream->write_function(stream, "\n\n"); if (mutex != NULL) { @@ -739,41 +885,39 @@ avmd_show(switch_stream_handle_t *stream, switch_mutex_t *mutex) } } -SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load) -{ +SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load) { #ifndef WIN32 char err[150]; int ret; #endif - switch_application_interface_t *app_interface; - switch_api_interface_t *api_interface; - /* connect my internal structure to the blank pointer passed to me */ - *module_interface = switch_loadable_module_create_module_interface(pool, modname); + switch_application_interface_t *app_interface; + switch_api_interface_t *api_interface; + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); - if (avmd_register_all_events() != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register avmd events!\n"); - return SWITCH_STATUS_TERM; - } + if (avmd_register_all_events() != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register avmd events!\n"); + return SWITCH_STATUS_TERM; + } memset(&avmd_globals, 0, sizeof(avmd_globals)); if (pool == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No memory pool assigned!\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No memory pool assigned!\n"); return SWITCH_STATUS_TERM; } - switch_mutex_init(&avmd_globals.mutex, SWITCH_MUTEX_DEFAULT, pool); + switch_mutex_init(&avmd_globals.mutex, SWITCH_MUTEX_DEFAULT, pool); avmd_globals.pool = pool; - if (avmd_load_xml_configuration(NULL) != SWITCH_STATUS_SUCCESS) - { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't load XML configuration! Loading default settings\n"); + if (avmd_load_xml_configuration(NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't load XML configuration! Loading default settings\n"); avmd_set_xml_default_configuration(NULL); } - if ((switch_event_bind(modname, SWITCH_EVENT_RELOADXML, NULL, avmd_reloadxml_event_handler, NULL) != SWITCH_STATUS_SUCCESS)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our reloadxml handler! Module will not react to changes made in XML configuration\n"); - /* Not so severe to prevent further loading, well - it depends, anyway */ - } + if ((switch_event_bind(modname, SWITCH_EVENT_RELOADXML, NULL, avmd_reloadxml_event_handler, NULL) != SWITCH_STATUS_SUCCESS)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our reloadxml handler! Module will not react to changes made in XML configuration\n"); + /* Not so severe to prevent further loading, well - it depends, anyway */ + } #ifndef WIN32 if (avmd_globals.settings.fast_math == 1) { @@ -783,77 +927,76 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load) switch (ret) { case -1: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't access file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't access file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); break; case -2: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); break; case -3: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Access rights are OK but can't open file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Access rights are OK but can't open file [%s], error [%s]\n", ACOS_TABLE_FILENAME, err); break; case -4: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Access rights are OK but can't mmap file [%s], error [%s]\n",ACOS_TABLE_FILENAME, err); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Access rights are OK but can't mmap file [%s], error [%s]\n",ACOS_TABLE_FILENAME, err); break; default: - switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR, "Unknown error [%d] while initializing fast cos table [%s], errno [%s]\n", ret, ACOS_TABLE_FILENAME, err); + switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR, "Unknown error [%d] while initializing fast cos table [%s], errno [%s]\n", ret, ACOS_TABLE_FILENAME, err); return SWITCH_STATUS_TERM; } return SWITCH_STATUS_TERM; } else - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Advanced voicemail detection: fast math enabled, arc cosine table is [%s]\n", ACOS_TABLE_FILENAME); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Advanced voicemail detection: fast math enabled, arc cosine table is [%s]\n", ACOS_TABLE_FILENAME); } #endif - SWITCH_ADD_APP(app_interface, "avmd_start","Start avmd detection", "Start avmd detection", avmd_start_app, "", SAF_NONE); - SWITCH_ADD_APP(app_interface, "avmd_stop","Stop avmd detection", "Stop avmd detection", avmd_stop_app, "", SAF_NONE); - SWITCH_ADD_APP(app_interface, "avmd","Beep detection", "Advanced detection of voicemail beeps", avmd_start_function, AVMD_SYNTAX, SAF_NONE); + SWITCH_ADD_APP(app_interface, "avmd_start","Start avmd detection", "Start avmd detection", avmd_start_app, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "avmd_stop","Stop avmd detection", "Stop avmd detection", avmd_stop_app, "", SAF_NONE); + SWITCH_ADD_APP(app_interface, "avmd","Beep detection", "Advanced detection of voicemail beeps", avmd_start_function, AVMD_SYNTAX, SAF_NONE); - SWITCH_ADD_API(api_interface, "avmd", "Voicemail beep detection", avmd_api_main, AVMD_SYNTAX); + SWITCH_ADD_API(api_interface, "avmd", "Voicemail beep detection", avmd_api_main, AVMD_SYNTAX); - switch_console_set_complete("add avmd ::console::list_uuid ::[start:stop"); - switch_console_set_complete("add avmd set inbound"); /* set inbound = 1, outbound = 0 */ - switch_console_set_complete("add avmd set outbound"); /* set inbound = 0, outbound = 1 */ - switch_console_set_complete("add avmd set default"); /* restore to factory settings */ - switch_console_set_complete("add avmd load inbound"); /* reload + set inbound */ - switch_console_set_complete("add avmd load outbound"); /* reload + set outbound */ - switch_console_set_complete("add avmd reload"); /* reload XML (it loads from FS installation + switch_console_set_complete("add avmd ::console::list_uuid ::[start:stop"); + switch_console_set_complete("add avmd set inbound"); /* set inbound = 1, outbound = 0 */ + switch_console_set_complete("add avmd set outbound"); /* set inbound = 0, outbound = 1 */ + switch_console_set_complete("add avmd set default"); /* restore to factory settings */ + switch_console_set_complete("add avmd load inbound"); /* reload + set inbound */ + switch_console_set_complete("add avmd load outbound"); /* reload + set outbound */ + switch_console_set_complete("add avmd reload"); /* reload XML (it loads from FS installation * folder, not module's conf/autoload_configs */ - switch_console_set_complete("add avmd show"); + switch_console_set_complete("add avmd show"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Advanced voicemail detection enabled\n"); - return SWITCH_STATUS_SUCCESS; /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; /* indicate that the module should continue to be loaded */ } -void -avmd_config_dump(avmd_session_t *s) -{ +void avmd_config_dump(avmd_session_t *s) { struct avmd_settings *settings; - if (s == NULL) return; + if (s == NULL) { + return; + } settings = &s->settings; - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, - "Avmd dynamic configuration: debug [%u], report_status [%u], fast_math [%u]," - " require_continuous_streak [%u], sample_n_continuous_streak [%u], " - "sample_n_to_skip [%u], simplified_estimation [%u], " - "inbound_channel [%u], outbound_channel [%u]\n", - settings->debug, settings->report_status, settings->fast_math, - settings->require_continuous_streak, settings->sample_n_continuous_streak, - settings->sample_n_to_skip, settings->simplified_estimation, - settings->inbound_channnel, settings->outbound_channnel); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, "Avmd dynamic configuration: debug [%u], report_status [%u], fast_math [%u]," + " require_continuous_streak [%u], sample_n_continuous_streak [%u], sample_n_to_skip [%u], require_continuous_streak_amp [%u], sample_n_continuous_streak_amp [%u]," + " simplified_estimation [%u], inbound_channel [%u], outbound_channel [%u], detection_mode [%u]\n", + settings->debug, settings->report_status, settings->fast_math, settings->require_continuous_streak, settings->sample_n_continuous_streak, + settings->sample_n_to_skip, settings->require_continuous_streak_amp, settings->sample_n_continuous_streak_amp, + settings->simplified_estimation, settings->inbound_channnel, settings->outbound_channnel, settings->mode); return; } -static switch_status_t -avmd_parse_cmd_data_one_entry(char *candidate, struct avmd_settings *settings) -{ +static switch_status_t avmd_parse_cmd_data_one_entry(char *candidate, struct avmd_settings *settings) { char *candidate_parsed[3]; int argc; const char *key; const char *val; - if (settings == NULL) return SWITCH_STATUS_TERM; - if (candidate == NULL) return SWITCH_STATUS_NOOP; + if (settings == NULL) { + return SWITCH_STATUS_TERM; + } + if (candidate == NULL) { + return SWITCH_STATUS_NOOP; + } argc = switch_separate_string(candidate, '=', candidate_parsed, (sizeof(candidate_parsed) / sizeof(candidate_parsed[0]))); if (argc > 2) { /* currently we accept only option=value syntax */ @@ -887,12 +1030,22 @@ avmd_parse_cmd_data_one_entry(char *candidate, struct avmd_settings *settings) if(avmd_parse_u16_user_input(val, &settings->sample_n_to_skip, 0, UINT16_MAX) == -1) { return SWITCH_STATUS_FALSE; } + } else if (!strcmp(key, "require_continuous_streak_amp")) { + settings->require_continuous_streak_amp = (uint8_t) switch_true(val); + } else if (!strcmp(key, "sample_n_continuous_streak_amp")) { + if(avmd_parse_u16_user_input(val, &settings->sample_n_continuous_streak_amp, 0, UINT16_MAX) == -1) { + return SWITCH_STATUS_FALSE; + } } else if (!strcmp(key, "simplified_estimation")) { settings->simplified_estimation = (uint8_t) switch_true(val); } else if (!strcmp(key, "inbound_channel")) { settings->inbound_channnel = (uint8_t) switch_true(val); } else if (!strcmp(key, "outbound_channel")) { settings->outbound_channnel = (uint8_t) switch_true(val); + } else if (!strcmp(key, "detection_mode")) { + if(avmd_parse_u8_user_input(val, (uint8_t*)&settings->mode, 0, 2) == -1) { + return SWITCH_STATUS_FALSE; + } } else { return SWITCH_STATUS_NOTFOUND; } @@ -903,21 +1056,19 @@ avmd_parse_cmd_data_one_entry(char *candidate, struct avmd_settings *settings) * if it returns SWITCH_STATUS_SUCCESS parsing went OK and avmd settings * are updated accordingly to @cmd_data, if SWITCH_STATUS_FALSE then * parsing error occurred and avmd session is left untouched */ -static switch_status_t -avmd_parse_cmd_data(avmd_session_t *s, const char *cmd_data, enum avmd_app app) -{ - char *mydata; +static switch_status_t avmd_parse_cmd_data(avmd_session_t *s, const char *cmd_data, enum avmd_app app) { + char *mydata; struct avmd_settings settings; - int argc = 0, idx; - char *argv[AVMD_PARAMS_APP_MAX * 2] = { 0 }; + int argc = 0, idx; + char *argv[AVMD_PARAMS_APP_MAX * 2] = { 0 }; switch_status_t status = SWITCH_STATUS_SUCCESS; if (s == NULL) { return SWITCH_STATUS_NOOP; } - if (zstr(cmd_data)) { - return SWITCH_STATUS_SUCCESS; - } + if (zstr(cmd_data)) { + return SWITCH_STATUS_SUCCESS; + } /* copy current settings first */ memcpy(&settings, &s->settings, sizeof (struct avmd_settings)); @@ -929,8 +1080,8 @@ avmd_parse_cmd_data(avmd_session_t *s, const char *cmd_data, enum avmd_app app) argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); if (argc < AVMD_PARAMS_APP_START_MIN || argc > AVMD_PARAMS_APP_START_MAX) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_ERROR, - "Syntax Error, avmd_start APP takes [%u] to [%u] parameters\n", - AVMD_PARAMS_APP_START_MIN, AVMD_PARAMS_APP_START_MAX); + "Syntax Error, avmd_start APP takes [%u] to [%u] parameters\n", + AVMD_PARAMS_APP_START_MIN, AVMD_PARAMS_APP_START_MAX); switch_goto_status(SWITCH_STATUS_MORE_DATA, fail); } /* iterate over params, check if they mean something to us, set */ @@ -982,7 +1133,7 @@ avmd_parse_cmd_data(avmd_session_t *s, const char *cmd_data, enum avmd_app app) goto end_copy; default: switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_ERROR, - "There is no app with index [%u] for avmd\n", app); + "There is no app with index [%u] for avmd\n", app); switch_goto_status(SWITCH_STATUS_NOTFOUND, fail); } @@ -994,50 +1145,49 @@ fail: return status; } -SWITCH_STANDARD_APP(avmd_start_app) -{ - switch_media_bug_t *bug; - switch_status_t status; - switch_channel_t *channel; - avmd_session_t *avmd_session; +SWITCH_STANDARD_APP(avmd_start_app) { + switch_media_bug_t *bug; + switch_status_t status; + switch_channel_t *channel; + avmd_session_t *avmd_session; switch_core_media_flag_t flags = 0; - if (session == NULL) { + if (session == NULL) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, - "FreeSWITCH is NULL! Please report to developers\n"); - return; - } + "FreeSWITCH is NULL! Please report to developers\n"); + return; + } - /* Get current channel of the session to tag the session. This indicates that our module is present - * At this moment this cannot return NULL, it will either succeed or assert failed, but we make ourself secure anyway */ - channel = switch_core_session_get_channel(session); - if (channel == NULL) { + /* Get current channel of the session to tag the session. This indicates that our module is present + * At this moment this cannot return NULL, it will either succeed or assert failed, but we make ourself secure anyway */ + channel = switch_core_session_get_channel(session); + if (channel == NULL) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No channel for FreeSWITCH session! Please report this " "to the developers.\n"); return; - } - - bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); /* Is this channel already set? */ - if (bug != NULL) { /* We have already started */ - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), - SWITCH_LOG_ERROR, "Avmd already started!\n"); - return; - } - - /* Allocate memory attached to this FreeSWITCH session for use in the callback routine and to store state information */ - avmd_session = (avmd_session_t *) switch_core_session_alloc(session, sizeof(avmd_session_t)); - if (avmd_session == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't allocate memory for avmd session!\n"); - goto end; } - status = init_avmd_session_data(avmd_session, session, NULL); + bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); /* Is this channel already set? */ + if (bug != NULL) { /* We have already started */ + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), + SWITCH_LOG_ERROR, "Avmd already started!\n"); + return; + } + + /* Allocate memory attached to this FreeSWITCH session for use in the callback routine and to store state information */ + avmd_session = (avmd_session_t *) switch_core_session_alloc(session, sizeof(avmd_session_t)); + if (avmd_session == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't allocate memory for avmd session!\n"); + goto end; + } + + status = init_avmd_session_data(avmd_session, session, avmd_globals.mutex); if (status != SWITCH_STATUS_SUCCESS) { switch (status) { case SWITCH_STATUS_MEMERR: switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to init avmd session. Buffer error!\n"); - break; + break; case SWITCH_STATUS_MORE_DATA: switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to init avmd session. SMA buffer size is 0!\n"); break; @@ -1073,7 +1223,7 @@ SWITCH_STANDARD_APP(avmd_start_app) } if (avmd_session->settings.outbound_channnel == 1) { if (SWITCH_CALL_DIRECTION_OUTBOUND != switch_channel_direction(channel)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel [%s] is not outbound!\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel [%s] is not outbound!\n", switch_channel_get_name(channel)); goto end; } else { flags |= SMBF_READ_REPLACE; @@ -1081,79 +1231,76 @@ SWITCH_STANDARD_APP(avmd_start_app) } if (avmd_session->settings.inbound_channnel == 1) { if (SWITCH_CALL_DIRECTION_INBOUND != switch_channel_direction(channel)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel [%s] is not inbound!\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel [%s] is not inbound!\n", switch_channel_get_name(channel)); goto end; } else { flags |= SMBF_WRITE_REPLACE; } } if (flags == 0) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't set direction for channel [%s]\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't set direction for channel [%s]\n", switch_channel_get_name(channel)); goto end; } if (avmd_session->settings.outbound_channnel == 1) { if (switch_channel_test_flag(channel, CF_MEDIA_SET) == 0) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to start session. Channel [%s] has no codec assigned yet." - " Please try again\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to start session. Channel [%s] has no codec assigned yet." + " Please try again\n", switch_channel_get_name(channel)); goto end; } } - status = switch_core_media_bug_add(session, "avmd", NULL, avmd_callback, avmd_session, 0, flags, &bug); /* Add a media bug that allows me to intercept the reading leg of the audio stream */ - if (status != SWITCH_STATUS_SUCCESS) { /* If adding a media bug fails exit */ + status = switch_core_media_bug_add(session, "avmd", NULL, avmd_callback, avmd_session, 0, flags, &bug); /* Add a media bug that allows me to intercept the reading leg of the audio stream */ + if (status != SWITCH_STATUS_SUCCESS) { /* If adding a media bug fails exit */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to add media bug!\n"); goto end; - } - switch_channel_set_private(channel, "_avmd_", bug); /* Set the avmd tag to detect an existing avmd media bug */ + } + switch_channel_set_private(channel, "_avmd_", bug); /* Set the avmd tag to detect an existing avmd media bug */ avmd_fire_event(AVMD_EVENT_SESSION_START, session, 0, 0, 0, 0, 0, 0, 0, 0, avmd_session->start_time, 0); if (avmd_session->settings.report_status == 1) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Avmd on channel [%s] started!\n", switch_channel_get_name(channel)); } + status = avmd_launch_threads(avmd_session); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to start detection threads!\n"); + goto end; + } + end: switch_mutex_unlock(avmd_session->mutex); return; } -SWITCH_STANDARD_APP(avmd_stop_app) -{ - switch_media_bug_t *bug; - switch_channel_t *channel; +SWITCH_STANDARD_APP(avmd_stop_app) { + switch_media_bug_t *bug; + switch_channel_t *channel; avmd_session_t *avmd_session; switch_time_t start_time, stop_time, total_time; uint8_t report_status = 0; avmd_beep_state_t beep_status = BEEP_NOTDETECTED; - if (session == NULL) { + if (session == NULL) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "FreeSWITCH is NULL! Please report to developers\n"); - return; - } - - /* Get current channel of the session to tag the session - * This indicates that our module is present - * At this moment this cannot return NULL, it will either - * succeed or assert failed, but we make ourself secure anyway */ - channel = switch_core_session_get_channel(session); - if (channel == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No channel for FreeSWITCH session! Please report this " - "to the developers.\n"); return; - } + } - /* Is this channel already set? */ - bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); - /* If yes */ - if (bug == NULL) { - /* We have not started avmd on this channel */ - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Stop failed - no avmd session running" - " on this channel [%s]!\n", switch_channel_get_name(channel)); + /* Get current channel of the session to tag the session. This indicates that our module is present + * At this moment this cannot return NULL, it will either succeed or assert failed, but we make ourself secure anyway */ + channel = switch_core_session_get_channel(session); + if (channel == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No channel for FreeSWITCH session! Please report this to the developers.\n"); return; - } + } + + bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); + if (bug == NULL) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Stop failed - no avmd session running on this channel [%s]!\n", switch_channel_get_name(channel)); + return; + } avmd_session = switch_core_media_bug_get_user_data(bug); if (avmd_session == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Stop failed - no avmd session object, stop event not fired" - " on this channel [%s]!\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Stop failed - no avmd session object, stop event not fired on this channel [%s]!\n", switch_channel_get_name(channel)); } else { switch_mutex_lock(avmd_session->mutex); report_status = avmd_session->settings.report_status; @@ -1163,11 +1310,10 @@ SWITCH_STANDARD_APP(avmd_stop_app) stop_time = avmd_session->stop_time; total_time = stop_time - start_time; switch_mutex_unlock(avmd_session->mutex); - avmd_fire_event(AVMD_EVENT_SESSION_STOP, session, 0, 0, 0, 0, beep_status, 1, 0, 0, start_time, stop_time); - if (report_status == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Avmd on channel [%s] stopped, beep status: [%s], total running time [%" PRId64 "] [us]\n", - switch_channel_get_name(channel), beep_status == BEEP_DETECTED ? "DETECTED" : "NOTDETECTED", total_time); - } + avmd_fire_event(AVMD_EVENT_SESSION_STOP, session, 0, 0, 0, 0, beep_status, 1, 0, 0, start_time, stop_time); + if (report_status == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Avmd on channel [%s] stopped, beep status: [%s], total running time [%" PRId64 "] [us]\n", switch_channel_get_name(channel), beep_status == BEEP_DETECTED ? "DETECTED" : "NOTDETECTED", total_time); + } } switch_channel_set_private(channel, "_avmd_", NULL); switch_core_media_bug_remove(session, &bug); @@ -1177,45 +1323,34 @@ SWITCH_STANDARD_APP(avmd_stop_app) /*! \brief FreeSWITCH application handler function. * This handles calls made from applications such as LUA and the dialplan. */ -SWITCH_STANDARD_APP(avmd_start_function) -{ - switch_media_bug_t *bug; - switch_channel_t *channel; +SWITCH_STANDARD_APP(avmd_start_function) { + switch_media_bug_t *bug; + switch_channel_t *channel; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "YOU ARE USING DEPRECATED APP INTERFACE." - " Please read documentation about new syntax\n"); - if (session == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "No FreeSWITCH session assigned!\n"); - return; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "YOU ARE USING DEPRECATED APP INTERFACE. Please read documentation about new syntax\n"); + if (session == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No FreeSWITCH session assigned!\n"); + return; } - channel = switch_core_session_get_channel(session); + channel = switch_core_session_get_channel(session); - /* Is this channel already using avmd ? */ - bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); - /* If it is using avmd */ - if (bug != NULL) { - /* If we have a stop remove audio bug */ - if (strcasecmp(data, "stop") == 0) { - switch_channel_set_private(channel, "_avmd_", NULL); - switch_core_media_bug_remove(session, &bug); - return; - } - /* We have already started */ - switch_log_printf( - SWITCH_CHANNEL_SESSION_LOG(session), - SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n"); - return; - } + bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); + if (bug != NULL) { + if (strcasecmp(data, "stop") == 0) { + switch_channel_set_private(channel, "_avmd_", NULL); + switch_core_media_bug_remove(session, &bug); + return; + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n"); + return; + } avmd_start_app(session, NULL); } -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown) -{ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown) { #ifndef WIN32 - int res; + int res; #endif switch_mutex_lock(avmd_globals.mutex); @@ -1224,42 +1359,36 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown) #ifndef WIN32 if (avmd_globals.settings.fast_math == 1) { - res = destroy_fast_acosf(); + res = destroy_fast_acosf(); if (res != 0) { switch (res) { case -1: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Failed unmap arc cosine table\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed unmap arc cosine table\n"); break; case -2: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Failed closing arc cosine table\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed closing arc cosine table\n"); break; default: - break; + break; } } } #endif - switch_event_unbind_callback(avmd_reloadxml_event_handler); - + switch_event_unbind_callback(avmd_reloadxml_event_handler); switch_mutex_unlock(avmd_globals.mutex); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, - "Advanced voicemail detection disabled\n"); - - return SWITCH_STATUS_SUCCESS; + switch_mutex_destroy(avmd_globals.mutex); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Advanced voicemail detection disabled\n"); + return SWITCH_STATUS_SUCCESS; } /*! \brief FreeSWITCH API handler function. * This function handles API calls from mod_event_socket and LUA scripts. */ -SWITCH_STANDARD_API(avmd_api_main) -{ - switch_media_bug_t *bug; - avmd_session_t *avmd_session; - switch_channel_t *channel; +SWITCH_STANDARD_API(avmd_api_main) { + switch_media_bug_t *bug; + avmd_session_t *avmd_session; + switch_channel_t *channel; int argc; const char *uuid, *uuid_dup; const char *command; @@ -1270,25 +1399,19 @@ SWITCH_STANDARD_API(avmd_api_main) switch_mutex_lock(avmd_globals.mutex); - /* No command? Display usage */ - if (zstr(cmd)) { - stream->write_function(stream, "-ERR, bad command!\n-USAGE: %s\n\n", AVMD_SYNTAX); - goto end; - } + if (zstr(cmd)) { + stream->write_function(stream, "-ERR, bad command!\n-USAGE: %s\n\n", AVMD_SYNTAX); + goto end; + } - /* Duplicated contents of original string */ - dupped = strdup(cmd); - switch_assert(dupped); - /* Separate the arguments */ - argc = switch_separate_string((char*)dupped, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + dupped = strdup(cmd); + switch_assert(dupped); + argc = switch_separate_string((char*)dupped, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); - /* If we don't have the expected number of parameters - * display usage */ - if (argc < AVMD_PARAMS_API_MIN) { - stream->write_function(stream, "-ERR, avmd takes [%u] min and [%u] max parameters!\n" - "-USAGE: %s\n\n", AVMD_PARAMS_API_MIN, AVMD_PARAMS_API_MAX, AVMD_SYNTAX); - goto end; - } + if (argc < AVMD_PARAMS_API_MIN) { + stream->write_function(stream, "-ERR, avmd takes [%u] min and [%u] max parameters!\n-USAGE: %s\n\n", AVMD_PARAMS_API_MIN, AVMD_PARAMS_API_MAX, AVMD_SYNTAX); + goto end; + } command = argv[0]; if (strcasecmp(command, "reload") == 0) { @@ -1376,72 +1499,58 @@ SWITCH_STANDARD_API(avmd_api_main) goto end; } - uuid = argv[0]; - command = argv[1]; + uuid = argv[0]; + command = argv[1]; - /* using uuid locate a reference to the FreeSWITCH session */ - fs_session = switch_core_session_locate(uuid); + fs_session = switch_core_session_locate(uuid); /* using uuid locate a reference to the FreeSWITCH session */ + if (fs_session == NULL) { + stream->write_function(stream, "-ERR, no FreeSWITCH session for uuid [%s]!\n-USAGE: %s\n\n", uuid, AVMD_SYNTAX); + goto end; + } - /* If the session was not found exit */ - if (fs_session == NULL) { - stream->write_function(stream, "-ERR, no FreeSWITCH session for uuid [%s]!\n-USAGE: %s\n\n", uuid, AVMD_SYNTAX); - goto end; - } + /* Get current channel of the session to tag the session. This indicates that our module is present + * At this moment this cannot return NULL, it will either succeed or assert failed, but we make ourself secure anyway */ + channel = switch_core_session_get_channel(fs_session); + if (channel == NULL) { + stream->write_function(stream, "-ERR, no channel for FreeSWITCH session [%s]!\n Please report this to the developers\n\n", uuid); + goto end; + } - /* Get current channel of the session to tag the session - * This indicates that our module is present - * At this moment this cannot return NULL, it will either - * succeed or assert failed, but we make ourself secure anyway */ - channel = switch_core_session_get_channel(fs_session); - if (channel == NULL) { - stream->write_function(stream, "-ERR, no channel for FreeSWITCH session [%s]!" - "\n Please report this to the developers\n\n", uuid); - goto end; - } - - /* Is this channel already set? */ - bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); - /* If yes */ - if (bug != NULL) { - /* If we have a stop remove audio bug */ - if (strcasecmp(command, "stop") == 0) { + bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_avmd_"); + if (bug != NULL) { + if (strcasecmp(command, "stop") == 0) { avmd_session = (avmd_session_t*) switch_core_media_bug_get_user_data(bug); if (avmd_session == NULL) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Stop failed - no avmd session object" - " on this channel [%s]!\n", switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Stop failed - no avmd session object on this channel [%s]!\n", switch_channel_get_name(channel)); goto end; } uuid_dup = switch_core_strdup(switch_core_session_get_pool(fs_session), uuid); - switch_channel_set_private(channel, "_avmd_", NULL); - switch_core_media_bug_remove(fs_session, &bug); + switch_channel_set_private(channel, "_avmd_", NULL); + switch_core_media_bug_remove(fs_session, &bug); avmd_fire_event(AVMD_EVENT_SESSION_STOP, fs_session, 0, 0, 0, 0, 0, 0, 0, 0, avmd_session->start_time, avmd_session->stop_time); if (avmd_globals.settings.report_status == 1) { - stream->write_function(stream, "+OK\n [%s] [%s] stopped\n\n", uuid_dup, switch_channel_get_name(channel)); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_INFO, "Avmd on channel [%s] stopped!\n", switch_channel_get_name(channel)); + stream->write_function(stream, "+OK\n [%s] [%s] stopped\n\n", uuid_dup, switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_INFO, "Avmd on channel [%s] stopped!\n", switch_channel_get_name(channel)); } - goto end; - } - if (avmd_globals.settings.report_status == 1) { - /* We have already started */ - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Avmd already started!\n"); - stream->write_function(stream, "-ERR, avmd for FreeSWITCH session [%s]\n already started\n\n", uuid); + goto end; } - goto end; - } + if (avmd_globals.settings.report_status == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Avmd already started!\n"); + stream->write_function(stream, "-ERR, avmd for FreeSWITCH session [%s]\n already started\n\n", uuid); + } + goto end; + } - if (strcasecmp(command, "stop") == 0) { + if (strcasecmp(command, "stop") == 0) { uuid_dup = switch_core_strdup(switch_core_session_get_pool(fs_session), uuid); - stream->write_function(stream, "+ERR, avmd has not yet been started on\n" - " [%s] [%s]\n\n", uuid_dup, switch_channel_get_name(channel)); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, - "Stop failed - avmd has not yet been started on channel [%s]!\n", switch_channel_get_name(channel)); + stream->write_function(stream, "+ERR, avmd has not yet been started on\n [%s] [%s]\n\n", uuid_dup, switch_channel_get_name(channel)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Stop failed - avmd has not yet been started on channel [%s]!\n", switch_channel_get_name(channel)); goto end; } if (avmd_globals.settings.outbound_channnel == 1) { if (SWITCH_CALL_DIRECTION_OUTBOUND != switch_channel_direction(channel)) { - stream->write_function(stream, "-ERR, channel for FreeSWITCH session [%s]" - "\n is not outbound\n\n", uuid); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Channel [%s] is not outbound!\n", switch_channel_get_name(channel)); + stream->write_function(stream, "-ERR, channel for FreeSWITCH session [%s]\n is not outbound\n\n", uuid); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Channel [%s] is not outbound!\n", switch_channel_get_name(channel)); goto end; } else { flags |= SMBF_READ_REPLACE; @@ -1449,89 +1558,208 @@ SWITCH_STANDARD_API(avmd_api_main) } if (avmd_globals.settings.inbound_channnel == 1) { if (SWITCH_CALL_DIRECTION_INBOUND != switch_channel_direction(channel)) { - stream->write_function(stream, "-ERR, channel for FreeSWITCH session [%s]" - "\n is not inbound\n\n", uuid); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Channel [%s] is not inbound!\n", switch_channel_get_name(channel)); + stream->write_function(stream, "-ERR, channel for FreeSWITCH session [%s]\n is not inbound\n\n", uuid); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Channel [%s] is not inbound!\n", switch_channel_get_name(channel)); goto end; } else { flags |= SMBF_WRITE_REPLACE; } } if (flags == 0) { - stream->write_function(stream, "-ERR, can't set direction for channel [%s]\n" - " for FreeSWITCH session [%s]. Please check avmd configuration\n\n", - switch_channel_get_name(channel), uuid); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, - "Can't set direction for channel [%s]\n", switch_channel_get_name(channel)); + stream->write_function(stream, "-ERR, can't set direction for channel [%s]\n for FreeSWITCH session [%s]. Please check avmd configuration\n\n", switch_channel_get_name(channel), uuid); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Can't set direction for channel [%s]\n", switch_channel_get_name(channel)); goto end; } if (avmd_globals.settings.outbound_channnel == 1) { if (switch_channel_test_flag(channel, CF_MEDIA_SET) == 0) { - stream->write_function(stream, "-ERR, channel [%s] for FreeSWITCH session [%s]" - "\n has no read codec assigned yet. Please try again.\n\n", switch_channel_get_name(channel), uuid); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, - "Failed to start session. Channel [%s] has no codec assigned yet. Please try again\n", switch_channel_get_name(channel)); + stream->write_function(stream, "-ERR, channel [%s] for FreeSWITCH session [%s]\n has no read codec assigned yet. Please try again.\n\n", switch_channel_get_name(channel), uuid); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to start session. Channel [%s] has no codec assigned yet. Please try again\n", switch_channel_get_name(channel)); goto end; } } - if (strcasecmp(command, "start") != 0) { /* If we don't see the expected start exit */ - stream->write_function(stream, "-ERR, did you mean\n" - " api avmd %s start ?\n-USAGE: %s\n\n", uuid, AVMD_SYNTAX); - goto end; - } + if (strcasecmp(command, "start") != 0) { /* If we don't see the expected start exit */ + stream->write_function(stream, "-ERR, did you mean\n api avmd %s start ?\n-USAGE: %s\n\n", uuid, AVMD_SYNTAX); + goto end; + } avmd_session = (avmd_session_t *) switch_core_session_alloc(fs_session, sizeof(avmd_session_t)); /* Allocate memory attached to this FreeSWITCH session for use in the callback routine and to store state information */ status = init_avmd_session_data(avmd_session, fs_session, NULL); if (status != SWITCH_STATUS_SUCCESS) { - stream->write_function(stream, "-ERR, failed to initialize avmd session\n for FreeSWITCH session [%s]\n", uuid); + stream->write_function(stream, "-ERR, failed to initialize avmd session\n for FreeSWITCH session [%s]\n", uuid); switch (status) { case SWITCH_STATUS_MEMERR: - stream->write_function(stream, "-ERR, buffer error\n\n"); + stream->write_function(stream, "-ERR, buffer error\n\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to init avmd session. Buffer error!\n"); - break; + break; case SWITCH_STATUS_MORE_DATA: - stream->write_function(stream, "-ERR, SMA buffer size is 0\n\n"); + stream->write_function(stream, "-ERR, SMA buffer size is 0\n\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to init avmd session. SMA buffer size is 0!\n"); break; case SWITCH_STATUS_FALSE: - stream->write_function(stream, "-ERR, SMA buffer error\n\n"); + stream->write_function(stream, "-ERR, SMA buffer error\n\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to init avmd session. SMA buffers error\n"); break; default: - stream->write_function(stream, "-ERR, unknown error\n\n"); + stream->write_function(stream, "-ERR, unknown error\n\n"); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to init avmd session. Unknown error\n"); break; } - goto end; + goto end; } - status = switch_core_media_bug_add(fs_session, "avmd", NULL, avmd_callback, avmd_session, 0, flags, &bug); /* Add a media bug that allows me to intercept the reading leg of the audio stream */ + status = switch_core_media_bug_add(fs_session, "avmd", NULL, avmd_callback, avmd_session, 0, flags, &bug); /* Add a media bug that allows me to intercept the reading leg of the audio stream */ - if (status != SWITCH_STATUS_SUCCESS) { /* If adding a media bug fails exit */ + if (status != SWITCH_STATUS_SUCCESS) { /* If adding a media bug fails exit */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_ERROR, "Failed to add media bug!\n"); - stream->write_function(stream, "-ERR, [%s] failed to add media bug!\n\n", uuid); - goto end; - } + stream->write_function(stream, "-ERR, [%s] failed to add media bug!\n\n", uuid); + goto end; + } - switch_channel_set_private(channel, "_avmd_", bug); /* Set the vmd tag to detect an existing vmd media bug */ + switch_channel_set_private(channel, "_avmd_", bug); /* Set the vmd tag to detect an existing vmd media bug */ avmd_fire_event(AVMD_EVENT_SESSION_START, fs_session, 0, 0, 0, 0, 0, 0, 0, 0, avmd_session->start_time, 0); if (avmd_globals.settings.report_status == 1) { - stream->write_function(stream, "+OK\n [%s] [%s] started!\n\n", uuid, switch_channel_get_name(channel)); + stream->write_function(stream, "+OK\n [%s] [%s] started!\n\n", uuid, switch_channel_get_name(channel)); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(fs_session), SWITCH_LOG_INFO, "Avmd on channel [%s] started!\n", switch_channel_get_name(channel)); switch_assert(status == SWITCH_STATUS_SUCCESS); } end: - if (fs_session) { - switch_core_session_rwunlock(fs_session); - } + if (fs_session) { + switch_core_session_rwunlock(fs_session); + } - switch_safe_free(dupped); + switch_safe_free(dupped); switch_mutex_unlock(avmd_globals.mutex); - return SWITCH_STATUS_SUCCESS; + return SWITCH_STATUS_SUCCESS; +} + +static int +avmd_decision_amplitude(const avmd_session_t *s, const struct avmd_buffer *b, double v, double rsd_threshold) { + double rsd; + + if ((s->settings.require_continuous_streak_amp == 1 && (b->sma_amp_b.lpos > s->settings.sample_n_continuous_streak_amp) && (b->samples_streak_amp == 0)) + || (s->settings.require_continuous_streak_amp == 0 && (b->sma_amp_b.lpos > 1))) { + rsd = sqrt(v) / fabs(b->sma_amp_b.sma); + if (rsd < rsd_threshold) { + return 1; + } + } + return 0; +} + +static int +avmd_decision_freq(const avmd_session_t *s, const struct avmd_buffer *b, double v, double rsd_threshold) { + double f, rsd; + size_t lpos; + f = AVMD_TO_HZ(s->rate, fabs(b->sma_b_fir.sma)); + if ((f < AVMD_MIN_FREQUENCY) || (f > AVMD_MAX_FREQUENCY)) { + return 0; + } + lpos = b->sma_b.lpos; + if ((lpos >= AVMD_BEEP_LEN(s->rate) / b->resolution) && ((s->settings.require_continuous_streak == 1 && (b->sma_b.lpos > s->settings.sample_n_continuous_streak) && (b->samples_streak == 0)) + || (s->settings.require_continuous_streak == 0 && (b->sma_b.lpos > 1)))) { + rsd = sqrt(v) / f; + if ((rsd < 0.3 * rsd_threshold) && (b->sma_amp_b.sma >= 0.005 * b->amplitude_max)) { + return 1; + } + if ((rsd < 0.6 * rsd_threshold) && (b->sma_amp_b.sma >= 0.01 * b->amplitude_max)) { + return 1; + } + if ((rsd < rsd_threshold) && (b->sma_amp_b.sma >= 0.02 * b->amplitude_max)) { + return 1; + } + } + return 0; +} + +static void avmd_report_detection(avmd_session_t *s, enum avmd_detection_mode mode, const struct avmd_buffer *b) { + switch_channel_t *channel; + switch_time_t detection_time; + double f_sma = 0.0; + double v_amp = 9999.9, v_fir = 9999.9; + + const sma_buffer_t *sma_b_fir = &b->sma_b_fir; + const sma_buffer_t *sqa_b_fir = &b->sqa_b_fir; + + const sma_buffer_t *sma_amp_b = &b->sma_amp_b; + const sma_buffer_t *sqa_amp_b = &b->sqa_amp_b; + + channel = switch_core_session_get_channel(s->session); + + s->detection_stop_time = switch_micro_time_now(); /* stop detection timer */ + detection_time = s->detection_stop_time - s->detection_start_time; /* detection time length */ + switch_channel_set_variable_printf(channel, "avmd_total_time", "[%" PRId64 "]", detection_time / 1000); + switch_channel_execute_on(channel, "execute_on_avmd_beep"); + switch_channel_set_variable(channel, "avmd_detect", "TRUE"); + switch (mode) { + + case AVMD_DETECT_AMP: + v_amp = sqa_amp_b->sma - (sma_amp_b->sma * sma_amp_b->sma); /* calculate variance of amplitude (biased estimator) */ + avmd_fire_event(AVMD_EVENT_BEEP, s->session, 0, 0, sma_amp_b->sma, v_amp, 0, 0, s->detection_start_time, s->detection_stop_time, 0, 0); + if (s->settings.report_status == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, "<<< AVMD - Beep Detected [%u][%u][%u]: amplitude = [%f] variance = [%f], detection time [%" PRId64 "] [us] >>>\n", + mode, b->resolution, b->offset, sma_amp_b->sma, v_amp, detection_time); + } + break; + + case AVMD_DETECT_FREQ: + f_sma = sma_b_fir->sma; + v_fir = sqa_b_fir->sma - (sma_b_fir->sma * sma_b_fir->sma); /* calculate variance of filtered samples */ + avmd_fire_event(AVMD_EVENT_BEEP, s->session, AVMD_TO_HZ(s->rate, f_sma), v_fir, 0, 0, 0, 0, s->detection_start_time, s->detection_stop_time, 0, 0); + if (s->settings.report_status == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, "<<< AVMD - Beep Detected [%u][%u][%u]: f = [%f] variance = [%f], detection time [%" PRId64 "] [us] >>>\n", + mode, b->resolution, b->offset, AVMD_TO_HZ(s->rate, f_sma), v_fir, detection_time); + } + break; + + case AVMD_DETECT_BOTH: + v_amp = sqa_amp_b->sma - (sma_amp_b->sma * sma_amp_b->sma); /* calculate variance of amplitude (biased estimator) */ + f_sma = sma_b_fir->sma; + v_fir = sqa_b_fir->sma - (sma_b_fir->sma * sma_b_fir->sma); /* calculate variance of filtered samples */ + avmd_fire_event(AVMD_EVENT_BEEP, s->session, AVMD_TO_HZ(s->rate, f_sma), v_fir, sma_amp_b->sma, v_amp, 0, 0, s->detection_start_time, s->detection_stop_time, 0, 0); + if (s->settings.report_status == 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, "<<< AVMD - Beep Detected [%u][%u][%u]: f = [%f] variance = [%f], amplitude = [%f] variance = [%f], detection time [%" PRId64 "] [us] >>>\n", + mode, b->resolution, b->offset, AVMD_TO_HZ(s->rate, f_sma), v_fir, sma_amp_b->sma, v_amp, detection_time); + } + break; + + default: + break; + } + s->state.beep_state = BEEP_DETECTED; +} + +static uint8_t +avmd_detection_in_progress(avmd_session_t *s) { + uint8_t idx = 0; + while (idx < AVMD_DETECTORS_N) { + switch_mutex_lock(s->detectors[idx].mutex); + if (s->detectors[idx].flag_processing_done == 0) { + switch_mutex_unlock(s->detectors[idx].mutex); + return 1; + } + switch_mutex_unlock(s->detectors[idx].mutex); + ++idx; + } + return 0; +} + +static enum avmd_detection_mode +avmd_detection_result(avmd_session_t *s) { + enum avmd_detection_mode res; + uint8_t idx = 0; + while (idx < AVMD_DETECTORS_N) { + res = s->detectors[idx].result; + if (res != AVMD_DETECT_NONE) { + avmd_report_detection(s, res, &s->detectors[idx].buffer); + return res; + } + ++idx; + } + return AVMD_DETECT_NONE; } /*! \brief Process one frame of data with avmd algorithm. @@ -1539,156 +1767,56 @@ end: * @param frame An audio frame. */ static void avmd_process(avmd_session_t *s, switch_frame_t *frame) { - switch_channel_t *channel; - switch_time_t detection_time; - circ_buffer_t *b; - size_t pos; - double omega, amplitude; - double f; - double v, v_amp = 0.0; - double sma_digital_freq; - uint32_t sine_len_i; - int sample_to_skip_n = s->settings.sample_n_to_skip; - size_t sample_n = 0; + circ_buffer_t *b; + uint8_t idx; + struct avmd_detector *d; b = &s->b; - /* If beep has already been detected skip the CPU heavy stuff */ - if (s->state.beep_state == BEEP_DETECTED) return; - if (s->detection_start_time == 0) { - s->detection_start_time = switch_micro_time_now(); /* start detection timer */ + switch_mutex_lock(s->mutex_detectors_done); + while (avmd_detection_in_progress(s) == 1) { + switch_thread_cond_wait(s->cond_detectors_done, s->mutex_detectors_done); + } + switch_mutex_unlock(s->mutex_detectors_done); + + if (s->state.beep_state == BEEP_DETECTED) { /* If beep has already been detected skip the CPU heavy stuff */ + return; } - /* Precompute values used heavily in the inner loop */ - sine_len_i = (uint32_t) SINE_LEN(s->rate); - //sine_len = (double)sine_len_i; - //beep_len_i = BEEP_LEN(session->rate); + if (s->frame_n_to_skip > 0) { + s->frame_n_to_skip--; + return; + } + if (s->detection_start_time == 0) { + s->detection_start_time = switch_micro_time_now(); /* start detection timer */ + } - channel = switch_core_session_get_channel(s->session); - - /* Insert frame of 16 bit samples into buffer */ - INSERT_INT16_FRAME(b, (int16_t *)(frame->data), frame->samples); + INSERT_INT16_FRAME(b, (int16_t *)(frame->data), frame->samples); /* Insert frame of 16 bit samples into buffer */ s->sample_count += frame->samples; - /* INNER LOOP -- OPTIMIZATION TARGET */ - pos = s->pos; - while (sample_n < (frame->samples - P)) { - /*for (pos = session->pos; pos < (GET_CURRENT_POS(b) - P); pos++) { */ - if ((sample_n % sine_len_i) == 0) { - /* Get a desa2 frequency estimate every sine len */ - omega = avmd_desa2_tweaked(b, pos + sample_n, &litude); + idx = 0; + while (idx < AVMD_DETECTORS_N) { + d = &s->detectors[idx]; + switch_mutex_lock(d->mutex); + d = &s->detectors[idx]; + if (d->result == AVMD_DETECT_NONE) { + d->flag_processing_done = 0; + d->flag_should_exit = 0; + d->samples = frame->samples; + switch_thread_cond_signal(d->cond_start_processing); + } + switch_mutex_unlock(d->mutex); + ++idx; + } - if (omega < -0.999999 || omega > 0.999999) { - if (s->settings.debug == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD RESET >>>\n"); - } - v = 99999.0; - if (s->settings.require_continuous_streak == 1) { - RESET_SMA_BUFFER(&s->sma_b); - RESET_SMA_BUFFER(&s->sqa_b); - RESET_SMA_BUFFER(&s->sma_amp_b); - RESET_SMA_BUFFER(&s->sqa_amp_b); - s->samples_streak = s->settings.sample_n_continuous_streak; - sample_to_skip_n = s->settings.sample_n_to_skip; - } - } else { - if (ISNAN(omega)) { - if (s->settings.debug == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD, SKIP NaN >>>\n"); - } - sample_to_skip_n = s->settings.sample_n_to_skip; - goto loop_continue; - } - if (s->sma_b.pos > 0 && (fabs(omega - s->sma_b.data[s->sma_b.pos - 1]) < 0.00000001)) { - if (s->settings.debug == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD, SKIP >>>\n"); - } - goto loop_continue; - } - if (s->settings.debug == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD omega [%f] >>>\n", omega); - } - if (sample_to_skip_n > 0) { - sample_to_skip_n--; - goto loop_continue; - } + switch_mutex_lock(s->mutex_detectors_done); + while (avmd_detection_in_progress(s) == 1) { + switch_thread_cond_wait(s->cond_detectors_done, s->mutex_detectors_done); + } + avmd_detection_result(s); + switch_mutex_unlock(s->mutex_detectors_done); - if (omega < -0.9999) { /* saturate */ - omega = -0.9999; - } - if (omega > 0.9999) { - omega = 0.9999; - } - - APPEND_SMA_VAL(&s->sma_b, omega); /* append */ - APPEND_SMA_VAL(&s->sqa_b, omega * omega); - APPEND_SMA_VAL(&s->sma_amp_b, amplitude); /* append */ - APPEND_SMA_VAL(&s->sqa_amp_b, amplitude * amplitude); - if (s->settings.require_continuous_streak == 1) { - if (s->samples_streak > 0) { - --s->samples_streak; - } - } - v = s->sqa_b.sma - (s->sma_b.sma * s->sma_b.sma); /* calculate variance of omega(biased estimator) */ - v_amp = s->sqa_amp_b.sma - (s->sma_amp_b.sma * s->sma_amp_b.sma); /* calculate variance of amplitude (biased estimator) */ - if (s->settings.debug == 1) { -#if !defined(WIN32) && defined(AVMD_FAST_MATH) - f = 0.5 * (double) fast_acosf((float)omega); - sma_digital_freq = 0.5 * (double) fast_acosf((float)s->sma_b.sma); -#else - f = 0.5 * acos(omega); - sma_digital_freq = 0.5 * acos(s->sma_b.sma); -#endif /* !WIN32 && AVMD_FAST_MATH */ - if (s->settings.require_continuous_streak == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\t" - "amplitude[%f]\tv_amp[%f]\t" - "streak[%zu] pos[%zu] sample_n[%zu] lpos[%zu] s[%zu]>>>\n", v, omega, f, TO_HZ(s->rate, f), s->sma_b.sma, TO_HZ(s->rate, sma_digital_freq), s->sqa_b.sma, - amplitude, v_amp, - s->samples_streak, s->sma_b.pos, sample_n, s->sma_b.lpos, pos); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_DEBUG, "<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\t" - "amplitude[%f]\tv_amp[%f]\t" - "pos[%zu] sample_n[%zu] lpos[%zu] s[%zu]>>>\n", v, omega, f, TO_HZ(s->rate, f), s->sma_b.sma, TO_HZ(s->rate, sma_digital_freq), s->sqa_b.sma, - amplitude, v_amp, - s->sma_b.pos, sample_n, s->sma_b.lpos, pos); - } - } - } - - /* DECISION */ - /* If variance is less than threshold - * and we have at least two estimates and more than required by continuous - * streak option then we have detection */ - if ((s->settings.require_continuous_streak == 1 && v < VARIANCE_THRESHOLD && (s->sma_b.lpos > 1) && (s->samples_streak == 0)) || (s->settings.require_continuous_streak == 0 && v < VARIANCE_THRESHOLD && (s->sma_b.lpos > 1))) { -#if !defined(WIN32) && defined(AVMD_FAST_MATH) - sma_digital_freq = 0.5 * (double) fast_acosf((float)s->sma_b.sma); -#else - sma_digital_freq = 0.5 * acos(s->sma_b.sma); -#endif /* !WIN32 && AVMD_FAST_MATH */ - - s->detection_stop_time = switch_micro_time_now(); /* stop detection timer */ - detection_time = s->detection_stop_time - s->detection_start_time; /* detection time length */ - switch_channel_set_variable_printf(channel, "avmd_total_time", "[%" PRId64 "]", detection_time / 1000); - switch_channel_execute_on(channel, "execute_on_avmd_beep"); - switch_channel_set_variable(channel, "avmd_detect", "TRUE"); - avmd_fire_event(AVMD_EVENT_BEEP, s->session, TO_HZ(s->rate, sma_digital_freq), v, s->sma_amp_b.sma, v_amp, 0, 0, s->detection_start_time, s->detection_stop_time, 0, 0); - if (s->settings.report_status == 1) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(s->session), SWITCH_LOG_INFO, "<<< AVMD - Beep Detected: f = [%f] variance = [%f], amplitude = [%f] variance = [%f], detection time [%" PRId64 "] [us] >>>\n", TO_HZ(s->rate, sma_digital_freq), v, s->sma_amp_b.sma, v_amp, detection_time); - } - RESET_SMA_BUFFER(&s->sma_b); - RESET_SMA_BUFFER(&s->sqa_b); - RESET_SMA_BUFFER(&s->sma_amp_b); - RESET_SMA_BUFFER(&s->sqa_amp_b); - s->state.beep_state = BEEP_DETECTED; - goto done; - } - } -loop_continue: - ++sample_n; - } - -done: - s->pos += sample_n; + s->pos += frame->samples - AVMD_P; s->pos &= b->mask; return; @@ -1698,6 +1826,182 @@ static void avmd_reloadxml_event_handler(switch_event_t *event) { avmd_load_xml_configuration(avmd_globals.mutex); } +static enum avmd_detection_mode avmd_process_sample(avmd_session_t *s, circ_buffer_t *b, size_t sample_n, size_t pos, struct avmd_detector *d) { + struct avmd_buffer *buffer = &d->buffer; + uint16_t sample_to_skip_n = s->settings.sample_n_to_skip; + enum avmd_detection_mode mode = s->settings.mode; + uint8_t valid_amplitude = 1, valid_omega = 1; + double omega = 0.0, amplitude = 0.0; + double f = 0.0, f_fir = 0.0; + double v_amp = 9999.9, v_fir = 9999.9; + + sma_buffer_t *sma_b = &buffer->sma_b; + sma_buffer_t *sqa_b = &buffer->sqa_b; + + sma_buffer_t *sma_b_fir = &buffer->sma_b_fir; + sma_buffer_t *sqa_b_fir = &buffer->sqa_b_fir; + + sma_buffer_t *sma_amp_b = &buffer->sma_amp_b; + sma_buffer_t *sqa_amp_b = &buffer->sqa_amp_b; + + if (sample_to_skip_n > 0) { + sample_to_skip_n--; + valid_amplitude = 0; + valid_omega = 0; + return AVMD_DETECT_NONE; + } + + omega = avmd_desa2_tweaked(b, pos + sample_n, &litude); + + if (mode == AVMD_DETECT_AMP || mode == AVMD_DETECT_BOTH) { + if (ISNAN(amplitude)) { + valid_amplitude = 0; + if (s->settings.require_continuous_streak_amp == 1) { + RESET_SMA_BUFFER(sma_amp_b); + RESET_SMA_BUFFER(sqa_amp_b); + buffer->samples_streak_amp = s->settings.sample_n_continuous_streak_amp; + sample_to_skip_n = s->settings.sample_n_to_skip; + } + } else { + if (ISINF(amplitude)) { + amplitude = buffer->amplitude_max; + } + if (valid_amplitude == 1) { + APPEND_SMA_VAL(sma_amp_b, amplitude); /* append amplitude */ + APPEND_SMA_VAL(sqa_amp_b, amplitude * amplitude); + if (s->settings.require_continuous_streak_amp == 1) { + if (buffer->samples_streak_amp > 0) { + --buffer->samples_streak_amp; + valid_amplitude = 0; + } + } + } + if (sma_amp_b->sma > buffer->amplitude_max) { + buffer->amplitude_max = sma_amp_b->sma; + } + } + } + + if (mode == AVMD_DETECT_FREQ || mode == AVMD_DETECT_BOTH) { + if (ISNAN(omega)) { + valid_omega = 0; + if (s->settings.require_continuous_streak == 1) { + RESET_SMA_BUFFER(sma_b); + RESET_SMA_BUFFER(sqa_b); + RESET_SMA_BUFFER(sma_b_fir); + RESET_SMA_BUFFER(sqa_b_fir); + buffer->samples_streak = s->settings.sample_n_continuous_streak; + sample_to_skip_n = s->settings.sample_n_to_skip; + } + sample_to_skip_n = s->settings.sample_n_to_skip; + } else if (omega < -0.99999 || omega > 0.99999) { + valid_omega = 0; + if (s->settings.require_continuous_streak == 1) { + RESET_SMA_BUFFER(sma_b); + RESET_SMA_BUFFER(sqa_b); + RESET_SMA_BUFFER(sma_b_fir); + RESET_SMA_BUFFER(sqa_b_fir); + buffer->samples_streak = s->settings.sample_n_continuous_streak; + sample_to_skip_n = s->settings.sample_n_to_skip; + } + } else { + if (valid_omega) { + +#if !defined(WIN32) && defined(AVMD_FAST_MATH) + f = 0.5 * (double) fast_acosf((float)omega); +#else + f = 0.5 * acos(omega); +#endif /* !WIN32 && AVMD_FAST_MATH */ + f_fir = sma_b->pos > 1 ? (AVMD_MEDIAN_FILTER(sma_b->data[sma_b->pos - 2], sma_b->data[sma_b->pos - 1], f)) : f; + + APPEND_SMA_VAL(sma_b, f); /* append frequency */ + APPEND_SMA_VAL(sqa_b, f * f); + APPEND_SMA_VAL(sma_b_fir, f_fir); /* append filtered frequency */ + APPEND_SMA_VAL(sqa_b_fir, f_fir * f_fir); + if (s->settings.require_continuous_streak == 1) { + if (buffer->samples_streak > 0) { + --buffer->samples_streak; + valid_omega = 0; + } + } + } + } + } + + if (((mode == AVMD_DETECT_AMP) || (mode == AVMD_DETECT_BOTH)) && (valid_amplitude == 1)) { + v_amp = sqa_amp_b->sma - (sma_amp_b->sma * sma_amp_b->sma); /* calculate variance of amplitude (biased estimator) */ + if ((mode == AVMD_DETECT_AMP) && (avmd_decision_amplitude(s, buffer, v_amp, AVMD_AMPLITUDE_RSD_THRESHOLD) == 1)) { + return AVMD_DETECT_AMP; + } + } + if (((mode == AVMD_DETECT_FREQ) || (mode == AVMD_DETECT_BOTH)) && (valid_omega == 1)) { + v_fir = sqa_b_fir->sma - (sma_b_fir->sma * sma_b_fir->sma); /* calculate variance of filtered samples */ + if ((mode == AVMD_DETECT_FREQ) && (avmd_decision_freq(s, buffer, v_fir, AVMD_VARIANCE_RSD_THRESHOLD) == 1)) { + return AVMD_DETECT_FREQ; + } + if (mode == AVMD_DETECT_BOTH) { + if ((sma_amp_b->sma > AVMD_MIN_AMP) && (avmd_decision_amplitude(s, buffer, v_amp, AVMD_AMPLITUDE_RSD_THRESHOLD) == 1) && (avmd_decision_freq(s, buffer, v_fir, AVMD_VARIANCE_RSD_THRESHOLD) == 1) && (valid_omega == 1)) { + return AVMD_DETECT_BOTH; + } + } + } + return AVMD_DETECT_NONE; +} + +static void* +avmd_detector_func(switch_thread_t *thread, void *arg) { + size_t sample_n = 0, samples = AVMD_P; + size_t pos; + uint8_t resolution, offset; + avmd_session_t *s; + enum avmd_detection_mode res = AVMD_DETECT_NONE; + struct avmd_detector *d; + + + d = (struct avmd_detector*) arg; + s = d->s; + pos = s->pos; + while (1) { + switch_mutex_lock(d->mutex); + while ((d->flag_processing_done == 1) && (d->flag_should_exit == 0)) { + switch_thread_cond_wait(d->cond_start_processing, d->mutex); + } + /* master set processing_done flag to 0 */ + if (d->flag_should_exit == 1) { + d->flag_processing_done = 1; + goto end; + } + resolution = d->buffer.resolution; + offset = d->buffer.offset; + samples = d->samples; + switch_mutex_unlock(d->mutex); + + sample_n = 0; + while (sample_n < (samples - AVMD_P)) { + if (((sample_n + offset) % resolution) == 0) { + res = avmd_process_sample(d->s, &s->b, sample_n, pos, d); + if (res != AVMD_DETECT_NONE) { + break; + } + } + ++sample_n; + } + switch_mutex_lock(d->mutex); + d->flag_processing_done = 1; + d->result = res; + switch_mutex_unlock(d->mutex); + + switch_mutex_lock(s->mutex_detectors_done); + switch_thread_cond_signal(s->cond_detectors_done); + switch_mutex_unlock(s->mutex_detectors_done); + } + return NULL; + +end: + switch_mutex_unlock(d->mutex); + return NULL; +} + /* For Emacs: * Local Variables: diff --git a/src/mod/applications/mod_avmd/scripts/avmd_get_events.pl b/src/mod/applications/mod_avmd/scripts/avmd_get_events.pl index 1934cf44a2..024ee32aa2 100644 --- a/src/mod/applications/mod_avmd/scripts/avmd_get_events.pl +++ b/src/mod/applications/mod_avmd/scripts/avmd_get_events.pl @@ -1,13 +1,17 @@ +#!/usr/bin/perl -w + + #brief Subscribe to avmd events and print them to the console. #author Piotr Gregor #date 13 Sept 2016 09:44 PM -#!/usr/bin/perl +$|++; # turn on autoflush use strict; use warnings; require ESL; + my $host = "127.0.0.1"; my $port = "8021"; my $pass = "ClueCon"; diff --git a/src/mod/applications/mod_avmd/scripts/avmd_originate.pl b/src/mod/applications/mod_avmd/scripts/avmd_originate.pl new file mode 100644 index 0000000000..e4e4f7f55b --- /dev/null +++ b/src/mod/applications/mod_avmd/scripts/avmd_originate.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl -w + + +#brief Call single voicemail available in default dialplan +# and print detection result to the console. +#author Piotr Gregor +#date 15 Sept 2016 02:44 PM + + +use strict; +use warnings; +require ESL; +use POSIX; +use Time::HiRes; + +my $host = "127.0.0.1"; +my $port = "8021"; +my $pass = "ClueCon"; +my $extension_base = "sofia/internal/1000\@192.168.1.60"; + +my $playback = 'local_stream://moh'; +my $context = 'default'; +my $endpoint; +my $dest; +my $callerid; + + +if ($#ARGV + 1 eq 2) { + $dest = $ARGV[0]; + $callerid = $ARGV[1]; + print "Dialing [" .$dest ."] as " .$callerid ."]\n"; +} else { + die "Please specify destination number and caller id\n"; +} + +my $con = new ESL::ESLconnection($host, $port, $pass); +if (!$con) { + die "Unable to establish connection to $host:$port\n"; +} +if ($con->connected()) { + print "OK, Connected.\n"; +} else { + die "Connection failure.\n"; +} + +print "Subscribing to avmd events...\n"; +$con->events("plain", "CUSTOM avmd::start"); +$con->events("plain", "CUSTOM avmd::stop"); +$con->events("plain", "CUSTOM avmd::beep"); + +while($con->connected()) { + test_once($dest, $callerid); + return 0; +} + +print "Disconnected.\n\n"; + +sub test_once { + my ($dest, $callerid) = @_; + my $originate_string = + 'originate ' . + '{ignore_early_media=true,' . + 'origination_uuid=%s,' . + 'originate_timeout=60,' . + 'origination_caller_id_number=' . $callerid . ',' . + 'origination_caller_id_name=' . $callerid . '}'; + + if(defined($endpoint)) { + $originate_string .= $endpoint; + } else { + $originate_string .= 'loopback/' . $dest . '/' . $context; + } + $originate_string .= ' ' . '&playback(' . $playback . ')'; + + my $uuid = $con->api('create_uuid')->getBody(); + my ($time_epoch, $time_hires) = Time::HiRes::gettimeofday(); + printf("Calling with uuid [%s] [%s]...\n", $uuid, POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)), $time_hires); + + $con->bgapi(sprintf($originate_string, $uuid)); + + print "Waiting for the events...\n\n"; + while($con->connected()) { + my $e = $con->recvEvent(); + if ($e) { + my $body = $e->serialize('plain'); + print $body; + print "\n\n"; + } + } +} diff --git a/src/mod/applications/mod_avmd/scripts/avmd_test.pl b/src/mod/applications/mod_avmd/scripts/avmd_test.pl index 1f1af2929a..3550b85469 100644 --- a/src/mod/applications/mod_avmd/scripts/avmd_test.pl +++ b/src/mod/applications/mod_avmd/scripts/avmd_test.pl @@ -4,8 +4,10 @@ #brief Test module avmd by calling all voicemails available # in avmd test suite and print detection results to the console. #author Piotr Gregor -#details If you are testing locally - remember to set avmd to inbound mode, -# "avmd set inbound" in fs_cli. +#details If you are testing serving voicemails from dialplan then avmd +# must be set to inbound mode, either globally (by avmd set inbound +# in fs_cli) or in dialplan settings ( pairs my %numbers = ( - 400 => "DETECTED", - 401 => "DETECTED", - 402 => "DETECTED", - 403 => "DETECTED", - 404 => "DETECTED", - 405 => "DETECTED", - 406 => "DETECTED", - 407 => "DETECTED", - 408 => "DETECTED", - 409 => "DETECTED", - 410 => "DETECTED", - 411 => "DETECTED", - 412 => "DETECTED", - 413 => "DETECTED", - 414 => "DETECTED", - 500 => "NOTDETECTED", - 501 => "NOTDETECTED" + 503 => "NOTDETECTED", # dual frequency (similar to single freq with varying amplitude), mode [0] AVMD_DETECT_AMP + 504 => "NOTDETECTED", + 505 => "NOTDETECTED", + 506 => "NOTDETECTED", + 507 => "NOTDETECTED", + 508 => "NOTDETECTED", + 509 => "NOTDETECTED", + 510 => "NOTDETECTED", + 511 => "NOTDETECTED", + 512 => "NOTDETECTED", + 513 => "NOTDETECTED", + 514 => "NOTDETECTED", + 515 => "NOTDETECTED", + 516 => "NOTDETECTED", + 517 => "NOTDETECTED", + 518 => "NOTDETECTED", + 519 => "NOTDETECTED", + 520 => "NOTDETECTED", + 521 => "NOTDETECTED", + 522 => "NOTDETECTED", + 523 => "NOTDETECTED", + 603 => "DETECTED", # dual frequency (similar to single freq with varying amplitude), mode [1] AVMD_DETECT_FREQ + 604 => "DETECTED", + 605 => "DETECTED", + 606 => "DETECTED", + 607 => "DETECTED", + 608 => "DETECTED", + 609 => "DETECTED", + 610 => "DETECTED", + 611 => "DETECTED", + 612 => "DETECTED", + 613 => "DETECTED", + 614 => "DETECTED", + 615 => "DETECTED", + 616 => "DETECTED", + 617 => "DETECTED", + 618 => "DETECTED", + 619 => "DETECTED", + 620 => "DETECTED", + 621 => "DETECTED", + 622 => "DETECTED", + 623 => "DETECTED", + 703 => "NOTDETECTED", # dual frequency (similar to single freq with varying amplitude), mode [2] AVMD_DETECT_BOTH + 704 => "NOTDETECTED", + 705 => "NOTDETECTED", + 706 => "NOTDETECTED", + 707 => "NOTDETECTED", + 708 => "NOTDETECTED", + 709 => "NOTDETECTED", + 710 => "NOTDETECTED", + 711 => "NOTDETECTED", + 712 => "NOTDETECTED", + 713 => "NOTDETECTED", + 714 => "NOTDETECTED", + 715 => "NOTDETECTED", + 716 => "NOTDETECTED", + 717 => "NOTDETECTED", + 718 => "NOTDETECTED", + 719 => "NOTDETECTED", + 720 => "NOTDETECTED", + 721 => "NOTDETECTED", + 722 => "NOTDETECTED", + 723 => "NOTDETECTED", + 840531000 => "DETECTED", # obscure voicemails, mode AVMD_DETECT_BOTH + 840531001 => "DETECTED", + 840531002 => "DETECTED", + 840531003 => "DETECTED", + 840531004 => "DETECTED", + 840531005 => "DETECTED", + 840531006 => "DETECTED", + 840531007 => "DETECTED", + 840531008 => "DETECTED", + 840531009 => "DETECTED", + 840531010 => "DETECTED", + 840531011 => "DETECTED", + 840531012 => "DETECTED", + 840531013 => "DETECTED", + 840531014 => "DETECTED", + 840531200 => "DETECTED", # obscure voicemails, mode AVMD_DETECT_FREQ + 840531201 => "DETECTED", + 840531202 => "DETECTED", + 840531203 => "DETECTED", + 840531204 => "DETECTED", + 840531205 => "DETECTED", + 840531206 => "DETECTED", + 840531207 => "DETECTED", + 840531208 => "DETECTED", + 840531209 => "DETECTED", + 840531210 => "DETECTED", + 840531211 => "DETECTED", + 840531212 => "DETECTED", + 840531213 => "DETECTED", + 840531214 => "DETECTED", ); my $host = "127.0.0.1"; @@ -72,7 +150,7 @@ if (!$con) { if ($con->connected()) { print "OK.\n"; } else { - die "Conenction failure.\n"; + die "Connection failure.\n"; } print "Subscribing to avmd events...\t"; @@ -108,10 +186,12 @@ sub test_once { 'originate_timeout=60,' . 'origination_caller_id_number=' . $callerid . ',' . 'origination_caller_id_name=' . $callerid . '}'; - my $outcome; - my $result; - my $event_uuid; + my $outcome = ""; + my $result = ""; + my $event_uuid = "N/A"; my $uuid_in = ""; + my $freq = "N/A"; + my $freq_var = "N/A"; if(defined($endpoint)) { $originate_string .= $endpoint; @@ -137,6 +217,10 @@ sub test_once { } elsif (!($uuid_in eq "") && (($avmd_event_type eq 'avmd::beep') || ($avmd_event_type eq 'avmd::stop'))) { $event_uuid = $e->getHeader("Unique-ID"); if ($event_uuid eq $uuid_in) { + if ($avmd_event_type eq 'avmd::beep') { + $freq = $e->getHeader("Frequency"); + $freq_var = $e->getHeader("Frequency-variance"); + } $outcome = $e->getHeader("Beep-Status"); if ($outcome eq $expectation) { $result = "PASS"; @@ -150,7 +234,7 @@ sub test_once { } } elsif ($event_name eq 'CHANNEL_HANGUP') { $event_uuid = $e->getHeader("variable_origination_uuid"); - if ($event_uuid eq $uuid_out) { + if ((defined $event_uuid) && ($event_uuid eq $uuid_out)) { $outcome = "HANGUP"; $result = "HANGUP"; $hanguped++; @@ -159,5 +243,6 @@ sub test_once { } } } - printf("\t[%s]\t[%s]\t\t[%s]\n", POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)), $expectation, $result); + printf("\t[%s]\t[%s]\t\t[%s]\t[%s]HZ\t[%s]\n", POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)), $expectation, $result, $freq, $freq_var); + Time::HiRes::sleep(0.5); # avoid switch_core_session.c:2265 Throttle Error! 33, switch_time.c:1227 Over Session Rate of 30! }