From 8e189ed2337efd5f10b292476ffabaa8ae750a44 Mon Sep 17 00:00:00 2001 From: Piotr Gregor Date: Tue, 20 Sep 2016 17:49:07 +0100 Subject: [PATCH] FS-9542 [avmd]: multithreaded Now avmd detection is done in detector threads processing audio frames with different resolution and/or offsets. Detection decision is based on relative standard deviation which dynamically adjusts to the signal. Detection of amplitude, frequency and both simultaneously is enabled and corresponding setting added to the configuration. Frequency estimates are filtered with median filter. This commit also resolves: FS-9539 - Get estimates with different resolution FS-9513 - Use adjustable thresholds FS-9502 - Add detection mode setting FS-9501 - Enable three modes of detection FS-9407 - Add check of amplitude for NaN before appending to SMA buffer FS-9139 - Unit test framework --- conf/vanilla/autoload_configs/avmd.conf.xml | 11 +- src/mod/applications/mod_avmd/avmd_buffer.h | 2 +- .../mod_avmd/avmd_desa2_tweaked.c | 4 +- src/mod/applications/mod_avmd/avmd_fir.h | 6 +- src/mod/applications/mod_avmd/avmd_sma_buf.h | 2 +- .../conf/autoload_configs/avmd.conf.xml | 11 +- .../mod_avmd/conf/avmd_test_dialplan.xml | 782 ++++++++ src/mod/applications/mod_avmd/mod_avmd.c | 1626 ++++++++++------- .../mod_avmd/scripts/avmd_get_events.pl | 6 +- .../mod_avmd/scripts/avmd_originate.pl | 90 + .../mod_avmd/scripts/avmd_test.pl | 135 +- 11 files changed, 1977 insertions(+), 698 deletions(-) create mode 100644 src/mod/applications/mod_avmd/conf/avmd_test_dialplan.xml create mode 100644 src/mod/applications/mod_avmd/scripts/avmd_originate.pl 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! }