提交 c9d027ea authored 作者: Piotr Gregor's avatar Piotr Gregor

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.
上级 462ab954
...@@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam ...@@ -2,7 +2,7 @@ include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_avmd MODNAME=mod_avmd
mod_LTLIBRARIES = mod_avmd.la 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_CFLAGS = $(AM_CFLAGS) $(AM_MOD_AVMD_CXXFLAGS)
mod_avmd_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_avmd_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_avmd_la_LDFLAGS = -avoid-version -module -no-undefined -shared mod_avmd_la_LDFLAGS = -avoid-version -module -no-undefined -shared
#ifndef __AMPLITUDE_H__ #ifndef __AVMD_AMPLITUDE_H__
#include <math.h> #include <math.h>
#include "avmd_amplitude.h" #include "avmd_amplitude.h"
#include "avmd_psi.h" #include "avmd_psi.h"
...@@ -10,14 +12,13 @@ ...@@ -10,14 +12,13 @@
* @param f Frequency estimate * @param f Frequency estimate
* @return The amplitude at position i * @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; double result;
result = sqrt(PSI(b, i) / sin(f * f)); result = sqrt(PSI(b, i) / sin(f * f));
return result; return result;
} }
#endif
#endif /* __AVMD_AMPLITUDE_H__ */
#ifndef __AMPLITUDE_H__ /*
#define __AMPLITUDE_H__ * @brief Estimation of amplitude using DESA-2 algorithm.
#include "avmd_buffer.h" * @author Eric des Courtis
* @par Modifications: Piotr Gregor < piotrek.gregor gmail.com >
*/
#ifndef __AVMD_AMPLITUDE_H__
#define __AVMD_AMPLITUDE_H__
extern double amplitude(circ_buffer_t *, size_t i, double f);
#include "avmd_buffer.h"
#endif extern double avmd_amplitude(circ_buffer_t *, size_t i, double f);
#endif /* __AVMD_AMPLITUDE_H__ */
#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 <stdlib.h>
#include <assert.h> #include <assert.h>
...@@ -55,6 +65,10 @@ extern size_t next_power_of_2(size_t v); ...@@ -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; \ if ((b)->backlog > (b)->buf_len) (b)->backlog = (b)->buf_len; \
} while (0) } 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) \ #define INSERT_INT16_FRAME(b, f, l) \
{ \ { \
for ((b)->i = 0; (b)->i < (l); (b)->i++) { \ for ((b)->i = 0; (b)->i < (l); (b)->i++) { \
...@@ -62,9 +76,7 @@ extern size_t next_power_of_2(size_t v); ...@@ -62,9 +76,7 @@ extern size_t next_power_of_2(size_t v);
(b), \ (b), \
((b)->i + (b)->pos), \ ((b)->i + (b)->pos), \
( \ ( \
((f)[(b)->i] >= 0) ? \ (BUFF_TYPE)(f)[(b)->i] \
((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MAX): \
(0.0 - ((BUFF_TYPE)(f)[(b)->i] / (BUFF_TYPE)INT16_MIN)) \
) \ ) \
); \ ); \
} \ } \
...@@ -102,5 +114,4 @@ extern size_t next_power_of_2(size_t v); ...@@ -102,5 +114,4 @@ extern size_t next_power_of_2(size_t v);
SET_SAMPLE((b), GET_CURRENT_LPOS((b)), (s)); \ SET_SAMPLE((b), GET_CURRENT_LPOS((b)), (s)); \
} while (0) } while (0)
#endif #endif /* __AVMD_BUFFER_H__ */
#ifndef __DESA2_H__ #ifndef __AVMD_DESA2_H__
#include <stdio.h> #include <stdio.h>
#ifdef WIN32 #ifdef WIN32
#include <float.h> #include <float.h>
...@@ -14,7 +16,7 @@ ...@@ -14,7 +16,7 @@
#include "avmd_fast_acosf.h" #include "avmd_fast_acosf.h"
#endif #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 d;
double n; double n;
...@@ -33,13 +35,10 @@ extern double desa2(circ_buffer_t *b, size_t i) ...@@ -33,13 +35,10 @@ extern double desa2(circ_buffer_t *b, size_t i)
x4 = GET_SAMPLE((b), ((i) + 4)); x4 = GET_SAMPLE((b), ((i) + 4));
x2sq = x2 * x2; x2sq = x2 * x2;
d = 2.0 * ((x2sq) - (x1 * x3)); d = 2.0 * ((x2sq) - (x1 * x3));
if (d == 0.0) return 0.0; if (d == 0.0) return 0.0;
n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4)); n = ((x2sq) - (x0 * x4)) - ((x1 * x1) - (x0 * x2)) - ((x3 * x3) - (x2 * x4));
#ifdef AVMD_FAST_MATH #ifdef AVMD_FAST_MATH
result = 0.5 * (double)fast_acosf((float)n/d); result = 0.5 * (double)fast_acosf((float)n/d);
#else #else
...@@ -52,4 +51,4 @@ extern double desa2(circ_buffer_t *b, size_t i) ...@@ -52,4 +51,4 @@ extern double desa2(circ_buffer_t *b, size_t i)
} }
#endif #endif /* __AVMD_DESA2_H__ */
#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 <math.h>
#include "avmd_buffer.h" #include "avmd_buffer.h"
extern double desa2(circ_buffer_t *b, size_t i); /* Returns digital frequency estimation. */
#endif extern double avmd_desa2(circ_buffer_t *b, size_t i);
#endif /* __AVMD_DESA2_H__ */
#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__ */
/*
* @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__ */
#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" #define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
/*! \brief Arc cosine table initialization. /*! \brief Arc cosine table initialization.
* *
* @author Eric des Courtis * @author Eric des Courtis
...@@ -42,5 +50,4 @@ extern float fast_acosf(float x); ...@@ -42,5 +50,4 @@ extern float fast_acosf(float x);
*/ */
extern int compute_table(void); extern int compute_table(void);
#endif #endif /* __AVMD_FAST_ACOSF_H__ */
/*
* @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
#ifndef __GOERTZEL_H__ #ifndef __AVMD_GOERTZEL_H__
#include <math.h> #include <math.h>
#include "avmd_goertzel.h" #include "avmd_goertzel.h"
#include "avmd_buffer.h" #include "avmd_buffer.h"
/*! \brief Identify frequency components of a signal
* @author Eric des Courtis extern double avmd_goertzel(circ_buffer_t *b, size_t pos, double f, size_t num)
* @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)
{ {
double s = 0.0; double s = 0.0;
double p = 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) ...@@ -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); coeff = 2.0 * cos(2.0 * M_PI * f);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
/* TODO: optimize to avoid GET_SAMPLE when possible */ /* TODO: optimize to avoid GET_SAMPLE when possible */
s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2; s = GET_SAMPLE(b, i + pos) + (coeff * p) - p2;
p2 = p; p2 = p;
p = s; p = s;
} }
return (p2 * p2) + (p * p) - (coeff * p2 * p); return (p2 * p2) + (p * p) - (coeff * p2 * p);
} }
#endif #endif /* __AVMD_GOERTZEL_H__ */
#ifndef __GOERTZEL_H__ /*
#define __GOERTZEL_H__ * @brief Goertzel algorithm.
* @author Eric des Courtis
*/
#ifndef __AVMD_GOERTZEL_H__
#define __AVMD_GOERTZEL_H__
#ifndef _MSC_VER #ifndef _MSC_VER
#include <stdint.h> #include <stdint.h>
...@@ -11,8 +18,16 @@ ...@@ -11,8 +18,16 @@
#define M_PI 3.14159265358979323846264338327 #define M_PI 3.14159265358979323846264338327
#endif #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 @@ ...@@ -10,11 +10,13 @@
#define __AVMD_OPTIONS_H__ #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 /* define/undef this to enable/disable reporting of beep
* detection status after session ended */ * detection status after session ended */
#define AVMD_REPORT_STATUS 1 #define AVMD_REPORT_STATUS
/* define/undefine this to enable/disable faster computation /* define/undefine this to enable/disable faster computation
* of arcus cosine - table will be created mapping floats * of arcus cosine - table will be created mapping floats
...@@ -25,7 +27,16 @@ ...@@ -25,7 +27,16 @@
/* define/undefine this to classify avmd beep detection as valid /* define/undefine this to classify avmd beep detection as valid
* only when there is required number of consecutive elements * only when there is required number of consecutive elements
* in the SMA buffer without reset */ * 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/undefine to enable/disable avmd on incoming audio */
#define AVMD_INBOUND_CHANNEL #define AVMD_INBOUND_CHANNEL
......
#ifndef __PSI_H__ #ifndef __AVMD_PSI_H__
#define __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 #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 /* __AVMD_PSI_H__ */
#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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#ifndef _MSC_VER #ifndef _MSC_VER
...@@ -64,7 +74,11 @@ typedef struct { ...@@ -64,7 +74,11 @@ typedef struct {
}while(0); }while(0);
*/ */
#endif
#endif /* __AVMD_SMA_BUFFER_H__ */
/* /*
int main(void) int main(void)
......
...@@ -154,4 +154,4 @@ ...@@ -154,4 +154,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论