From 9a136f905e91a45f24747485a90693c9f923f291 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 21 Aug 2008 20:58:43 +0000 Subject: [PATCH] add mod_stress (needs work) git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@9345 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/applications/mod_stress/FFTReal.cpp | 615 ++++++++++++++++++ src/mod/applications/mod_stress/FFTReal.h | 139 ++++ src/mod/applications/mod_stress/Makefile | 3 + .../applications/mod_stress/mod_stress.cpp | 266 ++++++++ 4 files changed, 1023 insertions(+) create mode 100644 src/mod/applications/mod_stress/FFTReal.cpp create mode 100644 src/mod/applications/mod_stress/FFTReal.h create mode 100644 src/mod/applications/mod_stress/Makefile create mode 100644 src/mod/applications/mod_stress/mod_stress.cpp diff --git a/src/mod/applications/mod_stress/FFTReal.cpp b/src/mod/applications/mod_stress/FFTReal.cpp new file mode 100644 index 0000000000..e1a77b465e --- /dev/null +++ b/src/mod/applications/mod_stress/FFTReal.cpp @@ -0,0 +1,615 @@ +/***************************************************************************** +* * +* DIGITAL SIGNAL PROCESSING TOOLS * +* Version 1.03, 2001/06/15 * +* (c) 1999 - Laurent de Soras * +* * +* FFTReal.cpp * +* Fourier transformation of real number arrays. * +* Portable ISO C++ * +* * +* Tab = 3 * +*****************************************************************************/ + + + +/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +#include "FFTReal.h" + +#include +#include + + + +#if defined (_MSC_VER) +#pragma pack (push, 8) +#endif // _MSC_VER + + + +/*\\\ PUBLIC MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - length: length of the array on which we want to do a FFT. */ +/* Range: power of 2 only, > 0. */ +/* Throws: std::bad_alloc, anything */ +/*==========================================================================*/ + +FFTReal::FFTReal (const long length) +: _length (length) +, _nbr_bits (int (floor (log (length) / log (2) + 0.5))) +, _bit_rev_lut (int (floor (log (length) / log (2) + 0.5))) +, _trigo_lut (int (floor (log (length) / log (2) + 0.5))) +, _sqrt2_2 (flt_t (sqrt (2) * 0.5)) +{ + assert ((1L << _nbr_bits) == length); + + _buffer_ptr = 0; + if (_nbr_bits > 2) + { + _buffer_ptr = new flt_t [_length]; + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::~FFTReal (void) +{ + delete [] _buffer_ptr; + _buffer_ptr = 0; +} + + + +/*==========================================================================*/ +/* Name: do_fft */ +/* Description: Compute the FFT of the array. */ +/* Input parameters: */ +/* - x: pointer on the source array (time). */ +/* Output parameters: */ +/* - f: pointer on the destination array (frequencies). */ +/* f [0...length(x)/2] = real values, */ +/* f [length(x)/2+1...length(x)-1] = imaginary values of */ +/* coefficents 1...length(x)/2-1. */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::do_fft (flt_t f [], const flt_t x []) const +{ + +/*______________________________________________ + * + * General case + *______________________________________________ + */ + + if (_nbr_bits > 2) + { + flt_t * sf; + flt_t * df; + + if (_nbr_bits & 1) + { + df = _buffer_ptr; + sf = f; + } + else + { + df = f; + sf = _buffer_ptr; + } + + /* Do the transformation in several pass */ + { + int pass; + long nbr_coef; + long h_nbr_coef; + long d_nbr_coef; + long coef_index; + + /* First and second pass at once */ + { + const long * const bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); + coef_index = 0; + do + { + const long rev_index_0 = bit_rev_lut_ptr [coef_index]; + const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1]; + const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2]; + const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3]; + + flt_t * const df2 = df + coef_index; + df2 [1] = x [rev_index_0] - x [rev_index_1]; + df2 [3] = x [rev_index_2] - x [rev_index_3]; + + const flt_t sf_0 = x [rev_index_0] + x [rev_index_1]; + const flt_t sf_2 = x [rev_index_2] + x [rev_index_3]; + + df2 [0] = sf_0 + sf_2; + df2 [2] = sf_0 - sf_2; + + coef_index += 4; + } + while (coef_index < _length); + } + + /* Third pass */ + { + coef_index = 0; + const flt_t sqrt2_2 = _sqrt2_2; + do + { + flt_t v; + + sf [coef_index] = df [coef_index] + df [coef_index + 4]; + sf [coef_index + 4] = df [coef_index] - df [coef_index + 4]; + sf [coef_index + 2] = df [coef_index + 2]; + sf [coef_index + 6] = df [coef_index + 6]; + + v = (df [coef_index + 5] - df [coef_index + 7]) * sqrt2_2; + sf [coef_index + 1] = df [coef_index + 1] + v; + sf [coef_index + 3] = df [coef_index + 1] - v; + + v = (df [coef_index + 5] + df [coef_index + 7]) * sqrt2_2; + sf [coef_index + 5] = v + df [coef_index + 3]; + sf [coef_index + 7] = v - df [coef_index + 3]; + + coef_index += 8; + } + while (coef_index < _length); + } + + /* Next pass */ + for (pass = 3; pass < _nbr_bits; ++pass) + { + coef_index = 0; + nbr_coef = 1 << pass; + h_nbr_coef = nbr_coef >> 1; + d_nbr_coef = nbr_coef << 1; + const flt_t * const cos_ptr = _trigo_lut.get_ptr (pass); + do + { + long i; + const flt_t * const sf1r = sf + coef_index; + const flt_t * const sf2r = sf1r + nbr_coef; + flt_t * const dfr = df + coef_index; + flt_t * const dfi = dfr + nbr_coef; + + /* Extreme coefficients are always real */ + dfr [0] = sf1r [0] + sf2r [0]; + dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] = + dfr [h_nbr_coef] = sf1r [h_nbr_coef]; + dfi [h_nbr_coef] = sf2r [h_nbr_coef]; + + /* Others are conjugate complex numbers */ + const flt_t * const sf1i = sf1r + h_nbr_coef; + const flt_t * const sf2i = sf1i + nbr_coef; + for (i = 1; i < h_nbr_coef; ++ i) + { + const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); + const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); + flt_t v; + + v = sf2r [i] * c - sf2i [i] * s; + dfr [i] = sf1r [i] + v; + dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] = + + v = sf2r [i] * s + sf2i [i] * c; + dfi [i] = v + sf1i [i]; + dfi [nbr_coef - i] = v - sf1i [i]; + } + + coef_index += d_nbr_coef; + } + while (coef_index < _length); + + /* Prepare to the next pass */ + { + flt_t * const temp_ptr = df; + df = sf; + sf = temp_ptr; + } + } + } + } + +/*______________________________________________ + * + * Special cases + *______________________________________________ + */ + + /* 4-point FFT */ + else if (_nbr_bits == 2) + { + f [1] = x [0] - x [2]; + f [3] = x [1] - x [3]; + + const flt_t b_0 = x [0] + x [2]; + const flt_t b_2 = x [1] + x [3]; + + f [0] = b_0 + b_2; + f [2] = b_0 - b_2; + } + + /* 2-point FFT */ + else if (_nbr_bits == 1) + { + f [0] = x [0] + x [1]; + f [1] = x [0] - x [1]; + } + + /* 1-point FFT */ + else + { + f [0] = x [0]; + } +} + + + +/*==========================================================================*/ +/* Name: do_ifft */ +/* Description: Compute the inverse FFT of the array. Notice that */ +/* IFFT (FFT (x)) = x * length (x). Data must be */ +/* post-scaled. */ +/* Input parameters: */ +/* - f: pointer on the source array (frequencies). */ +/* f [0...length(x)/2] = real values, */ +/* f [length(x)/2+1...length(x)] = imaginary values of */ +/* coefficents 1...length(x)-1. */ +/* Output parameters: */ +/* - x: pointer on the destination array (time). */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::do_ifft (const flt_t f [], flt_t x []) const +{ + +/*______________________________________________ + * + * General case + *______________________________________________ + */ + + if (_nbr_bits > 2) + { + flt_t * sf = const_cast (f); + flt_t * df; + flt_t * df_temp; + + if (_nbr_bits & 1) + { + df = _buffer_ptr; + df_temp = x; + } + else + { + df = x; + df_temp = _buffer_ptr; + } + + /* Do the transformation in several pass */ + { + int pass; + long nbr_coef; + long h_nbr_coef; + long d_nbr_coef; + long coef_index; + + /* First pass */ + for (pass = _nbr_bits - 1; pass >= 3; --pass) + { + coef_index = 0; + nbr_coef = 1 << pass; + h_nbr_coef = nbr_coef >> 1; + d_nbr_coef = nbr_coef << 1; + const flt_t *const cos_ptr = _trigo_lut.get_ptr (pass); + do + { + long i; + const flt_t * const sfr = sf + coef_index; + const flt_t * const sfi = sfr + nbr_coef; + flt_t * const df1r = df + coef_index; + flt_t * const df2r = df1r + nbr_coef; + + /* Extreme coefficients are always real */ + df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef] + df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef] + df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2; + df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2; + + /* Others are conjugate complex numbers */ + flt_t * const df1i = df1r + h_nbr_coef; + flt_t * const df2i = df1i + nbr_coef; + for (i = 1; i < h_nbr_coef; ++ i) + { + df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i] + df1i [i] = sfi [i] - sfi [nbr_coef - i]; + + const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef); + const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef); + const flt_t vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i] + const flt_t vi = sfi [i] + sfi [nbr_coef - i]; + + df2r [i] = vr * c + vi * s; + df2i [i] = vi * c - vr * s; + } + + coef_index += d_nbr_coef; + } + while (coef_index < _length); + + /* Prepare to the next pass */ + if (pass < _nbr_bits - 1) + { + flt_t * const temp_ptr = df; + df = sf; + sf = temp_ptr; + } + else + { + sf = df; + df = df_temp; + } + } + + /* Antepenultimate pass */ + { + const flt_t sqrt2_2 = _sqrt2_2; + coef_index = 0; + do + { + df [coef_index] = sf [coef_index] + sf [coef_index + 4]; + df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4]; + df [coef_index + 2] = sf [coef_index + 2] * 2; + df [coef_index + 6] = sf [coef_index + 6] * 2; + + df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3]; + df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7]; + + const flt_t vr = sf [coef_index + 1] - sf [coef_index + 3]; + const flt_t vi = sf [coef_index + 5] + sf [coef_index + 7]; + + df [coef_index + 5] = (vr + vi) * sqrt2_2; + df [coef_index + 7] = (vi - vr) * sqrt2_2; + + coef_index += 8; + } + while (coef_index < _length); + } + + /* Penultimate and last pass at once */ + { + coef_index = 0; + const long * bit_rev_lut_ptr = _bit_rev_lut.get_ptr (); + const flt_t * sf2 = df; + do + { + { + const flt_t b_0 = sf2 [0] + sf2 [2]; + const flt_t b_2 = sf2 [0] - sf2 [2]; + const flt_t b_1 = sf2 [1] * 2; + const flt_t b_3 = sf2 [3] * 2; + + x [bit_rev_lut_ptr [0]] = b_0 + b_1; + x [bit_rev_lut_ptr [1]] = b_0 - b_1; + x [bit_rev_lut_ptr [2]] = b_2 + b_3; + x [bit_rev_lut_ptr [3]] = b_2 - b_3; + } + { + const flt_t b_0 = sf2 [4] + sf2 [6]; + const flt_t b_2 = sf2 [4] - sf2 [6]; + const flt_t b_1 = sf2 [5] * 2; + const flt_t b_3 = sf2 [7] * 2; + + x [bit_rev_lut_ptr [4]] = b_0 + b_1; + x [bit_rev_lut_ptr [5]] = b_0 - b_1; + x [bit_rev_lut_ptr [6]] = b_2 + b_3; + x [bit_rev_lut_ptr [7]] = b_2 - b_3; + } + + sf2 += 8; + coef_index += 8; + bit_rev_lut_ptr += 8; + } + while (coef_index < _length); + } + } + } + +/*______________________________________________ + * + * Special cases + *______________________________________________ + */ + + /* 4-point IFFT */ + else if (_nbr_bits == 2) + { + const flt_t b_0 = f [0] + f [2]; + const flt_t b_2 = f [0] - f [2]; + + x [0] = b_0 + f [1] * 2; + x [2] = b_0 - f [1] * 2; + x [1] = b_2 + f [3] * 2; + x [3] = b_2 - f [3] * 2; + } + + /* 2-point IFFT */ + else if (_nbr_bits == 1) + { + x [0] = f [0] + f [1]; + x [1] = f [0] - f [1]; + } + + /* 1-point IFFT */ + else + { + x [0] = f [0]; + } +} + + + +/*==========================================================================*/ +/* Name: rescale */ +/* Description: Scale an array by divide each element by its length. */ +/* This function should be called after FFT + IFFT. */ +/* Input/Output parameters: */ +/* - x: pointer on array to rescale (time or frequency). */ +/* Throws: Nothing */ +/*==========================================================================*/ + +void FFTReal::rescale (flt_t x []) const +{ + const flt_t mul = flt_t (1.0 / _length); + long i = _length - 1; + + do + { + x [i] *= mul; + --i; + } + while (i >= 0); +} + + + +/*\\\ NESTED CLASS MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - nbr_bits: number of bits of the array on which we want to do a */ +/* FFT. Range: > 0 */ +/* Throws: std::bad_alloc */ +/*==========================================================================*/ + +FFTReal::BitReversedLUT::BitReversedLUT (const int nbr_bits) +{ + long length; + long cnt; + long br_index; + long bit; + + length = 1L << nbr_bits; + _ptr = new long [length]; + + br_index = 0; + _ptr [0] = 0; + for (cnt = 1; cnt < length; ++cnt) + { + /* ++br_index (bit reversed) */ + bit = length >> 1; + while (((br_index ^= bit) & bit) == 0) + { + bit >>= 1; + } + + _ptr [cnt] = br_index; + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::BitReversedLUT::~BitReversedLUT (void) +{ + delete [] _ptr; + _ptr = 0; +} + + + +/*==========================================================================*/ +/* Name: Constructor */ +/* Input parameters: */ +/* - nbr_bits: number of bits of the array on which we want to do a */ +/* FFT. Range: > 0 */ +/* Throws: std::bad_alloc, anything */ +/*==========================================================================*/ + +FFTReal::TrigoLUT::TrigoLUT (const int nbr_bits) +{ + long total_len; + + _ptr = 0; + if (nbr_bits > 3) + { + total_len = (1L << (nbr_bits - 1)) - 4; + _ptr = new flt_t [total_len]; + + const double PI = atan (1) * 4; + for (int level = 3; level < nbr_bits; ++level) + { + const long level_len = 1L << (level - 1); + flt_t * const level_ptr = const_cast (get_ptr (level)); + const double mul = PI / (level_len << 1); + + for (long i = 0; i < level_len; ++ i) + { + level_ptr [i] = (flt_t) cos (i * mul); + } + } + } +} + + + +/*==========================================================================*/ +/* Name: Destructor */ +/*==========================================================================*/ + +FFTReal::TrigoLUT::~TrigoLUT (void) +{ + delete [] _ptr; + _ptr = 0; +} + + + +#if defined (_MSC_VER) +#pragma pack (pop) +#endif // _MSC_VER + + + +/***************************************************************************** + + LEGAL + + Source code may be freely used for any purpose, including commercial + applications. Programs must display in their "About" dialog-box (or + documentation) a text telling they use these routines by Laurent de Soras. + Modified source code can be distributed, but modifications must be clearly + indicated. + + CONTACT + + Laurent de Soras + 92 avenue Albert 1er + 92500 Rueil-Malmaison + France + + ldesoras@club-internet.fr + +*****************************************************************************/ + + + +/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/src/mod/applications/mod_stress/FFTReal.h b/src/mod/applications/mod_stress/FFTReal.h new file mode 100644 index 0000000000..792a97beac --- /dev/null +++ b/src/mod/applications/mod_stress/FFTReal.h @@ -0,0 +1,139 @@ +/***************************************************************************** +* * +* DIGITAL SIGNAL PROCESSING TOOLS * +* Version 1.03, 2001/06/15 * +* (c) 1999 - Laurent de Soras * +* * +* FFTReal.h * +* Fourier transformation of real number arrays. * +* Portable ISO C++ * +* * +* Tab = 3 * +*****************************************************************************/ + + + +#if defined (FFTReal_CURRENT_HEADER) + #error Recursive inclusion of FFTReal header file. +#endif +#define FFTReal_CURRENT_HEADER + +#if ! defined (FFTReal_HEADER_INCLUDED) +#define FFTReal_HEADER_INCLUDED + + + +#if defined (_MSC_VER) +#pragma pack (push, 8) +#endif // _MSC_VER + + + +class FFTReal +{ + +/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +public: + + /* Change this typedef to use a different floating point type in your FFTs + (i.e. float, double or long double). */ + typedef float flt_t; + + explicit FFTReal (const long length); + ~FFTReal (); + void do_fft (flt_t f [], const flt_t x []) const; + void do_ifft (const flt_t f [], flt_t x []) const; + void rescale (flt_t x []) const; + + + +/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +private: + + /* Bit-reversed look-up table nested class */ + class BitReversedLUT + { + public: + explicit BitReversedLUT (const int nbr_bits); + ~BitReversedLUT (); + const long * get_ptr () const + { + return (_ptr); + } + private: + long * _ptr; + }; + + /* Trigonometric look-up table nested class */ + class TrigoLUT + { + public: + explicit TrigoLUT (const int nbr_bits); + ~TrigoLUT (); + const flt_t * get_ptr (const int level) const + { + return (_ptr + (1L << (level - 1)) - 4); + }; + private: + flt_t * _ptr; + }; + + const BitReversedLUT _bit_rev_lut; + const TrigoLUT _trigo_lut; + const flt_t _sqrt2_2; + const long _length; + const int _nbr_bits; + flt_t * _buffer_ptr; + + + +/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ + +private: + + FFTReal (const FFTReal &other); + const FFTReal& operator = (const FFTReal &other); + int operator == (const FFTReal &other); + int operator != (const FFTReal &other); +}; + + + +#if defined (_MSC_VER) +#pragma pack (pop) +#endif // _MSC_VER + + + +#endif // FFTReal_HEADER_INCLUDED + +#undef FFTReal_CURRENT_HEADER + + + +/***************************************************************************** + + LEGAL + + Source code may be freely used for any purpose, including commercial + applications. Programs must display in their "About" dialog-box (or + documentation) a text telling they use these routines by Laurent de Soras. + Modified source code can be distributed, but modifications must be clearly + indicated. + + CONTACT + + Laurent de Soras + 92 avenue Albert 1er + 92500 Rueil-Malmaison + France + + ldesoras@club-internet.fr + +*****************************************************************************/ + + + +/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ diff --git a/src/mod/applications/mod_stress/Makefile b/src/mod/applications/mod_stress/Makefile new file mode 100644 index 0000000000..a3203c70ff --- /dev/null +++ b/src/mod/applications/mod_stress/Makefile @@ -0,0 +1,3 @@ +BASE=../../../.. +LOCAL_OBJS=FFTReal.o +include $(BASE)/build/modmake.rules diff --git a/src/mod/applications/mod_stress/mod_stress.cpp b/src/mod/applications/mod_stress/mod_stress.cpp new file mode 100644 index 0000000000..552085af9e --- /dev/null +++ b/src/mod/applications/mod_stress/mod_stress.cpp @@ -0,0 +1,266 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * mod_stress.cpp -- Detect Voice Stress + * + */ + +#include +#include +#include "FFTReal.h" +using namespace std; + +#include + +SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown); +SWITCH_MODULE_DEFINITION(mod_stress, mod_stress_load, mod_stress_shutdown, NULL); + +struct stress_helper { + switch_core_session_t *session; + int read; + uint32_t frame_size; + FFTReal *fft; + float *data; + float *result; + float *pow_spectrum; + float bind; + int start; + int end; + float avg_tremor_pwr; + float avg_total_pwr; + float total_pwr; + float tremor_ratio; + float stress; + uint32_t rate; + switch_buffer_t *audio_buffer; + int16_t *audio; +}; + +static switch_bool_t stress_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + struct stress_helper *sth = (struct stress_helper *) user_data; + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + { + switch_codec_t *read_codec = switch_core_session_get_read_codec(sth->session); + + sth->rate = read_codec->implementation->actual_samples_per_second; + + if (sth->rate == 8000) { + sth->frame_size = 8192; + } else if (sth->rate == 16000) { + sth->frame_size = 16384; + } else if (sth->rate == 32000) { + sth->frame_size = 32768; + } else { + return SWITCH_FALSE; + } + + sth->data = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->data) * sth->frame_size); + sth->result = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->result) * sth->frame_size); + sth->pow_spectrum = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->pow_spectrum) * sth->frame_size); + sth->audio = (int16_t *) switch_core_session_alloc(sth->session, sizeof(*sth->audio) * sth->frame_size); + + sth->fft = new FFTReal (sth->frame_size); + switch_buffer_create_dynamic(&sth->audio_buffer, sth->frame_size, sth->frame_size * 3, 0); + + sth->bind = (float) sth->rate / sth->frame_size; + sth->start = (int) (8.0 / sth->bind); + sth->end = (int) (14.0 / sth->bind); + + } + break; + case SWITCH_ABC_TYPE_CLOSE: + { + switch_buffer_destroy(&sth->audio_buffer); + delete sth->fft; + } + break; + case SWITCH_ABC_TYPE_READ: + case SWITCH_ABC_TYPE_WRITE: + break; + case SWITCH_ABC_TYPE_READ_REPLACE: + case SWITCH_ABC_TYPE_WRITE_REPLACE: + { + switch_frame_t *frame; + + if (sth->read) { + frame = switch_core_media_bug_get_read_replace_frame(bug); + } else { + frame = switch_core_media_bug_get_write_replace_frame(bug); + } + + if (!switch_test_flag(frame, SFF_CNG)) { + switch_buffer_write(sth->audio_buffer, frame->data, frame->datalen); + } + + sth->stress = 0.0; + + if (switch_buffer_inuse(sth->audio_buffer) >= sth->frame_size * sizeof(int16_t)) { + switch_size_t bytes; + uint32_t samples, i; + const float threshold = 1.5; + + bytes = switch_buffer_read(sth->audio_buffer, sth->audio, sth->frame_size * sizeof(int16_t)); + samples = bytes / sizeof(int16_t); + + switch_short_to_float(sth->audio, sth->data, samples); + sth->fft->do_fft(sth->result, sth->data); + + for (i = 0; i < samples; ++i) { + sth->pow_spectrum[i] = pow(fabs(sth->result[i]), 2) / (float) samples; + } + + sth->avg_tremor_pwr = 0.0; + sth->avg_total_pwr = 0.0; + sth->total_pwr = 0.0; + + for (i = sth->start; i <= sth->end; ++i) { + sth->avg_tremor_pwr += sth->pow_spectrum[i]; + } + sth->avg_tremor_pwr /= ((sth->end - sth->start) + 1); + + for (i = 0; i < samples; ++i) { + sth->total_pwr += sth->pow_spectrum[i]; + } + sth->avg_total_pwr = sth->total_pwr / samples; + + if (sth->total_pwr < threshold) { + sth->tremor_ratio = 0.0; + } else { + sth->tremor_ratio = sth->avg_tremor_pwr / sth->avg_total_pwr; + } + + if (sth->total_pwr >= 1.0) { + float d = pow(sth->tremor_ratio, 4); + if (d > 0.0) { + sth->stress = (10.0 / d) / 10000; + if (sth->stress >= 20000.0) { + sth->stress = 20000.0; + } + } + } + } + + if (sth->stress) { + switch_event_t *event, *dup; + + if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "stress-level"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Stress-Level", "%02f", sth->stress); + if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) { + switch_event_fire(&dup); + } + if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Event queue failed!\n"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + } + } + default: + break; + } + + return SWITCH_TRUE; +} + +SWITCH_STANDARD_APP(stress_start_function) +{ + switch_media_bug_t *bug; + switch_status_t status; + switch_channel_t *channel = switch_core_session_get_channel(session); + struct stress_helper *sth; + char *argv[6]; + int argc; + char *lbuf = NULL; + int x = 0; + + if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_stress_"))) { + if (!switch_strlen_zero(data) && !strcasecmp(data, "stop")) { + switch_channel_set_private(channel, "_stress_", NULL); + switch_core_media_bug_remove(session, &bug); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n"); + } + return; + } + + sth = (struct stress_helper *) switch_core_session_alloc(session, sizeof(*sth)); + assert(sth != NULL); + + + if (data && (lbuf = switch_core_session_strdup(session, data)) + && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + if (!strncasecmp(argv[x], "read", 4)) { + sth->read = 1; + } + } + + sth->session = session; + + if ((status = switch_core_media_bug_add(session, stress_callback, sth, 0, + sth->read ? SMBF_READ_REPLACE : SMBF_WRITE_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure!\n"); + return; + } + + switch_channel_set_private(channel, "_stress_", bug); + +} + +SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load) +{ + switch_application_interface_t *app_interface; + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + SWITCH_ADD_APP(app_interface, "stress", "Analyze the stream for voice stress", "Analyze the stream for voice stress", + stress_start_function, "[read|write|stop]", SAF_NONE); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown) +{ + return SWITCH_STATUS_UNLOAD; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */