mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-13 15:50:59 +00:00
FS-8875 Enable faster beep detection
Unnecessary computation of arc cosine is omitted and convergence is checked on partial results. Original frequency is easily retrieved when needed.
This commit is contained in:
parent
462ab95426
commit
c9d027ea91
@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam
|
||||
MODNAME=mod_avmd
|
||||
|
||||
mod_LTLIBRARIES = mod_avmd.la
|
||||
mod_avmd_la_SOURCES = mod_avmd.c avmd_amplitude.c avmd_buffer.c avmd_desa2.c avmd_goertzel.c avmd_fast_acosf.c
|
||||
mod_avmd_la_SOURCES = mod_avmd.c avmd_buffer.c avmd_desa2_tweaked.c avmd_fast_acosf.c
|
||||
mod_avmd_la_CFLAGS = $(AM_CFLAGS) $(AM_MOD_AVMD_CXXFLAGS)
|
||||
mod_avmd_la_LIBADD = $(switch_builddir)/libfreeswitch.la
|
||||
mod_avmd_la_LDFLAGS = -avoid-version -module -no-undefined -shared
|
||||
|
@ -1,4 +1,6 @@
|
||||
#ifndef __AMPLITUDE_H__
|
||||
#ifndef __AVMD_AMPLITUDE_H__
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include "avmd_amplitude.h"
|
||||
#include "avmd_psi.h"
|
||||
@ -10,14 +12,13 @@
|
||||
* @param f Frequency estimate
|
||||
* @return The amplitude at position i
|
||||
*/
|
||||
extern double amplitude(circ_buffer_t *b, size_t i, double f)
|
||||
extern double avmd_amplitude(circ_buffer_t *b, size_t i, double f)
|
||||
{
|
||||
double result;
|
||||
|
||||
result = sqrt(PSI(b, i) / sin(f * f));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __AVMD_AMPLITUDE_H__ */
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
#ifndef __AMPLITUDE_H__
|
||||
#define __AMPLITUDE_H__
|
||||
/*
|
||||
* @brief Estimation of amplitude using DESA-2 algorithm.
|
||||
* @author Eric des Courtis
|
||||
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_AMPLITUDE_H__
|
||||
#define __AVMD_AMPLITUDE_H__
|
||||
|
||||
|
||||
#include "avmd_buffer.h"
|
||||
|
||||
extern double amplitude(circ_buffer_t *, size_t i, double f);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
extern double avmd_amplitude(circ_buffer_t *, size_t i, double f);
|
||||
|
||||
|
||||
#endif /* __AVMD_AMPLITUDE_H__ */
|
||||
|
@ -1,5 +1,15 @@
|
||||
#ifndef __BUFFER_H__
|
||||
#define __BUFFER_H__
|
||||
/*
|
||||
* @brief Circular buffer.
|
||||
*
|
||||
* @author Eric des Courtis
|
||||
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_BUFFER_H__
|
||||
#define __AVMD_BUFFER_H__
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
@ -55,6 +65,10 @@ extern size_t next_power_of_2(size_t v);
|
||||
if ((b)->backlog > (b)->buf_len) (b)->backlog = (b)->buf_len; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* ((f)[(b)->i] >= 0) ? \
|
||||
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
|
||||
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \ */
|
||||
#define INSERT_INT16_FRAME(b, f, l) \
|
||||
{ \
|
||||
for ((b)->i = 0; (b)->i < (l); (b)->i++) { \
|
||||
@ -62,9 +76,7 @@ extern size_t next_power_of_2(size_t v);
|
||||
(b), \
|
||||
((b)->i + (b)->pos), \
|
||||
( \
|
||||
((f)[(b)->i] >= 0) ? \
|
||||
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
|
||||
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \
|
||||
(BUFF_TYPE)(f)[(b)->i] \
|
||||
) \
|
||||
); \
|
||||
} \
|
||||
@ -102,5 +114,4 @@ extern size_t next_power_of_2(size_t v);
|
||||
SET_SAMPLE((b), GET_CURRENT_LPOS((b)), (s)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __AVMD_BUFFER_H__ */
|
||||
|
@ -1,4 +1,6 @@
|
||||
#ifndef __DESA2_H__
|
||||
#ifndef __AVMD_DESA2_H__
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <float.h>
|
||||
@ -14,7 +16,7 @@
|
||||
#include "avmd_fast_acosf.h"
|
||||
#endif
|
||||
|
||||
extern double desa2(circ_buffer_t *b, size_t i)
|
||||
extern double avmd_desa2(circ_buffer_t *b, size_t i)
|
||||
{
|
||||
double d;
|
||||
double n;
|
||||
@ -33,13 +35,10 @@ extern double desa2(circ_buffer_t *b, size_t i)
|
||||
x4 = GET_SAMPLE((b), ((i) + 4));
|
||||
|
||||
x2sq = x2 * x2;
|
||||
|
||||
d = 2.0 * ((x2sq) - (x1 * x3));
|
||||
if (d == 0.0) return 0.0;
|
||||
|
||||
n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4));
|
||||
|
||||
|
||||
#ifdef AVMD_FAST_MATH
|
||||
result = 0.5 * (double)fast_acosf((float)n/d);
|
||||
#else
|
||||
@ -52,4 +51,4 @@ extern double desa2(circ_buffer_t *b, size_t i)
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* __AVMD_DESA2_H__ */
|
||||
|
@ -1,8 +1,19 @@
|
||||
#ifndef __DESA2_H__
|
||||
#define __DESA2_H__
|
||||
/*
|
||||
* @brief DESA-2 algorithm implementation.
|
||||
* @author Eric des Courtis
|
||||
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_DESA2_H__
|
||||
#define __AVMD_DESA2_H__
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include "avmd_buffer.h"
|
||||
|
||||
extern double desa2(circ_buffer_t *b, size_t i);
|
||||
#endif
|
||||
/* Returns digital frequency estimation. */
|
||||
extern double avmd_desa2(circ_buffer_t *b, size_t i);
|
||||
|
||||
|
||||
#endif /* __AVMD_DESA2_H__ */
|
||||
|
64
src/mod/applications/mod_avmd/avmd_desa2_tweaked.c
Normal file
64
src/mod/applications/mod_avmd/avmd_desa2_tweaked.c
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef __AVMD_DESA2_TWEAKED_H__
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <float.h>
|
||||
#define ISNAN(x) (!!(_isnan(x)))
|
||||
#else
|
||||
#define ISNAN(x) (isnan(x))
|
||||
#endif
|
||||
#include "avmd_buffer.h"
|
||||
#include "avmd_desa2_tweaked.h"
|
||||
#include "avmd_options.h"
|
||||
|
||||
#ifdef AVMD_FAST_MATH
|
||||
#include "avmd_fast_acosf.h"
|
||||
#endif
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
|
||||
double
|
||||
avmd_desa2_tweaked(circ_buffer_t *b, size_t i,
|
||||
switch_core_session_t *session)
|
||||
{
|
||||
double d;
|
||||
double n;
|
||||
double x0;
|
||||
double x1;
|
||||
double x2;
|
||||
double x3;
|
||||
double x4;
|
||||
double x2sq;
|
||||
double result;
|
||||
|
||||
x0 = GET_SAMPLE((b), (i));
|
||||
x1 = GET_SAMPLE((b), ((i) + 1));
|
||||
x2 = GET_SAMPLE((b), ((i) + 2));
|
||||
x3 = GET_SAMPLE((b), ((i) + 3));
|
||||
x4 = GET_SAMPLE((b), ((i) + 4));
|
||||
x2sq = x2 * x2;
|
||||
d = 2.0 * ((x2sq) - (x1 * x3));
|
||||
n = ((x2sq) - (x0 * x4)) - ((x1 * x1)
|
||||
- (x0 * x2)) - ((x3 * x3) - (x2 * x4));
|
||||
|
||||
/* instead of
|
||||
#ifdef FASTMATH
|
||||
result = 0.5 * (double)fast_acosf((float)n/d);
|
||||
#else
|
||||
result = 0.5 * acos(n/d);
|
||||
#endif
|
||||
we do simplified, modified for speed version : */
|
||||
|
||||
result = n/d;
|
||||
if (isinf(result)) {
|
||||
if (n < 0.0)
|
||||
return -10.0;
|
||||
else
|
||||
return 10.0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* __AVMD_DESA2_TWEAKED_H__ */
|
42
src/mod/applications/mod_avmd/avmd_desa2_tweaked.h
Normal file
42
src/mod/applications/mod_avmd/avmd_desa2_tweaked.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* @brief Estimator of cosine of digital frequency.
|
||||
* @details It is tweaked DESA implementation which
|
||||
* returns partial product of DESA-2 estimation
|
||||
* so that arc cosine transform can be ommited
|
||||
* on all computations, but these values can
|
||||
* be checked for convergence in the same time.
|
||||
* If the partial results converge then frequency
|
||||
* converges too.
|
||||
* @author Piotr Gregor < piotrek.gregor gmail.com >
|
||||
* @date 20 Mar 2016
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_DESA2_TWEAKED_H__
|
||||
#define __AVMD_DESA2_TWEAKED_H__
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include "avmd_buffer.h"
|
||||
#include <switch.h>
|
||||
|
||||
|
||||
/* Instead of returning digital frequency estimation using
|
||||
* result = 0.5 * acos(n/d),
|
||||
* which involves expensive computation of arc cosine on
|
||||
* each new sample, this function returns only (n/d) factor.
|
||||
* The series of these partial DESA-2 results can be still
|
||||
* checked for convergence, though measures and thresholds
|
||||
* used to assess this will differ from those used for
|
||||
* assessment of convergence of instantaneous frequency
|
||||
* estimates since transformation of tweaked results
|
||||
* to corresponding frequencies is nonlinear.
|
||||
* The actual frequency estimation can be retrieved later
|
||||
* from this partial result using
|
||||
* 0.5 * acos(n/d)
|
||||
*/
|
||||
double avmd_desa2_tweaked(circ_buffer_t *b, size_t i,
|
||||
switch_core_session_t *session);
|
||||
|
||||
|
||||
#endif /* __AVMD_DESA2_TWEAKED_H__ */
|
@ -1,9 +1,17 @@
|
||||
#ifndef __FAST_ACOSF_H__
|
||||
#define __FAST_ACOSF_H__
|
||||
/*
|
||||
* @brief Fast arithmetic using precomputed arc cosine table.
|
||||
* @author Eric des Courtis
|
||||
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_FAST_ACOSF_H__
|
||||
#define __AVMD_FAST_ACOSF_H__
|
||||
|
||||
|
||||
#define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
|
||||
|
||||
|
||||
/*! \brief Arc cosine table initialization.
|
||||
*
|
||||
* @author Eric des Courtis
|
||||
@ -42,5 +50,4 @@ extern float fast_acosf(float x);
|
||||
*/
|
||||
extern int compute_table(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __AVMD_FAST_ACOSF_H__ */
|
||||
|
17
src/mod/applications/mod_avmd/avmd_fir.h
Normal file
17
src/mod/applications/mod_avmd/avmd_fir.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @brief Filters.
|
||||
* @author Piotr Gregor < piotrek.gregor gmail.com >
|
||||
* @date 23 Mar 2016
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_FIR_H__
|
||||
#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))
|
||||
|
||||
|
||||
#endif
|
@ -1,17 +1,12 @@
|
||||
#ifndef __GOERTZEL_H__
|
||||
#ifndef __AVMD_GOERTZEL_H__
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include "avmd_goertzel.h"
|
||||
#include "avmd_buffer.h"
|
||||
|
||||
/*! \brief Identify frequency components of a signal
|
||||
* @author Eric des Courtis
|
||||
* @param b A circular buffer
|
||||
* @param pos Position in the buffer
|
||||
* @param f Frequency to look at
|
||||
* @param num Number of samples to look at
|
||||
* @return A power estimate for frequency f at position pos in the stream
|
||||
*/
|
||||
extern double goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
|
||||
|
||||
extern double avmd_goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
|
||||
{
|
||||
double s = 0.0;
|
||||
double p = 0.0;
|
||||
@ -22,15 +17,14 @@ extern double goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
|
||||
coeff = 2.0 * cos(2.0 * M_PI * f);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
/* TODO: optimize to avoid GET_SAMPLE when possible */
|
||||
s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2;
|
||||
p2 = p;
|
||||
p = s;
|
||||
/* TODO: optimize to avoid GET_SAMPLE when possible */
|
||||
s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2;
|
||||
p2 = p;
|
||||
p = s;
|
||||
}
|
||||
|
||||
return (p2 * p2) + (p * p) - (coeff * p2 * p);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __AVMD_GOERTZEL_H__ */
|
||||
|
@ -1,5 +1,12 @@
|
||||
#ifndef __GOERTZEL_H__
|
||||
#define __GOERTZEL_H__
|
||||
/*
|
||||
* @brief Goertzel algorithm.
|
||||
* @author Eric des Courtis
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_GOERTZEL_H__
|
||||
#define __AVMD_GOERTZEL_H__
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
@ -11,8 +18,16 @@
|
||||
#define M_PI 3.14159265358979323846264338327
|
||||
#endif
|
||||
|
||||
extern double goertzel(circ_buffer_t *b, size_t pos, double f, size_t num);
|
||||
|
||||
#endif
|
||||
/*! \brief Identify frequency components of a signal
|
||||
* @author Eric des Courtis
|
||||
* @param b A circular buffer
|
||||
* @param pos Position in the buffer
|
||||
* @param f Frequency to look at
|
||||
* @param num Number of samples to look at
|
||||
* @return A power estimate for frequency f at position pos in the stream
|
||||
*/
|
||||
extern double avmd_goertzel(circ_buffer_t *b, size_t pos, double f, size_t num);
|
||||
|
||||
|
||||
#endif /* __AVMD_GOERTZEL_H__ */
|
||||
|
@ -10,11 +10,13 @@
|
||||
#define __AVMD_OPTIONS_H__
|
||||
|
||||
|
||||
/* #define AVMD_DEBUG 1 */
|
||||
/* define/undefine this to enable/disable printing of avmd
|
||||
* intermediate computations to log */
|
||||
/*#define AVMD_DEBUG */
|
||||
|
||||
/* define/undef this to enable/disable reporting of beep
|
||||
* detection status after session ended */
|
||||
#define AVMD_REPORT_STATUS 1
|
||||
#define AVMD_REPORT_STATUS
|
||||
|
||||
/* define/undefine this to enable/disable faster computation
|
||||
* of arcus cosine - table will be created mapping floats
|
||||
@ -25,7 +27,16 @@
|
||||
/* define/undefine this to classify avmd beep detection as valid
|
||||
* only when there is required number of consecutive elements
|
||||
* in the SMA buffer without reset */
|
||||
#define AVMD_REQUIRE_CONTINUOUS_STREAK 5
|
||||
#define AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
|
||||
/* define number of samples to skip starting from the beginning
|
||||
* of frame and after reset */
|
||||
#define AVMD_SAMLPE_TO_SKIP_N 6
|
||||
|
||||
/* define/undefine this to enable/disable simplified estimation
|
||||
* of frequency based on approximation of sin(x) with (x)
|
||||
* in the range x=[0,PI/2] */
|
||||
#define AVMD_SIMPLIFIED_ESTIMATION
|
||||
|
||||
/* define/undefine to enable/disable avmd on incoming audio */
|
||||
#define AVMD_INBOUND_CHANNEL
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef __PSI_H__
|
||||
#define __PSI_H__
|
||||
#ifndef __AVMD_PSI_H__
|
||||
#define __AVMD_PSI_H__
|
||||
|
||||
|
||||
#include "avmd_buffer.h"
|
||||
|
||||
#define PSI(b, i) (GET_SAMPLE((b), ((i) + 1))*GET_SAMPLE((b), ((i) + 1))-GET_SAMPLE((b), ((i) + 2))*GET_SAMPLE((b), ((i) + 0)))
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __AVMD_PSI_H__ */
|
||||
|
@ -1,5 +1,15 @@
|
||||
#ifndef __SMA_BUFFER_H__
|
||||
#define __SMA_BUFFER_H__
|
||||
/*
|
||||
* @brief SMA buffer.
|
||||
*
|
||||
* @author Eric des Courtis
|
||||
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AVMD_SMA_BUFFER_H__
|
||||
#define __AVMD_SMA_BUFFER_H__
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
@ -64,7 +74,11 @@ typedef struct {
|
||||
}while(0);
|
||||
|
||||
*/
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __AVMD_SMA_BUFFER_H__ */
|
||||
|
||||
|
||||
/*
|
||||
|
||||
int main(void)
|
||||
|
@ -154,4 +154,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -27,10 +27,12 @@
|
||||
*
|
||||
* Modifications:
|
||||
* Piotr Gregor <piotrek.gregor gmail.com>:
|
||||
* FS-8808, FS-8809, FS-8810, FS-8852, FS-8853, FS-8854, FS-8855, FS-8860, FS-8861
|
||||
* FS-8808, FS-8809, FS-8810, FS-8852, FS-8853, FS-8854, FS-8855,
|
||||
* FS-8860, FS-8861, FS-8875
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <g711.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -43,11 +45,8 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include "avmd_amplitude.h"
|
||||
#include "avmd_buffer.h"
|
||||
#include "avmd_desa2.h"
|
||||
//#include "avmd_goertzel.h"
|
||||
#include "avmd_psi.h"
|
||||
#include "avmd_desa2_tweaked.h"
|
||||
#include "avmd_sma_buf.h"
|
||||
#include "avmd_options.h"
|
||||
|
||||
@ -59,9 +58,9 @@
|
||||
/*! Calculate how many audio samples per ms based on the rate */
|
||||
#define SAMPLES_PER_MS(r, m) ((r) / (1000/(m)))
|
||||
/*! Minimum beep length */
|
||||
#define BEEP_TIME (100)
|
||||
#define BEEP_TIME (2)
|
||||
/*! How often to evaluate the output of desa2 in ms */
|
||||
#define SINE_TIME (10)
|
||||
#define SINE_TIME (2*0.125)
|
||||
/*! How long in samples does desa2 results get evaluated */
|
||||
#define SINE_LEN(r) SAMPLES_PER_MS((r), SINE_TIME)
|
||||
/*! How long in samples is the minimum beep length */
|
||||
@ -92,11 +91,11 @@
|
||||
/*! 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.0001)
|
||||
#define VARIANCE_THRESHOLD (0.00025)
|
||||
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
/* increase this value to eliminate false positives */
|
||||
#define SAMPLES_CONSECUTIVE_STREAK 3
|
||||
#define SAMPLES_CONSECUTIVE_STREAK 15
|
||||
#endif
|
||||
|
||||
/*! Syntax of the API call. */
|
||||
@ -108,6 +107,9 @@
|
||||
/*! FreeSWITCH CUSTOM event type. */
|
||||
#define AVMD_EVENT_BEEP "avmd::beep"
|
||||
|
||||
#define AVMD_CHAR_BUF_LEN 10
|
||||
#define AVMD_BUF_LINEAR_LEN 160
|
||||
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown);
|
||||
@ -144,13 +146,16 @@ typedef struct {
|
||||
switch_time_t start_time;
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
size_t samples_streak; /* number of DESA samples in single streak without reset
|
||||
needed to validate SMA estimator, half the size of SMA buffer */
|
||||
needed to validate SMA estimator */
|
||||
#endif
|
||||
size_t sample_count;
|
||||
} avmd_session_t;
|
||||
|
||||
static void avmd_process(avmd_session_t *session, switch_frame_t *frame);
|
||||
static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, switch_abc_type_t type);
|
||||
static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session);
|
||||
static switch_bool_t avmd_callback(switch_media_bug_t * bug,
|
||||
void *user_data, switch_abc_type_t type);
|
||||
static void init_avmd_session_data(avmd_session_t *avmd_session,
|
||||
switch_core_session_t *fs_session);
|
||||
|
||||
|
||||
/*! \brief The avmd session data initialization function.
|
||||
@ -158,7 +163,8 @@ static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_se
|
||||
* @param avmd_session A reference to a avmd session.
|
||||
* @param fs_session A reference to a FreeSWITCH session.
|
||||
*/
|
||||
static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_session_t *fs_session)
|
||||
static void init_avmd_session_data(avmd_session_t *avmd_session,
|
||||
switch_core_session_t *fs_session)
|
||||
{
|
||||
/*! This is a worst case sample rate estimate */
|
||||
avmd_session->rate = 48000;
|
||||
@ -175,6 +181,7 @@ static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_se
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
avmd_session->samples_streak = SAMPLES_CONSECUTIVE_STREAK;
|
||||
#endif
|
||||
avmd_session->sample_count = 0;
|
||||
|
||||
INIT_SMA_BUFFER(
|
||||
&avmd_session->sma_b,
|
||||
@ -198,7 +205,8 @@ static void init_avmd_session_data(avmd_session_t *avmd_session, switch_core_se
|
||||
* @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)
|
||||
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;
|
||||
@ -216,7 +224,8 @@ static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, sw
|
||||
read_codec = switch_core_session_get_read_codec(avmd_session->session);
|
||||
avmd_session->rate = read_codec->implementation->samples_per_second;
|
||||
avmd_session->start_time = switch_micro_time_now();
|
||||
/* avmd_session->vmd_codec.channels = read_codec->implementation->number_of_channels; */
|
||||
/* avmd_session->vmd_codec.channels =
|
||||
* read_codec->implementation->number_of_channels; */
|
||||
break;
|
||||
|
||||
case SWITCH_ABC_TYPE_READ_REPLACE:
|
||||
@ -255,13 +264,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
|
||||
if (switch_event_reserve_subclass(AVMD_EVENT_BEEP) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
|
||||
"Couldn't register subclass [%s]!\n", AVMD_EVENT_BEEP);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
|
||||
switch_log_printf(
|
||||
SWITCH_CHANNEL_LOG,
|
||||
@ -340,7 +347,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
|
||||
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);
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
@ -386,7 +394,8 @@ SWITCH_STANDARD_APP(avmd_start_function)
|
||||
return;
|
||||
}
|
||||
|
||||
avmd_session = (avmd_session_t *)switch_core_session_alloc(session, sizeof(avmd_session_t));
|
||||
avmd_session = (avmd_session_t *)switch_core_session_alloc(
|
||||
session, sizeof(avmd_session_t));
|
||||
|
||||
init_avmd_session_data(avmd_session, session);
|
||||
|
||||
@ -554,7 +563,8 @@ SWITCH_STANDARD_API(avmd_api_main)
|
||||
|
||||
/* 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(fs_session, sizeof(avmd_session_t));
|
||||
avmd_session = (avmd_session_t *) switch_core_session_alloc(
|
||||
fs_session, sizeof(avmd_session_t));
|
||||
|
||||
init_avmd_session_data(avmd_session, fs_session);
|
||||
|
||||
@ -624,18 +634,16 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
|
||||
|
||||
circ_buffer_t *b;
|
||||
size_t pos;
|
||||
double f;
|
||||
double omega;
|
||||
#ifdef AVMD_DEBUG
|
||||
double f;
|
||||
#endif
|
||||
double v;
|
||||
// double error = 0.0;
|
||||
// double success = 0.0;
|
||||
// double amp = 0.0;
|
||||
// double s_rate;
|
||||
// double e_rate;
|
||||
// double avg_a;
|
||||
//double sine_len;
|
||||
double sma_digital_freq;
|
||||
uint32_t sine_len_i;
|
||||
//uint32_t beep_len_i;
|
||||
// int valid;
|
||||
char buf[AVMD_CHAR_BUF_LEN];
|
||||
int sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
|
||||
size_t sample_n = 0;
|
||||
|
||||
b = &session->b;
|
||||
|
||||
@ -651,50 +659,109 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
|
||||
|
||||
/* Insert frame of 16 bit samples into buffer */
|
||||
INSERT_INT16_FRAME(b, (int16_t *)(frame->data), frame->samples);
|
||||
session->sample_count += frame->samples;
|
||||
|
||||
//switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_INFO, "<<< AVMD sine_len_i=%d >>>\n", sine_len_i);
|
||||
|
||||
/* INNER LOOP -- OPTIMIZATION TARGET */
|
||||
for (pos = session->pos; pos < (GET_CURRENT_POS(b) - P); pos++) {
|
||||
if ((pos % sine_len_i) == 0) {
|
||||
/* INNER LOOP -- OPTIMIZATION TARGET */
|
||||
pos = session->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 */
|
||||
f = desa2(b, pos);
|
||||
omega = avmd_desa2_tweaked(b, pos + sample_n, session->session);
|
||||
|
||||
if (f < MIN_FREQUENCY_R(session->rate) || f > MAX_FREQUENCY_R(session->rate)) {
|
||||
if (omega < -0.999999 || omega > 0.999999) {
|
||||
#ifdef AVMD_DEBUG
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
|
||||
SWITCH_LOG_DEBUG, "<<< AVMD RESET >>>\n");
|
||||
#endif
|
||||
v = 99999.0;
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
RESET_SMA_BUFFER(&session->sma_b);
|
||||
RESET_SMA_BUFFER(&session->sqa_b);
|
||||
session->samples_streak = SAMPLES_CONSECUTIVE_STREAK;
|
||||
sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
|
||||
#endif
|
||||
} else {
|
||||
APPEND_SMA_VAL(&session->sma_b, f);
|
||||
APPEND_SMA_VAL(&session->sqa_b, f * f);
|
||||
if (isnan(omega)) {
|
||||
#ifdef AVMD_DEBUG
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
|
||||
SWITCH_LOG_DEBUG, "<<< AVMD, SKIP NaN >>>\n");
|
||||
#endif
|
||||
sample_to_skip_n = AVMD_SAMLPE_TO_SKIP_N;
|
||||
goto loop_continue;
|
||||
}
|
||||
if (session->sma_b.pos > 0 &&
|
||||
(fabs(omega - session->sma_b.data[session->sma_b.pos - 1]) < 0.00000001)) {
|
||||
#ifdef AVMD_DEBUG
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
|
||||
"<<< AVMD, SKIP >>>\n");
|
||||
#endif
|
||||
goto loop_continue;
|
||||
}
|
||||
#ifdef AVMD_DEBUG
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session),
|
||||
SWITCH_LOG_DEBUG, "<<< AVMD omega [%f] >>>\n", omega);
|
||||
#endif
|
||||
if (sample_to_skip_n > 0) {
|
||||
sample_to_skip_n--;
|
||||
goto loop_continue;
|
||||
}
|
||||
|
||||
/* saturate */
|
||||
if (omega < -0.9999)
|
||||
omega = -0.9999;
|
||||
if (omega > 0.9999)
|
||||
omega = 0.9999;
|
||||
|
||||
/* append */
|
||||
APPEND_SMA_VAL(&session->sma_b, omega);
|
||||
APPEND_SMA_VAL(&session->sqa_b, omega * omega);
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
if (session->samples_streak > 0)
|
||||
--session->samples_streak;
|
||||
#endif
|
||||
/* calculate variance */
|
||||
/* calculate variance (biased estimator) */
|
||||
v = session->sqa_b.sma - (session->sma_b.sma * session->sma_b.sma);
|
||||
#ifdef AVMD_DEBUG
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
#ifdef AVMD_FAST_MATH
|
||||
f = 0.5 * (double) fast_acosf((float)omega);
|
||||
sma_digital_freq = 0.5 * (double) fast_acosf((float)session->sma_b.sma);
|
||||
#else
|
||||
f = 0.5 * acos(omega);
|
||||
sma_digital_freq = 0.5 * acos(session->sma_b.sma);
|
||||
#endif /* AVMD_FAST_MATH */
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
|
||||
"<<< AVMD v[%f] f[%f] [%f]Hz\tsma[%f][%f]Hz\tsqa[%f]\tstreak[%zu] pos[%zu] >>>\n", v, f, TO_HZ(session->rate, f),
|
||||
session->sma_b.sma, TO_HZ(session->rate, session->sma_b.sma), session->sqa_b.sma, session->samples_streak, session->sma_b.pos);
|
||||
#else
|
||||
"<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\t"
|
||||
"streak[%zu] pos[%zu] sample_n[%zu] lpos[%zu] s[%zu]>>>\n",
|
||||
v, omega, f, TO_HZ(session->rate, f), session->sma_b.sma,
|
||||
TO_HZ(session->rate, sma_digital_freq), session->sqa_b.sma, session->samples_streak,
|
||||
session->sma_b.pos, sample_n, session->sma_b.lpos, pos);
|
||||
#else
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
|
||||
"<<< AVMD v[%f] f[%f] [%f]Hz\tsma[%f][%f]Hz\tsqa[%f]\tpos[%zu] >>>\n", v, f, TO_HZ(session->rate, f),
|
||||
session->sma_b.sma, TO_HZ(session->rate, session->sma_b.sma), session->sqa_b.sma, session->sma_b.pos);
|
||||
#endif
|
||||
#endif
|
||||
"<<< AVMD v[%.10f]\tomega[%f]\tf[%f] [%f]Hz\t\tsma[%f][%f]Hz\t\tsqa[%f]\tpos[%zu]"
|
||||
" sample_n[%zu] lpos[%zu] s[%zu]>>>\n", v, omega, f,
|
||||
TO_HZ(session->rate, f), session->sma_b.sma, TO_HZ(session->rate, sma_digital_freq),
|
||||
session->sqa_b.sma, session->sma_b.pos, sample_n, session->sma_b.lpos, pos);
|
||||
#endif /* AVMD_REQUIRE_CONTINUOUS_STREAK */
|
||||
#endif /* AVMD_DEBUG */
|
||||
}
|
||||
|
||||
/* If variance is less than threshold then we have detection */
|
||||
/* DECISION */
|
||||
/* If variance is less than threshold
|
||||
* and we have at least two estimates
|
||||
* then we have detection */
|
||||
#ifdef AVMD_REQUIRE_CONTINUOUS_STREAK
|
||||
if (v < VARIANCE_THRESHOLD && (session->sma_b.pos > 1) && (session->samples_streak == 0)) {
|
||||
if (v < VARIANCE_THRESHOLD && (session->sma_b.lpos > 1) && (session->samples_streak == 0)) {
|
||||
#else
|
||||
if (v < VARIANCE_THRESHOLD && (session->sma_b.pos > 1)) {
|
||||
if (v < VARIANCE_THRESHOLD && (session->sma_b.lpos > 1)) {
|
||||
#endif
|
||||
#ifdef AVMD_FAST_MATH
|
||||
sma_digital_freq = 0.5 * (double) fast_acosf((float)session->sma_b.sma);
|
||||
#else
|
||||
sma_digital_freq = 0.5 * acos(session->sma_b.sma);
|
||||
#endif /* AVMD_FAST_MATH */
|
||||
snprintf(buf, AVMD_CHAR_BUF_LEN, "%f", TO_HZ(session->rate, sma_digital_freq));
|
||||
switch_channel_set_variable_printf(channel, "avmd_total_time",
|
||||
"[%d]", (int)(switch_micro_time_now() - session->start_time) / 1000);
|
||||
switch_channel_execute_on(channel, "execute_on_avmd_beep");
|
||||
@ -707,6 +774,9 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID",
|
||||
switch_core_session_get_uuid(session->session));
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "avmd");
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "frequency", buf);
|
||||
snprintf(buf, AVMD_CHAR_BUF_LEN, "%f", v);
|
||||
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "variance", buf);
|
||||
|
||||
if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) return;
|
||||
|
||||
@ -714,22 +784,25 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
|
||||
switch_event_fire(&event_copy);
|
||||
|
||||
#ifdef AVMD_REPORT_STATUS
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
|
||||
"<<< AVMD - Beep Detected f = [%f] >>>\n", TO_HZ(session->rate, session->sma_b.sma));
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_NOTICE,
|
||||
"<<< AVMD - Beep Detected: f = [%f], variance = [%f] >>>\n",
|
||||
TO_HZ(session->rate, sma_digital_freq), v);
|
||||
#endif
|
||||
switch_channel_set_variable(channel, "avmd_detect", "TRUE");
|
||||
RESET_SMA_BUFFER(&session->sma_b);
|
||||
RESET_SMA_BUFFER(&session->sqa_b);
|
||||
session->state.beep_state = BEEP_DETECTED;
|
||||
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
//amp = 0.0;
|
||||
//success = 0.0;
|
||||
//error = 0.0;
|
||||
}
|
||||
loop_continue:
|
||||
++sample_n;
|
||||
}
|
||||
session->pos = pos;
|
||||
|
||||
done:
|
||||
session->pos += sample_n;
|
||||
session->pos &= b->mask;
|
||||
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user