diff options
author | Shaun Ruffell <sruffell@digium.com> | 2014-09-03 17:35:52 +0000 |
---|---|---|
committer | Shaun Ruffell <sruffell@digium.com> | 2014-09-03 17:35:52 +0000 |
commit | 19e5621b53930e912b17d4a81e943ee6cf49f9ad (patch) | |
tree | 40a5a2294c4deb8baa63111402a3ac703a8b2602 /drivers/dahdi/dahdi_echocan_mg2.c | |
parent | 9df2805ef9a14d731fcaa1325b6b1054c1f2c101 (diff) |
Point people to where active development is taking placesvn_trunk
git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10748 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi_echocan_mg2.c')
-rw-r--r-- | drivers/dahdi/dahdi_echocan_mg2.c | 890 |
1 files changed, 0 insertions, 890 deletions
diff --git a/drivers/dahdi/dahdi_echocan_mg2.c b/drivers/dahdi/dahdi_echocan_mg2.c deleted file mode 100644 index 4038ff2..0000000 --- a/drivers/dahdi/dahdi_echocan_mg2.c +++ /dev/null @@ -1,890 +0,0 @@ -/* - * ECHO_CAN_MG2 - * - * by Michael Gernoth - * - * Based upon kb1ec.h and mec2.h - * - * Copyright (C) 2002-2012, 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 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 char *name = "MG2"; -static const char *ec_name(const struct dahdi_chan *chan) { return name; } - -static const struct dahdi_echocan_factory my_factory = { - .get_name = ec_name, - .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 = { - .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) -{ - char *ptr = (char *) 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 = (int *) ptr; - ptr += (sizeof(int) * pvt->N_d); - pvt->a_s = (short *) ptr; - ptr += (sizeof(short) * pvt->N_d); - - /* Allocate backup memory */ - pvt->b_i = (int *) ptr; - ptr += (sizeof(int) * pvt->N_d); - pvt->c_i = (int *) 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 -static inline short 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.get_name(NULL)); - - 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); |