dahdi-linux/drivers/dahdi/dahdi_echocan_mg2.c
Shaun Ruffell bf3fe05dfb wct4xxp: Moving the transmit short detection behind debug module param.
This needs some more testing before it's on by default.  If the card is
otherwise functioning, these messages may be confusing to the user.  If
the card is not functioning, the driver can be reloaded with debug to
check for this condition.

Signed-off-by: Shaun Ruffell <sruffell@digium.com>

git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9205 a0bf4364-ded3-4de4-8d8a-66a801d63aff
2010-08-27 21:59:27 +00:00

892 lines
26 KiB
C

/*
* ECHO_CAN_MG2
*
* by Michael Gernoth
*
* Based upon kb1ec.h and mec2.h
*
* Copyright (C) 2002, Digium, Inc.
*
* Additional background on the techniques used in this code can be found in:
*
* Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine;
* Winship, Peter; "Digital Voice Echo Canceller with a TMS32020,"
* in Digital Signal Processing Applications with the TMS320 Family,
* pp. 415-437, Texas Instruments, Inc., 1986.
*
* A pdf of which is available by searching on the document title at http://www.ti.com/
*
*/
/*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ctype.h>
#include <linux/moduleparam.h>
#include <dahdi/kernel.h>
static int debug;
static int aggressive;
#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args)
#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args)
#define ABS(a) abs(a!=-32768?a:-32767)
#define RESTORE_COEFFS {\
int x;\
memcpy(pvt->a_i, pvt->c_i, pvt->N_d*sizeof(int));\
for (x = 0; x < pvt->N_d; x++) {\
pvt->a_s[x] = pvt->a_i[x] >> 16;\
}\
pvt->backup = BACKUP;\
}
/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */
/* #define MEC2_STATS 4000 */
/* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */
/* #define MEC2_STATS_DETAILED */
/* Uncomment to generate per-call DC bias offset messages */
/* #define MEC2_DCBIAS_MESSAGE */
/* Get optimized routines for math */
#include "arith.h"
/*
Important constants for tuning mg2 echo can
*/
/* Convergence (aka. adaptation) speed -- higher means slower */
#define DEFAULT_BETA1_I 2048
/* Constants for various power computations */
#define DEFAULT_SIGMA_LY_I 7
#define DEFAULT_SIGMA_LU_I 7
#define DEFAULT_ALPHA_ST_I 5 /* near-end speech detection sensitivity factor */
#define DEFAULT_ALPHA_YT_I 5
#define DEFAULT_CUTOFF_I 128
/* Define the near-end speech hangover counter: if near-end speech
* is declared, hcntr is set equal to hangt (see pg. 432)
*/
#define DEFAULT_HANGT 600 /* in samples, so 600 samples = 75ms */
/* define the residual error suppression threshold */
#define DEFAULT_SUPPR_I 16 /* 16 = -24db */
/* This is the minimum reference signal power estimate level
* that will result in filter adaptation.
* If this is too low then background noise will cause the filter
* coefficients to constantly be updated.
*/
#define MIN_UPDATE_THRESH_I 2048
/* The number of samples used to update coefficients using the
* the block update method (M). It should be related back to the
* length of the echo can.
* ie. it only updates coefficients when (sample number MOD default_m) = 0
*
* Getting this wrong may cause an oops. Consider yourself warned!
*/
#define DEFAULT_M 16 /* every 16th sample */
/* If AGGRESSIVE supression is enabled, then we start cancelling residual
* echos again even while there is potentially the very end of a near-side
* signal present.
* This defines how many samples of DEFAULT_HANGT can remain before we
* kick back in
*/
#define AGGRESSIVE_HCNTR 160 /* in samples, so 160 samples = 20ms */
/* Treat sample as error if it has a different sign as the
* input signal and is this number larger in ABS() as
* the input-signal */
#define MAX_SIGN_ERROR 3000
/* Number of coefficients really used for calculating the
* simulated echo. The value specifies how many of the
* biggest coefficients are used for calculating rs.
* This helps on long echo-tails by artificially limiting
* the number of coefficients for the calculation and
* preventing overflows.
* Comment this to deactivate the code */
#define USED_COEFFS 64
/* Backup coefficients every this number of samples */
#define BACKUP 256
/***************************************************************/
/* The following knobs are not implemented in the current code */
/* we need a dynamic level of suppression varying with the ratio of the
power of the echo to the power of the reference signal this is
done so that we have a smoother background.
we have a higher suppression when the power ratio is closer to
suppr_ceil and reduces logarithmically as we approach suppr_floor.
*/
#define SUPPR_FLOOR -64
#define SUPPR_CEIL -24
/* in a second departure, we calculate the residual error suppression
* as a percentage of the reference signal energy level. The threshold
* is defined in terms of dB below the reference signal.
*/
#define RES_SUPR_FACTOR -20
#define DC_NORMALIZE
#ifndef NULL
#define NULL 0
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
/* Generic circular buffer definition */
typedef struct {
/* Pointer to the relative 'start' of the buffer */
int idx_d;
/* The absolute size of the buffer */
int size_d;
/* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */
short *buf_d;
} echo_can_cb_s;
static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec);
static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size);
static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val);
static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable);
static const struct dahdi_echocan_factory my_factory = {
.name = "MG2",
.owner = THIS_MODULE,
.echocan_create = echo_can_create,
};
static const struct dahdi_echocan_features my_features = {
.NLP_toggle = 1,
};
static const struct dahdi_echocan_ops my_ops = {
.name = "MG2",
.echocan_free = echo_can_free,
.echocan_process = echo_can_process,
.echocan_traintap = echo_can_traintap,
.echocan_NLP_toggle = echocan_NLP_toggle,
};
struct ec_pvt {
struct dahdi_echocan_state dahdi;
/* an arbitrary ID for this echo can - this really should be settable from the calling channel... */
int id;
/* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */
int i_d;
/* Pre-computed constants */
/* ---------------------- */
/* Number of filter coefficents */
int N_d;
/* Rate of adaptation of filter */
int beta2_i;
/* Accumulators for power computations */
/* ----------------------------------- */
/* reference signal power estimate - aka. Average absolute value of y(k) */
int Ly_i;
/* ... */
int Lu_i;
/* Accumulators for signal detectors */
/* --------------------------------- */
/* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */
int s_tilde_i;
/* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */
int y_tilde_i;
/* Near end speech detection counter - stores Hangover counter time remaining, in samples */
int HCNTR_d;
/* Circular buffers and coefficients */
/* --------------------------------- */
/* ... */
int *a_i;
/* ... */
short *a_s;
/* Backups */
int *b_i;
int *c_i;
/* Reference samples of far-end receive signal */
echo_can_cb_s y_s;
/* Reference samples of near-end signal */
echo_can_cb_s s_s;
/* Reference samples of near-end signal minus echo estimate */
echo_can_cb_s u_s;
/* Reference samples of far-end receive signal used to calculate short-time average */
echo_can_cb_s y_tilde_s;
/* Peak far-end receive signal */
/* --------------------------- */
/* Highest y_tilde value in the sample buffer */
short max_y_tilde;
/* Index of the sample containing the max_y_tilde value */
int max_y_tilde_pos;
#ifdef MEC2_STATS
/* Storage for performance statistics */
int cntr_nearend_speech_frames;
int cntr_residualcorrected_frames;
int cntr_residualcorrected_framesskipped;
int cntr_coeff_updates;
int cntr_coeff_missedupdates;
int avg_Lu_i_toolow;
int avg_Lu_i_ok;
#endif
unsigned int aggressive:1;
short lastsig;
int lastcount;
int backup;
#ifdef DC_NORMALIZE
int dc_estimate;
#endif
int use_nlp;
};
#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi)
static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where)
{
cb->buf_d = (short *)where;
cb->idx_d = 0;
cb->size_d = len;
}
static inline void add_cc_s(echo_can_cb_s *cb, short newval)
{
/* Can't use modulus because N+M isn't a power of two (generally) */
cb->idx_d--;
if (cb->idx_d < (int)0)
/* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */
cb->idx_d += cb->size_d;
/* Load two copies into memory */
cb->buf_d[cb->idx_d] = newval;
cb->buf_d[cb->idx_d + cb->size_d] = newval;
}
static inline short get_cc_s(echo_can_cb_s *cb, int pos)
{
/* Load two copies into memory */
return cb->buf_d[cb->idx_d + pos];
}
static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu)
{
void *ptr = pvt;
unsigned long tmp;
/* Double-word align past end of state */
ptr += sizeof(*pvt);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
pvt->N_d = N;
pvt->beta2_i = DEFAULT_BETA1_I;
/* Allocate coefficient memory */
pvt->a_i = ptr;
ptr += (sizeof(int) * pvt->N_d);
pvt->a_s = ptr;
ptr += (sizeof(short) * pvt->N_d);
/* Allocate backup memory */
pvt->b_i = ptr;
ptr += (sizeof(int) * pvt->N_d);
pvt->c_i = ptr;
ptr += (sizeof(int) * pvt->N_d);
/* Reset Y circular buffer (short version) */
init_cb_s(&pvt->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sigma circular buffer (short version for FIR filter) */
init_cb_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2);
init_cb_s(&pvt->u_s, maxu, ptr);
ptr += (sizeof(short) * maxu * 2);
/* Allocate a buffer for the reference signal power computation */
init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr);
/* Reset the absolute time index */
pvt->i_d = (int)0;
/* Reset the power computations (for y and u) */
pvt->Ly_i = DEFAULT_CUTOFF_I;
pvt->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
pvt->id = (int)&ptr;
/* Reset performance stats */
pvt->cntr_nearend_speech_frames = (int)0;
pvt->cntr_residualcorrected_frames = (int)0;
pvt->cntr_residualcorrected_framesskipped = (int)0;
pvt->cntr_coeff_updates = (int)0;
pvt->cntr_coeff_missedupdates = (int)0;
pvt->avg_Lu_i_toolow = (int)0;
pvt->avg_Lu_i_ok = (int)0;
#endif
/* Reset the near-end speech detector */
pvt->s_tilde_i = (int)0;
pvt->y_tilde_i = (int)0;
pvt->HCNTR_d = (int)0;
}
static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
{
struct ec_pvt *pvt = dahdi_to_pvt(ec);
#if defined(DC_NORMALIZE) && defined(MEC2_DCBIAS_MESSAGE)
printk(KERN_INFO "EC: DC bias calculated: %d V\n", pvt->dc_estimate >> 15);
#endif
kfree(pvt);
}
#ifdef DC_NORMALIZE
short inline dc_removal(int *dc_estimate, short samp)
{
*dc_estimate += ((((int)samp << 15) - *dc_estimate) >> 9);
return samp - (*dc_estimate >> 15);
}
#endif
static inline short sample_update(struct ec_pvt *pvt, short iref, short isig)
{
/* Declare local variables that are used more than once */
/* ... */
int k;
/* ... */
int rs;
/* ... */
short u;
/* ... */
int Py_i;
/* ... */
int two_beta_i;
#ifdef DC_NORMALIZE
isig = dc_removal(&pvt->dc_estimate, isig);
#endif
/* flow A on pg. 428 */
/* eq. (16): high-pass filter the input to generate the next value;
* push the current value into the circular buffer
*
* sdc_im1_d = sdc_d;
* sdc_d = sig;
* s_i_d = sdc_d;
* s_d = s_i_d;
* s_i_d = (float)(1.0 - gamma_d) * s_i_d
* + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d);
*/
/* Update the Far-end receive signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
pvt->y_tilde_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1)) >> DEFAULT_ALPHA_YT_I;
/* Add the new sample to the power estimate accumulator */
pvt->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
/* Push a copy of the new sample into its circular buffer */
add_cc_s(&pvt->y_s, iref);
/* eq. (2): compute r in fixed-point */
rs = CONVOLVE2(pvt->a_s,
pvt->y_s.buf_d + pvt->y_s.idx_d,
pvt->N_d);
rs >>= 15;
if (pvt->lastsig == isig) {
pvt->lastcount++;
} else {
pvt->lastcount = 0;
pvt->lastsig = isig;
}
if (isig == 0) {
u = 0;
} else if (pvt->lastcount > 255) {
/* We have seen the same input-signal more than 255 times,
* we should pass it through uncancelled, as we are likely on hold */
u = isig;
} else {
int sign_error;
if (rs < -32768) {
rs = -32768;
pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
} else if (rs > 32767) {
rs = 32767;
pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
}
sign_error = ABS(rs) - ABS(isig);
if (ABS(sign_error) > MAX_SIGN_ERROR)
{
rs = 0;
RESTORE_COEFFS;
}
/* eq. (3): compute the output value (see figure 3) and the error
* note: the error is the same as the output signal when near-end
* speech is not present
*/
u = isig - rs;
if (u / isig < 0)
u = isig - (rs >> 1);
}
/* Push a copy of the output value sample into its circular buffer */
add_cc_s(&pvt->u_s, u);
if (!pvt->backup) {
/* Backup coefficients periodically */
pvt->backup = BACKUP;
memcpy(pvt->c_i, pvt->b_i, pvt->N_d*sizeof(int));
memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
} else
pvt->backup--;
/* Update the Near-end hybrid signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
pvt->s_tilde_i -= abs(get_cc_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1));
/* Add the new sample to the power estimate accumulator */
pvt->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
add_cc_s(&pvt->s_s, isig);
/* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */
add_cc_s(&pvt->y_tilde_s, pvt->y_tilde_i);
/* flow B on pg. 428 */
/* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */
if (!pvt->HCNTR_d) {
Py_i = (pvt->Ly_i >> DEFAULT_SIGMA_LY_I) * (pvt->Ly_i >> DEFAULT_SIGMA_LY_I);
Py_i >>= 15;
} else {
Py_i = (1 << 15);
}
#if 0
/* Vary rate of adaptation depending on position in the file
* Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech
* has begun of the file to allow the echo cancellor to estimate the
* channel accurately
* Still needs conversion!
*/
if (pvt->start_speech_d != 0) {
if (pvt->i_d > (DEFAULT_T0 + pvt->start_speech_d)*(SAMPLE_FREQ)) {
pvt->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((pvt->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - pvt->start_speech_d)));
}
} else {
pvt->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
pvt->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
two_beta_i = (pvt->beta2_i * Py_i) >> 15;
if (!two_beta_i)
two_beta_i++;
/* Update the Suppressed signal power estimate accumulator */
/* ------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
pvt->Lu_i -= abs(get_cc_s(&pvt->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1));
/* Add the new sample to the power estimate accumulator */
pvt->Lu_i += abs(u);
/* Update the Far-end reference signal power estimate accumulator */
/* -------------------------------------------------------------- */
/* eq. (10): update power estimate of the reference */
/* Delete the oldest sample from the power estimate accumulator */
pvt->Ly_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
/* Add the new sample to the power estimate accumulator */
pvt->Ly_i += abs(iref);
if (pvt->Ly_i < DEFAULT_CUTOFF_I)
pvt->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
if (pvt->y_tilde_i > pvt->max_y_tilde) {
/* New highest y_tilde with full life */
pvt->max_y_tilde = pvt->y_tilde_i;
pvt->max_y_tilde_pos = pvt->N_d - 1;
} else if (--pvt->max_y_tilde_pos < 0) {
/* Time to find new max y tilde... */
pvt->max_y_tilde = MAX16(pvt->y_tilde_s.buf_d + pvt->y_tilde_s.idx_d, pvt->N_d, &pvt->max_y_tilde_pos);
}
/* Determine if near end speech was detected in this sample */
/* -------------------------------------------------------- */
if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde)
&& (pvt->max_y_tilde > 0)) {
/* Then start the Hangover counter */
pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
#ifdef MEC2_STATS_DETAILED
printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", pvt->s_tilde_i, (pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), pvt->max_y_tilde);
#endif
#ifdef MEC2_STATS
++pvt->cntr_nearend_speech_frames;
#endif
} else if (pvt->HCNTR_d > (int)0) {
/* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
#ifdef MEC2_STATS
++pvt->cntr_nearend_speech_frames;
#endif
pvt->HCNTR_d--;
}
/* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0)
* and we have enough signal to bother trying to update.
* --------------------------------------------------------------------------
*/
if (!pvt->HCNTR_d && /* no near-end speech present */
!(pvt->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
if (pvt->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */
/* so loop over all the filter coefficients */
#ifdef USED_COEFFS
int max_coeffs[USED_COEFFS];
int *pos;
if (pvt->N_d > USED_COEFFS)
memset(max_coeffs, 0, USED_COEFFS*sizeof(int));
#endif
#ifdef MEC2_STATS_DETAILED
printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i);
#endif
#ifdef MEC2_STATS
pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i;
++pvt->cntr_coeff_updates;
#endif
for (k = 0; k < pvt->N_d; k++) {
/* eq. (7): compute an expectation over M_d samples */
int grad2;
grad2 = CONVOLVE2(pvt->u_s.buf_d + pvt->u_s.idx_d,
pvt->y_s.buf_d + pvt->y_s.idx_d + k,
DEFAULT_M);
/* eq. (7): update the coefficient */
pvt->a_i[k] += grad2 / two_beta_i;
pvt->a_s[k] = pvt->a_i[k] >> 16;
#ifdef USED_COEFFS
if (pvt->N_d > USED_COEFFS) {
if (abs(pvt->a_i[k]) > max_coeffs[USED_COEFFS-1]) {
/* More or less insertion-sort... */
pos = max_coeffs;
while (*pos > abs(pvt->a_i[k]))
pos++;
if (*pos > max_coeffs[USED_COEFFS-1])
memmove(pos+1, pos, (USED_COEFFS-(pos-max_coeffs)-1)*sizeof(int));
*pos = abs(pvt->a_i[k]);
}
}
#endif
}
#ifdef USED_COEFFS
/* Filter out irrelevant coefficients */
if (pvt->N_d > USED_COEFFS)
for (k = 0; k < pvt->N_d; k++)
if (abs(pvt->a_i[k]) < max_coeffs[USED_COEFFS-1])
pvt->a_i[k] = pvt->a_s[k] = 0;
#endif
} else {
#ifdef MEC2_STATS_DETAILED
printk(KERN_INFO "insufficient signal to update coefficients pvt->Lu_i %5d < %5d\n", pvt->Lu_i, MIN_UPDATE_THRESH_I);
#endif
#ifdef MEC2_STATS
pvt->avg_Lu_i_toolow = pvt->avg_Lu_i_toolow + pvt->Lu_i;
++pvt->cntr_coeff_missedupdates;
#endif
}
}
/* paragraph below eq. (15): if no near-end speech in the sample and
* the reference signal power estimate > cutoff threshold
* then perform residual error suppression
*/
#ifdef MEC2_STATS_DETAILED
if (pvt->HCNTR_d == 0)
printk(KERN_INFO "possibly correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d and expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifndef NO_ECHO_SUPPRESSOR
if (pvt->use_nlp) {
if (pvt->aggressive) {
if ((pvt->HCNTR_d < AGGRESSIVE_HCNTR) && (pvt->Ly_i > (pvt->Lu_i << 1))) {
for (k = 0; k < 2; k++) {
u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk(KERN_INFO "aggresively correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++pvt->cntr_residualcorrected_frames;
#endif
}
} else {
if (pvt->HCNTR_d == 0) {
if ((pvt->Ly_i/(pvt->Lu_i + 1)) > DEFAULT_SUPPR_I) {
for (k = 0; k < 1; k++) {
u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk(KERN_INFO "correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++pvt->cntr_residualcorrected_frames;
#endif
}
#ifdef MEC2_STATS
else {
++pvt->cntr_residualcorrected_framesskipped;
}
#endif
}
}
}
#endif
#if 0
/* This will generate a non-linear supression factor, once converted */
if ((pvt->HCNTR_d == 0) &&
((pvt->Lu_d/pvt->Ly_d) < DEFAULT_SUPPR) &&
(pvt->Lu_d/pvt->Ly_d > EC_MIN_DB_VALUE)) {
suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(pvt->Lu_d/pvt->Ly_d)
- SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL);
u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr;
}
#endif
#ifdef MEC2_STATS
/* Periodically dump performance stats */
if ((pvt->i_d % MEC2_STATS) == 0) {
/* make sure to avoid div0's! */
if (pvt->cntr_coeff_missedupdates > 0)
pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates);
else
pvt->avg_Lu_i_toolow = -1;
if (pvt->cntr_coeff_updates > 0)
pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates);
else
pvt->avg_Lu_i_ok = -1;
printk(KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n",
pvt->id,
pvt->cntr_nearend_speech_frames,
pvt->cntr_residualcorrected_frames, pvt->cntr_residualcorrected_framesskipped,
pvt->cntr_coeff_updates, pvt->cntr_coeff_missedupdates,
pvt->avg_Lu_i_ok, pvt->avg_Lu_i_toolow);
pvt->cntr_nearend_speech_frames = 0;
pvt->cntr_residualcorrected_frames = 0;
pvt->cntr_residualcorrected_framesskipped = 0;
pvt->cntr_coeff_updates = 0;
pvt->cntr_coeff_missedupdates = 0;
pvt->avg_Lu_i_ok = 0;
pvt->avg_Lu_i_toolow = 0;
}
#endif
/* Increment the sample index and return the corrected sample */
pvt->i_d++;
return u;
}
static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
struct ec_pvt *pvt = dahdi_to_pvt(ec);
u32 x;
short result;
for (x = 0; x < size; x++) {
result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
{
int maxy;
int maxu;
size_t size;
unsigned int x;
char *c;
struct ec_pvt *pvt;
maxy = ecp->tap_length + DEFAULT_M;
maxu = DEFAULT_M;
if (maxy < (1 << DEFAULT_ALPHA_YT_I))
maxy = (1 << DEFAULT_ALPHA_YT_I);
if (maxy < (1 << DEFAULT_SIGMA_LY_I))
maxy = (1 << DEFAULT_SIGMA_LY_I);
if (maxu < (1 << DEFAULT_SIGMA_LU_I))
maxu = (1 << DEFAULT_SIGMA_LU_I);
size = sizeof(**ec) +
4 + /* align */
sizeof(int) * ecp->tap_length + /* a_i */
sizeof(short) * ecp->tap_length + /* a_s */
sizeof(int) * ecp->tap_length + /* b_i */
sizeof(int) * ecp->tap_length + /* c_i */
2 * sizeof(short) * (maxy) + /* y_s */
2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */
2 * sizeof(short) * (maxu) + /* u_s */
2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */
pvt = kzalloc(size, GFP_KERNEL);
if (!pvt)
return -ENOMEM;
pvt->dahdi.ops = &my_ops;
pvt->aggressive = aggressive;
pvt->dahdi.features = my_features;
for (x = 0; x < ecp->param_count; x++) {
for (c = p[x].name; *c; c++)
*c = tolower(*c);
if (!strcmp(p[x].name, "aggressive")) {
pvt->aggressive = p[x].value ? 1 : 0;
} else {
printk(KERN_WARNING "Unknown parameter supplied to MG2 echo canceler: '%s'\n", p[x].name);
kfree(pvt);
return -EINVAL;
}
}
init_cc(pvt, ecp->tap_length, maxy, maxu);
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
accumulating noise". */
pvt->use_nlp = TRUE;
*ec = &pvt->dahdi;
return 0;
}
static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val)
{
struct ec_pvt *pvt = dahdi_to_pvt(ec);
/* Set the hangover counter to the length of the can to
* avoid adjustments occuring immediately after initial forced training
*/
pvt->HCNTR_d = pvt->N_d << 1;
if (pos >= pvt->N_d) {
memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int));
return 1;
}
pvt->a_i[pos] = val << 17;
pvt->a_s[pos] = val << 1;
if (++pos >= pvt->N_d) {
memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int));
memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int));
return 1;
}
return 0;
}
static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable)
{
struct ec_pvt *pvt = dahdi_to_pvt(ec);
pvt->use_nlp = enable ? 1 : 0;
}
static int __init mod_init(void)
{
if (dahdi_register_echocan_factory(&my_factory)) {
module_printk(KERN_ERR, "could not register with DAHDI core\n");
return -EPERM;
}
module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);
module_param(aggressive, int, S_IRUGO | S_IWUSR);
MODULE_DESCRIPTION("DAHDI 'MG2' Echo Canceler");
MODULE_AUTHOR("Michael Gernoth");
MODULE_LICENSE("GPL v2");
module_init(mod_init);
module_exit(mod_exit);