diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-12-20 01:19:37 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-12-20 01:19:37 +0000 |
commit | 1cdd316d671dac981ace7ecae43a389a5883978f (patch) | |
tree | 24f1432ecf6ee8151ddffa78a2987872dc30089c | |
parent | b5c7c251f68b9d0c508ab0d36cd26c8b9ce8bc79 (diff) |
revert the echocanparams code for now... it's not ready to be used
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3545 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | hpec/hpec_zaptel.h | 14 | ||||
-rw-r--r-- | kb1ec.h | 51 | ||||
-rw-r--r-- | mec.h | 322 | ||||
-rw-r--r-- | mec2.h | 436 | ||||
-rw-r--r-- | mec2_const.h | 28 | ||||
-rw-r--r-- | mec3-float.h | 229 | ||||
-rw-r--r-- | mec3.h | 257 | ||||
-rw-r--r-- | mg2ec.h | 66 | ||||
-rw-r--r-- | sec-2.h | 57 | ||||
-rw-r--r-- | sec.h | 54 | ||||
-rw-r--r-- | zaptel-base.c | 166 | ||||
-rw-r--r-- | zaptel.h | 28 | ||||
-rw-r--r-- | zconfig.h | 3 |
14 files changed, 1450 insertions, 263 deletions
@@ -375,7 +375,7 @@ wct4xxp/wct4xxp.o: tor2.o: tor2-hw.h tor2fw.h -zaptel-base.o: digits.h arith.h sec.h sec-2.h kb1ec.h mg2ec.h zconfig.h +zaptel-base.o: digits.h arith.h sec.h mec.h sec-2.h mec2.h mec3.h zconfig.h wcusb.o: wcusb.h diff --git a/hpec/hpec_zaptel.h b/hpec/hpec_zaptel.h index 2fb5a14..80004da 100644 --- a/hpec/hpec_zaptel.h +++ b/hpec/hpec_zaptel.h @@ -87,22 +87,18 @@ static inline void echo_can_array_update(struct echo_can_state *ec, short *iref, DECLARE_MUTEX(alloc_lock); -static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, - struct echo_can_state **ec) +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { - if (ecp->param_count > 0) { - printk(KERN_WARNING "HPEC does not support parameters; failing request\n"); - return -EINVAL; - } + struct echo_can_state *result = NULL; if (down_interruptible(&alloc_lock)) - return -ENOTTY; + return NULL; - *ec = hpec_channel_alloc(ecp->tap_length); + result = hpec_channel_alloc(len); up(&alloc_lock); - return *ec ? 0 : -ENOTTY; + return result; } static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) @@ -529,19 +529,12 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short return u; } -static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, - struct echo_can_state **ec) +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { + struct echo_can_state *ec; int maxy; int maxu; - size_t size; - - if (ecp->param_count > 0) { - printk(KERN_WARNING "KB1 echo canceler does not support parameters; failing request\n"); - return -EINVAL; - } - - maxy = ecp->tap_length + DEFAULT_M; + maxy = len + DEFAULT_M; maxu = DEFAULT_M; if (maxy < (1 << DEFAULT_ALPHA_YT_I)) maxy = (1 << DEFAULT_ALPHA_YT_I); @@ -549,24 +542,26 @@ static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam 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 */ - 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 */ - - if (!(*ec = MALLOC(size))) - return -ENOMEM; - - memset(*ec, 0, size); - - init_cc(*ec, ecp->tap_length, maxy, maxu); - - return 0; + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 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) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 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) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; } static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) @@ -0,0 +1,322 @@ +/* + * Mark's Echo Canceller + * + * Mark Spencer <markster@linux-support.net> + * + * Simple, LMS Echo Canceller with double talk detection. + * Partly based on the TI App note: + * "Digital Voice Echo Canceller with a TMS 32020" + * + * Special additional thanks to: + * Jim Dixon (Lambda Telecommunications) + * Iman Ghobrial (Adtran, Inc.) + * + * Copyright (C) 2001, Linux Support Services, Inc. + * + * This program is free software and may be used and + * distributed according to the terms of the GNU + * General Public License, incorporated herein by + * reference. + * + */ + +#ifndef _MEC_H +#define _MEC_H + +/* You have to express the size of the echo canceller in taps as + a power of 2 (6 = 64 taps, 7 = 128 taps, 8 = 256 taps) */ +#define NUM_TAPS_POW2 6 /* Size of echo canceller in power of 2 (taps) */ +#define NUM_TAPS (1 << NUM_TAPS_POW2) /* Actual number of taps */ +#define TAP_MASK (NUM_TAPS-1) + + +#define SIGMA_LU_POW NUM_TAPS_POW2 +#define SIGMA_LY_POW NUM_TAPS_POW2 +#define SIGMA_YT_POW (NUM_TAPS_POW2 - 1) +#define SIGMA_ST_POW (NUM_TAPS_POW2 - 1) + +#define BETA_POW 8 + +#define CUTOFF_S 4 + +/* The higher you make this, the better the quality, but the more CPU time required */ +#define MIN_QUALITY 100 + +/* This optimization saves a lot of processor but may degrade quality */ +#define OPTIMIZEDIV + +#if 0 +/* This converges much more slowly but saves processor */ +#define MIN_UPDATE 256 +#define MIN_SKIP 8 +#endif + +#define HANG_T 600 /* 600 samples, or 75ms */ + +struct echo_can_state { + /* Circular position */ + int cpos; + short y[NUM_TAPS]; /* Last N samples (relative to cpos) transmitted */ + short y_abs[NUM_TAPS]; /* Last N samples (relative to cpos) transmitted (abs value) */ + short s[NUM_TAPS]; /* Last N samples (relative to cpos) received */ + short s_abs[NUM_TAPS]; /* Last N samples (relative to cpos) received (abs value) */ + short u[NUM_TAPS]; /* Last N samples (relative to cpos) with echo removed */ + short u_abs[NUM_TAPS]; /* Last N samples (relative to cpos) with echo removed */ + + int Ly; /* tx power */ + int Lu; /* Power of echo-cancelled output */ + + int Ty[NUM_TAPS]; /* Short term power estimate of transmit */ + int Ts; /* Short term power estimate of received signal */ + + int a[NUM_TAPS]; /* Tap weight coefficients (not relative) */ + + short sdc[NUM_TAPS]; /* Near end signal before High Pass Filter */ + + int samples; /* Sample count */ + int pass; /* Number of passes we've made */ + + int hangt; + + int lastmax; /* Optimize maximum search */ + int maxTy; /* Maximum Ty */ +}; + +#define INLINE inline + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <linux/slab.h> +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree((a)) +#else +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +static void echo_can_init(void) +{ + printk("Zaptel Echo Canceller: MARK%s\n", ZAPTEL_ECHO_AGGRESSIVE); +} + +static void echo_can_identify(char *buf, size_t len) +{ + strncpy(buf, "MARK2", len); +} + +static void echo_can_shutdown(void) +{ +} + +static INLINE struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *ec; + /* Uhm, we're only one length, sorry. */ + ec = MALLOC(sizeof(struct echo_can_state)); + if (ec) + memset(ec, 0, sizeof(*ec)); + return ec; +} + +#define PASSPOS 32000 +#undef PASSPOS + +static INLINE void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static INLINE int16_t echo_can_update(struct echo_can_state *ec, int16_t tx, int16_t rx) +{ + /* Process a sample, where tx is the near end and rx is the far end + echo */ + + int suppr; + int nsuppr; + short rxabs, txabs; + register int Lu; + register int x; + register int pos; + register int r_hat; /* Estimated echo */ + int oldrxabs; + int oldtxabs; + int oldsupprabs; + int supprabs; +#ifdef MIN_UPDATE + int totalupd; +#endif + + txabs = abs(tx); + rxabs = abs(rx); + + ec->pass++; + + r_hat = 0; + + /* Load next value */ + ec->y[ec->cpos] = tx; + + /* Load next abs value */ + oldtxabs = ec->y_abs[ec->cpos]; + ec->y_abs[ec->cpos] = txabs; + + /* Bring in receive value (near-end signal) */ + ec->sdc[ec->cpos] = rx; + + /* Bring in receive value absolute value */ + oldrxabs = ec->s_abs[ec->cpos]; + ec->s_abs[ec->cpos] = rxabs; + + Lu = ec->Lu | 1; + +#if 0 + /* Apply first order high pass filter (3 dB @ 160 Hz) */ + tx = ec->s[ec->cpos] = (1.0-DEFGAMMA) * ec->s[(ec->cpos - 1) & TAP_MASK] + + 0.5 * (1.0-DEFGAMMA) * ( ec->sdc[(ec->cpos - 1) & TAP_MASK] - ec->sdc[(ec->cpos - 2) & TAP_MASK]); +#endif + + /* Estimate echo */ + pos = ec->cpos; + for (x=0;x<NUM_TAPS;x++) { + r_hat += ec->a[x] * ec->y[pos]; + /* Go backwards in time and loop around circular buffer */ + pos = (pos - 1) & TAP_MASK; + } + + r_hat >>= 16; + + if (ec->hangt > 0) + ec->hangt--; + + /* printf("rx: %F, rhat: %F\n", rx, r_hat); */ + /* Calculate suppressed amount */ + suppr = rx - r_hat; + + if (ec->pass > NUM_TAPS) { + /* Have to have enough taps to start with */ + if (ec->maxTy > ec->Ts) { + /* There is no near-end speech detected */ + if (!ec->hangt) { + /* We're not in the hang-time from the end of near-end speech */ + if ((ec->Ly > 1024) && ((ec->Ly / Lu) < MIN_QUALITY)) { +#ifdef OPTIMIZEDIV + /* We both have enough signal on the transmit */ + nsuppr = (suppr << 18) / ec->Ly; + + if (nsuppr > 32767) + nsuppr = 32767; + if (nsuppr < -32768) + nsuppr = -32768; + + nsuppr /= ec->Ly; +#else + /* We both have enough signal on the transmit */ + nsuppr = (suppr << 16) / ec->Ly; + + if (nsuppr > 32767) + nsuppr = 32767; + if (nsuppr < -32768) + nsuppr = -32768; + +#endif + + /* Update coefficients */ + pos = ec->cpos; +#ifdef MIN_UPDATE + totalupd =0; +#endif + for (x=0;x<NUM_TAPS;x++) { + register int adj; + adj = ec->y[pos] * nsuppr; +#ifndef OPTIMIZEDIV + adj /= ec->Ly; + adj >>= BETA_POW; +#else + adj >>= BETA_POW + 2; +#endif +#ifdef PASSPOS + if (ec->pass > PASSPOS) + printf("tx: %d, old %d: %d, adj %d, nsuppr: %d, power: %d\n", tx, x, ec->a[x], adj, nsuppr, ec->Ly); +#endif + ec->a[x] += adj; +#ifdef MIN_UPDATE + totalupd += abs(adj); +#endif + /* Go backwards in time and loop around circular buffer */ + pos = (pos - 1) & TAP_MASK; + } +#ifdef MIN_UPDATE + /* If we didn't update at least this much, delay for many more taps */ + if (totalupd < MIN_UPDATE) { + ec->hangt += MIN_SKIP; + } +#endif + } + + } + } else + /* Near end speech detected */ + ec->hangt = HANG_T; + } + + /* Save supression and absolute values */ + supprabs = abs(suppr); + oldsupprabs = ec->u_abs[ec->cpos]; + ec->u[ec->cpos] = suppr; + ec->u_abs[ec->cpos] = supprabs; + + /* Update tx power */ + ec->Ly += (txabs >> SIGMA_LY_POW) - (oldtxabs >> SIGMA_LY_POW); + + /* Update rx power */ + ec->Lu += (supprabs >> SIGMA_LU_POW) - (oldsupprabs >> SIGMA_LU_POW); + + /* Short term power of tx */ + ec->Ty[ec->cpos] = ec->Ty[(ec->cpos - 1) & TAP_MASK] + + ((txabs >> SIGMA_YT_POW ) - (oldtxabs >> SIGMA_YT_POW)); + + /* Keep track of highest */ + if (ec->lastmax == ec->cpos) { + register int maxTy = 0; + /* Have to loop through and find the new highest since our old highest expired */ + /* Estimate echo */ + pos = ec->cpos; + for (x=0;x<NUM_TAPS;x++) { + if (ec->Ty[pos] > maxTy) + maxTy = ec->Ty[pos]; + /* Go backwards in time and loop around circular buffer */ + pos = (pos - 1) & TAP_MASK; + } + ec->maxTy = maxTy; + } else { + /* Just keep the highest */ + if (ec->Ty[ec->cpos] > ec->maxTy) { + ec->maxTy = ec->Ty[ec->cpos]; + ec->lastmax = ec->cpos; + } + } + ec->Ts += (rxabs >> SIGMA_ST_POW) - (oldrxabs >> SIGMA_ST_POW) ; + + /* Increment position memory */ + ec->cpos = (ec->cpos + 1 ) & TAP_MASK; + + return suppr; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Reset hang counter to avoid adjustments after + initial forced training */ + ec->hangt = NUM_TAPS << 1; + if (pos >= NUM_TAPS) + return 1; + ec->a[pos] = val << 17; + if (++pos >= NUM_TAPS) + return 1; + return 0; +} + +#endif @@ -0,0 +1,436 @@ +/* + * Mark's Second Echo Canceller + * + * Copyright (C) 2002, Digium, Inc. + * + * This program is free software and may be used and + * distributed according to the terms of the GNU + * General Public License, incorporated herein by + * reference. + * + */ +#ifndef _MARK2_ECHO_H +#define _MARK2_ECHO_H + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <linux/slab.h> +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +/* Get optimized routines for math */ +#include "arith.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#include "mec2_const.h" + +/* Circular buffer definition */ +typedef struct { + int idx_d; + int size_d; + short *buf_d; /* Twice as large as we need */ +} echo_can_cb_s; + +// class definition +// +struct echo_can_state { + /* Echo canceller definition */ + + /* absolute time */ + int i_d; + + /* pre-computed constants */ + + int N_d; + int beta2_i; + + // declare accumulators for power computations + // + int Ly_i; + int Lu_i; + + // declare an accumulator for the near-end signal detector + // + int s_tilde_i; + int HCNTR_d; + + // circular buffers and coefficients + // + int *a_i; + short *a_s; + echo_can_cb_s y_s; + echo_can_cb_s s_s; + echo_can_cb_s u_s; + echo_can_cb_s y_tilde_s; + int y_tilde_i; + + /* Max memory */ + short max_y_tilde; + int max_y_tilde_pos; + +}; + +static void echo_can_init(void) +{ + printk("Zaptel Echo Canceller: MARK2%s\n", ZAPTEL_ECHO_AGGRESSIVE); +} + +static void echo_can_identify(char *buf, size_t len) +{ + strncpy(buf, "MARK2", len); +} + +static void echo_can_shutdown(void) +{ +} + +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) + {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 echo_can_state *ec, int N, int maxy, int maxu) { + + void *ptr = ec; + unsigned long tmp; + /* double-word align past end of state */ + ptr += sizeof(struct echo_can_state); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + // reset parameters + // + ec->N_d = N; + ec->beta2_i = DEFAULT_BETA1_I; + + // allocate coefficient memory + // + ec->a_i = ptr; + ptr += (sizeof(int) * ec->N_d); + ec->a_s = ptr; + ptr += (sizeof(short) * ec->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&ec->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sig circular buffer (short version for FIR filter) */ + init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&ec->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + // allocate a buffer for the reference signal power computation + // + init_cb_s(&ec->y_tilde_s, ec->N_d, ptr); + + + // reset absolute time + // + ec->i_d = (int)0; + + // reset the power computations (for y and u) + // + ec->Ly_i = DEFAULT_CUTOFF_I; + ec->Lu_i = DEFAULT_CUTOFF_I; + + // reset the near-end speech detector + // + ec->s_tilde_i = 0; + ec->y_tilde_i = 0; + ec->HCNTR_d = (int)0; + + // exit gracefully + // +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline short echo_can_update(struct echo_can_state *ec, 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; + + /*************************************************************************** + // + // 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); */ + + + /* Delete last sample from power estimate */ + ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I; + /* push the reference data onto the circular buffer */ + add_cc_s(&ec->y_s, iref); + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->N_d); + rs >>= 15; + + /* 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; + + add_cc_s(&ec->u_s, u); + + + + /* Delete oldest part of received s_tilde */ + ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 )); + + /* push the signal on the circular buffer, too */ + add_cc_s(&ec->s_s, isig); + ec->s_tilde_i += abs(isig); + ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_YT_I; + + /* Add to our list of recent y_tilde's */ + add_cc_s(&ec->y_tilde_s, ec->y_tilde_i); + + /**************************************************************************** + // + // flow B on pg. 428 + // + ****************************************************************************/ + + /* compute the new convergence factor + */ + if (!ec->HCNTR_d) { + Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + printf("Py: %e, Py_i: %e\n", Py, Py_i * AMPL_SCALE_1); +#endif + + /* 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 + */ +#if 0 + if (ec->start_speech_d != 0 ){ + if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){ + ec->beta2_d = max_cc_float(MIN_BETA, + DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - + DEFAULT_T0 - + ec->start_speech_d))); + } + } + else {ec->beta2_d = DEFAULT_BETA1;} +#endif + + ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point, inverted */ + + two_beta_i = (ec->beta2_i * Py_i) >> 15; /* Fixed point version, inverted */ + if (!two_beta_i) + two_beta_i++; + + /* Update Lu_i (Suppressed power estimate) */ + ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ; + ec->Lu_i += abs(u); + + /* eq. (10): update power estimate of the reference + */ + ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + ec->Ly_i += abs(iref); + + if (ec->Ly_i < DEFAULT_CUTOFF_I) + ec->Ly_i = DEFAULT_CUTOFF_I; + +#if 0 + printf("Float: %e, Int: %e\n", ec->Ly_d, (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * AMPL_SCALE_1); +#endif + + if (ec->y_tilde_i > ec->max_y_tilde) { + /* New highest y_tilde with full life */ + ec->max_y_tilde = ec->y_tilde_i; + ec->max_y_tilde_pos = ec->N_d - 1; + } else if (--ec->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos); + } + + if ((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde) + { + ec->HCNTR_d = DEFAULT_HANGT; + } + else if (ec->HCNTR_d > (int)0) + { + ec->HCNTR_d--; + } + + /* update coefficients if no near-end speech and we have enough signal + * to bother trying to update. + */ + if (!ec->HCNTR_d && !(ec->i_d % DEFAULT_M) && + (ec->Lu_i > MIN_UPDATE_THRESH_I)) { + // loop over all filter coefficients + // + for (k=0; k<ec->N_d; k++) { + + // eq. (7): compute an expectation over M_d samples + // + int grad2; + grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d, + ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M); + // eq. (7): update the coefficient + // + ec->a_i[k] += grad2 / two_beta_i; + ec->a_s[k] = ec->a_i[k] >> 16; + } + } + + /* paragraph below eq. (15): if no near-end speech, + // check for residual error suppression + */ +#ifndef NO_ECHO_SUPPRESSOR +#ifdef AGGRESSIVE_SUPPRESSOR +#ifdef AGGRESSIVE_TIMELIMIT /* This allows the aggressive suppressor to turn off after set amount of time */ + if (ec->i_d > AGGRESSIVE_TIMELIMIT ) { + if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } + } + else { +#endif + if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef AGGRESSIVE_TIMELIMIT + } +#endif +#else + if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) { + u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#endif +#endif + +#if 0 + if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) && + (ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10/(float)(SUPPR_FLOOR-SUPPR_CEIL))*log(ec->Lu_d/ec->Ly_d) + - SUPPR_CEIL/(float)(SUPPR_FLOOR - SUPPR_CEIL); + + u_suppr = pow(10.0,(suppr_factor)*RES_SUPR_FACTOR/10.0)*u_suppr; + + } +#endif + ec->i_d++; + return u; +} + +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *ec; + int maxy; + int maxu; + maxy = len + 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); + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 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) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + 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) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Reset hang counter to avoid adjustments after + initial forced training */ + ec->HCNTR_d = ec->N_d << 1; + if (pos >= ec->N_d) + return 1; + ec->a_i[pos] = val << 17; + ec->a_s[pos] = val << 1; + if (++pos >= ec->N_d) + return 1; + return 0; +} + +#endif diff --git a/mec2_const.h b/mec2_const.h new file mode 100644 index 0000000..4c7e8c9 --- /dev/null +++ b/mec2_const.h @@ -0,0 +1,28 @@ +/* + Important constants for tuning mec2 echo can + */ +#ifndef _MEC2_CONST_H +#define _MEC2_CONST_H + + +/* Convergence speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 +#define DEFAULT_ALPHA_YT_I 5 +#define DEFAULT_CUTOFF_I 128 +#define DEFAULT_HANGT 600 +#define DEFAULT_SUPPR_I 16 +#define MIN_UPDATE_THRESH_I 4096 +#define DEFAULT_M 16 +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 +#define RES_SUPR_FACTOR -20 +#define AGGRESSIVE_HCNTR 160 /* 20ms */ + +/* Only use agressive echo cancellation for this amount of time then go back to normal cancelation */ +/* #define AGGRESSIVE_TIMELIMIT 150000 */ /* 8 = 1ms */ + +#endif /* _MEC2_CONST_H */ + diff --git a/mec3-float.h b/mec3-float.h new file mode 100644 index 0000000..12c3038 --- /dev/null +++ b/mec3-float.h @@ -0,0 +1,229 @@ +/* + * Mark's Third Echo Canceller + * + * Copyright (C) 2003, Digium, Inc. + * + * This program is free software and may be used + * and distributed under the terms of the GNU General Public + * License, incorporated herein by reference. + * + * Dedicated to the crew of the Columbia, STS-107 for their + * bravery and courageous sacrifice for science. + * + */ + +#ifndef _MARK3_ECHO_H +#define _MARK3_ECHO_H + +#define ECHO_CAN_FP + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <linux/slab.h> +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#include <math.h> +#else +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +/* + * Define COEFF_BACKUP for experimental coefficient backup code + */ + + +#define STEP_SIZE 0.4 /* Convergence rate */ + +#define SIGMA_P 0.01 /* Minimum adjustment */ +#define SIGMA_REF_PWR 0.01 /* Keep denominator from being 0 */ + +#define MIN_TX_ENERGY 256.0/32767.0 /* Must have at least this much reference */ +#define MIN_RX_ENERGY 32.0/32767.0 /* Must have at least this much receive energy */ + +#define MAX_ATTENUATION 64.0 /* Maximum amount of loss we care about */ +#define MAX_BETA 0.1 + +#define SUPPR_ATTENUATION 16.0 /* Amount of loss at which we suppress audio */ + +#define HANG_TIME 600 /* Hangover time */ + +#define NTAPS 256 /* Number of echo can taps */ + +#define BACKUP 256 /* Backup every this number of samples */ + +typedef struct { + float buf[NTAPS * 2]; + float max; + int maxexp; +} cbuf_f; + +struct echo_can_state { + float a[NTAPS]; /* Coefficients */ +#ifdef COEFF_BACKUP + float b[NTAPS]; /* Coefficients */ + float c[NTAPS]; /* Coefficients */ + int backup; /* Backup timer */ +#endif + cbuf_f ref; /* Reference excitation */ + cbuf_f sig; /* Signal (echo + near end + noise) */ + cbuf_f e; /* Error */ + float refpwr; /* Reference power */ + int taps; /* Number of taps */ + int hcntr; /* Hangtime counter */ + int pos; /* Position in curcular buffers */ +}; + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline void buf_add(cbuf_f *b, float sample, int pos, int taps) +{ + /* Store and keep track of maxima */ + int x; + b->buf[pos] = sample; + b->buf[pos + taps] = sample; + if (sample > b->max) { + b->max = sample; + b->maxexp = taps; + } else { + b->maxexp--; + if (!b->maxexp) { + b->max = 0; + for (x=0;x<taps;x++) + if (b->max < fabs(b->buf[pos + x])) { + b->max = fabs(b->buf[pos + x]); + b->maxexp = x + 1; + } + } + } +} + +static inline short echo_can_update(struct echo_can_state *ec, short iref, short isig) +{ + int x; + float ref; + float sig; + float u; + float refpwr; + float beta; /* Factor */ + float se; /* Simulated echo */ + /* Convert to floats about 1.0 */ + ref = (((float)iref)/32767.0); + sig = (((float)isig)/32767.0); + +#if 0 + printf("start: %d, finish: %d\n", ec->start, ec->finish); +#endif + +#ifdef COEFF_BACKUP + if (!ec->backup) { + /* Backup coefficients periodically */ + ec->backup = BACKUP; + memcpy(ec->c,ec->b,sizeof(ec->c)); + memcpy(ec->b,ec->a,sizeof(ec->b)); + } else + ec->backup--; +#endif + /* Remove old samples from reference power calculation */ + ec->refpwr -= (ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]); + + /* Store signal and reference */ + buf_add(&ec->ref, ref, ec->pos, ec->taps); + buf_add(&ec->sig, sig, ec->pos, ec->taps); + + /* Add new reference power */ + ec->refpwr += (ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]); + + + /* Calculate simulated echo */ + se = 0.0; + for (x=0;x<ec->taps;x++) + se += ec->a[x] * ec->ref.buf[ec->pos + x]; + +#if 0 + if (!(ec->pos2++ % 1024)) { + printk("sig: %d, se: %d\n", (int)(32768.0 * sig), (int)(32768.0 * se)); + } +#endif + u = sig - se; + if (ec->hcntr) + ec->hcntr--; + + /* Store error */ + buf_add(&ec->e, u, ec->pos, ec->taps); + if ((ec->ref.max > MIN_TX_ENERGY) && + (ec->sig.max > MIN_RX_ENERGY) && + (ec->e.max * MAX_ATTENUATION > ec->ref.max)) { + /* We have sufficient energy */ + if (ec->sig.max * 2.0 < ec->ref.max) { + /* No double talk */ + if (!ec->hcntr) { + if (ec->refpwr < SIGMA_REF_PWR) + refpwr = SIGMA_REF_PWR; + else + refpwr = ec->refpwr; + beta = STEP_SIZE * u / refpwr; + if (beta > MAX_BETA) + beta = MAX_BETA; + if (beta < -MAX_BETA) + beta = -MAX_BETA; + /* Update coefficients */ + for (x=0;x<ec->taps;x++) { + ec->a[x] += beta * ec->ref.buf[ec->pos + x]; + } + } + } else { +#ifdef COEFF_BACKUP + if (!ec->hcntr) { + /* Our double talk detector is turning on for the first time. Revert + our coefficients, since we're probably well into the double talk by now */ + memcpy(ec->a, ec->c, sizeof(ec->a)); + } + ec->backup = BACKUP; +#endif + /* Reset hang-time counter, and prevent backups */ + ec->hcntr = HANG_TIME; + } + } +#ifndef NO_ECHO_SUPPRESSOR + if (ec->e.max * SUPPR_ATTENUATION < ec->ref.max) { + /* Suppress residual echo */ + u *= u; + } +#endif + ec->pos--; + if (ec->pos < 0) + ec->pos = ec->taps-1; + u *= 32767.0; + if (u < -32768.0) + u = -32768.0; + if (u > 32767.0) + u = 32767.0; + return (short)(u); +} + +static inline struct echo_can_state *echo_can_create(int taps, int adaption_mode) +{ + struct echo_can_state *ec; + taps = NTAPS; + ec = MALLOC(sizeof(struct echo_can_state)); + if (ec) { + printk("Allocating MEC3 canceller (%d)\n", taps); + memset(ec, 0, sizeof(struct echo_can_state)); + ec->taps = taps; + if (ec->taps > NTAPS) + ec->taps = NTAPS; + ec->pos = ec->taps-1; + } + return ec; +} + +#endif @@ -0,0 +1,257 @@ +/* + * Mark's Third Echo Canceller + * + * Copyright (C) 2003, Digium, Inc. + * + * This program is free software and may be used + * and distributed under the terms of the GNU General Public + * License, incorporated herein by reference. + * + * Dedicated to the crew of the Columbia, STS-107 for their + * bravery and courageous sacrifice for science. + * + */ + +#ifndef _MARK3_ECHO_H +#define _MARK3_ECHO_H + + + +#ifdef __KERNEL__ +#include <linux/kernel.h> +#include <linux/slab.h> +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +/* Features */ + +/* + * DO_BACKUP -- Backup coefficients, and revert in the presense of double talk to try to prevent + * them from diverging during the ramp-up before the DTD kicks in + */ +/* #define DO_BACKUP */ + +#define STEP_SHIFT 2 /* Convergence rate higher = slower / better (as a shift) */ + +#define SIGMA_REF_PWR 655 /* Keep denominator from being 0 */ + +#define MIN_TX_ENERGY 256 /* Must have at least this much reference */ +#define MIN_RX_ENERGY 32 /* Must have at least this much receive energy */ + +#define MAX_ATTENUATION_SHIFT 6 /* Maximum amount of loss we care about */ +#define MAX_BETA 1024 + +#define SUPPR_SHIFT 4 /* Amount of loss at which we suppress audio */ + +#define HANG_TIME 600 /* Hangover time */ + +#define NTAPS 256 /* Number of echo can taps */ + +#define BACKUP 256 /* Backup every this number of samples */ + +#define POWER_OFFSET 5 /* Shift power by this amount to be sure we don't overflow the + reference power. Higher = less likely to overflow, lower = more accurage */ + +#include "arith.h" + +typedef struct { + short buf[NTAPS * 2]; + short max; + int maxexp; +} cbuf_s; + +struct echo_can_state { + short a_s[NTAPS]; /* Coefficients in shorts */ + int a_i[NTAPS]; /* Coefficients in ints*/ +#ifdef DO_BACKUP + int b_i[NTAPS]; /* Coefficients (backup1) */ + int c_i[NTAPS]; /* Coefficients (backup2) */ +#endif + cbuf_s ref; /* Reference excitation */ + cbuf_s sig; /* Signal (echo + near end + noise) */ + cbuf_s e; /* Error */ + int refpwr; /* Reference power */ + int taps; /* Number of taps */ + int tappwr; /* Power of taps */ + int hcntr; /* Hangtime counter */ + int pos; /* Position in curcular buffers */ + int backup; /* Backup timer */ +}; + +static void echo_can_init(void) +{ + printk("Zaptel Echo Canceller: MARK3%s\n", ZAPTEL_ECHO_AGGRESSIVE); +} + +static void echo_can_identify(char *buf, size_t len) +{ + strncpy(buf, "MARK3", len); +} + +static void echo_can_shutdown(void) +{ +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + FREE(ec); +} + +static inline void buf_add(cbuf_s *b, short sample, int pos, int taps) +{ + /* Store and keep track of maxima */ + int x; + b->buf[pos] = sample; + b->buf[pos + taps] = sample; + if (sample > b->max) { + b->max = sample; + b->maxexp = taps; + } else { + b->maxexp--; + if (!b->maxexp) { + b->max = 0; + for (x=0;x<taps;x++) + if (b->max < abs(b->buf[pos + x])) { + b->max = abs(b->buf[pos + x]); + b->maxexp = x + 1; + } + } + } +} + +static inline short echo_can_update(struct echo_can_state *ec, short ref, short sig) +{ + int x; + short u; + int refpwr; + int beta; /* Factor */ + int se; /* Simulated echo */ +#ifdef DO_BACKUP + if (!ec->backup) { + /* Backup coefficients periodically */ + ec->backup = BACKUP; + memcpy(ec->c_i,ec->b_i,sizeof(ec->c_i)); + memcpy(ec->b_i,ec->a_i,sizeof(ec->b_i)); + } else + ec->backup--; +#endif + /* Remove old samples from reference power calculation */ + ec->refpwr -= ((ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]) >> POWER_OFFSET); + + /* Store signal and reference */ + buf_add(&ec->ref, ref, ec->pos, ec->taps); + buf_add(&ec->sig, sig, ec->pos, ec->taps); + + /* Add new reference power */ + ec->refpwr += ((ec->ref.buf[ec->pos] * ec->ref.buf[ec->pos]) >> POWER_OFFSET); + + + /* Calculate simulated echo */ + se = CONVOLVE2(ec->a_s, ec->ref.buf + ec->pos, ec->taps); + se >>= 15; + + u = sig - se; + if (ec->hcntr) + ec->hcntr--; + + /* Store error */ + buf_add(&ec->e, u, ec->pos, ec->taps); + if ((ec->ref.max > MIN_TX_ENERGY) && + (ec->sig.max > MIN_RX_ENERGY) && + (ec->e.max > (ec->ref.max >> MAX_ATTENUATION_SHIFT))) { + /* We have sufficient energy */ + if (ec->sig.max < (ec->ref.max >> 1)) { + /* No double talk */ + if (!ec->hcntr) { + refpwr = ec->refpwr >> (16 - POWER_OFFSET); + if (refpwr < SIGMA_REF_PWR) + refpwr = SIGMA_REF_PWR; + beta = (u << 16) / refpwr; + beta >>= STEP_SHIFT; + if (beta > MAX_BETA) + beta = MAX_BETA; + if (beta < -MAX_BETA) + beta = -MAX_BETA; + /* Update coefficients */ + for (x=0;x<ec->taps;x++) { + ec->a_i[x] += beta * ec->ref.buf[ec->pos + x]; + ec->a_s[x] = ec->a_i[x] >> 16; + } + } + } else { +#ifdef DO_BACKUP + if (!ec->hcntr) { + /* Our double talk detector is turning on for the first time. Revert + our coefficients, since we're probably well into the double talk by now */ + memcpy(ec->a_i, ec->c_i, sizeof(ec->a_i)); + for (x=0;x<ec->taps;x++) { + ec->a_s[x] = ec->a_i[x] >> 16; + } + } +#endif + /* Reset hang-time counter, and prevent backups */ + ec->hcntr = HANG_TIME; +#ifdef DO_BACKUP + ec->backup = BACKUP; +#endif + } + } +#ifndef NO_ECHO__SUPPRESSOR + if (ec->e.max < (ec->ref.max >> SUPPR_SHIFT)) { + /* Suppress residual echo */ + u *= u; + u >>= 16; + } +#endif + ec->pos--; + if (ec->pos < 0) + ec->pos = ec->taps-1; + return u; +} + +static inline struct echo_can_state *echo_can_create(int taps, int adaption_mode) +{ + struct echo_can_state *ec; + int x; + + taps = NTAPS; + ec = MALLOC(sizeof(struct echo_can_state)); + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state)); + ec->taps = taps; + ec->pos = ec->taps-1; + for (x=0;x<31;x++) { + if ((1 << x) >= ec->taps) { + ec->tappwr = x; + break; + } + } + } + return ec; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + /* Reset hang counter to avoid adjustments after + initial forced training */ + ec->hcntr = ec->taps << 1; + if (pos >= ec->taps) + return 1; + ec->a_i[pos] = val << 17; + ec->a_s[pos] = val << 1; + if (++pos >= ec->taps) + return 1; + return 0; +} + + +#endif @@ -508,7 +508,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short memset(max_coeffs, 0, USED_COEFFS*sizeof(int)); #endif #ifdef MEC2_STATS_DETAILED - printk(KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i); + printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i); #endif #ifdef MEC2_STATS ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i; @@ -550,7 +550,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short #endif } else { #ifdef MEC2_STATS_DETAILED - printk(KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I); + printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I); #endif #ifdef MEC2_STATS ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i; @@ -565,7 +565,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short */ #ifdef MEC2_STATS_DETAILED if (ec->HCNTR_d == 0) - printk(KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); + printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifndef NO_ECHO_SUPPRESSOR @@ -575,7 +575,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); } #ifdef MEC2_STATS_DETAILED - printk(KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); + printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifdef MEC2_STATS ++ec->cntr_residualcorrected_frames; @@ -588,7 +588,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); } #ifdef MEC2_STATS_DETAILED - printk(KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); + printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1))); #endif #ifdef MEC2_STATS ++ec->cntr_residualcorrected_frames; @@ -628,7 +628,7 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short else ec->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", + 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", ec->id, ec->cntr_nearend_speech_frames, ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped, @@ -650,19 +650,12 @@ static inline short echo_can_update(struct echo_can_state *ec, short iref, short return u; } -static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, - struct echo_can_state **ec) +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { + struct echo_can_state *ec; int maxy; int maxu; - size_t size; - - if (ecp->param_count > 0) { - printk(KERN_WARNING "MG2 echo canceler does not support parameters; failing request\n"); - return -EINVAL; - } - - maxy = ecp->tap_length + DEFAULT_M; + maxy = len + DEFAULT_M; maxu = DEFAULT_M; if (maxy < (1 << DEFAULT_ALPHA_YT_I)) maxy = (1 << DEFAULT_ALPHA_YT_I); @@ -670,23 +663,30 @@ static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam 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 */ - - if (!(*ec = MALLOC(size))) - return -ENOMEM; - - memset(*ec, 0, size); - - return 0; + ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + sizeof(int) * len + /* b_i */ + sizeof(int) * len + /* 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) * len); /* y_tilde_s */ + if (ec) { + memset(ec, 0, sizeof(struct echo_can_state) + + 4 + /* align */ + sizeof(int) * len + /* a_i */ + sizeof(short) * len + /* a_s */ + sizeof(int) * len + /* b_i */ + sizeof(int) * len + /* 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) * len); /* y_tilde_s */ + init_cc(ec, len, maxy, maxu); + } + return ec; } static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) @@ -97,6 +97,7 @@ struct echo_can_state was skipped, for test purposes */ }; +static struct echo_can_state *echo_can_create(int len, int adaption_mode); static void echo_can_free(struct echo_can_state *ec); static int16_t echo_can_update(struct echo_can_state *ec, int16_t tx, int16_t rx); @@ -126,44 +127,36 @@ static void echo_can_shutdown(void) /* #define MIN_TX_POWER_FOR_ADAPTION 4096 #define MIN_RX_POWER_FOR_ADAPTION 64 */ -static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, - struct echo_can_state **ec) +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { - size_t size; - - if (ecp->param_count > 0) { - printk(KERN_WARNING "SEC-2 echo canceler does not support parameters; failing request\n"); - return -EINVAL; - } - - size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t); - - if (!(*ec = MALLOC(size))) - return -ENOMEM; - - memset(*ec, 0, size); - - (*ec)->taps = ecp->tap_length; - (*ec)->curr_pos = ecp->tap_length - 1; - (*ec)->tap_mask = ecp->tap_length - 1; - (*ec)->fir_taps32 = (int32_t *) (*ec + sizeof(**ec)); - (*ec)->fir_taps16 = (int16_t *) (*ec + sizeof(**ec) + ecp->tap_length * sizeof(int32_t)); - /* Create FIR filter */ - fir16_create(&(*ec)->fir_state, (*ec)->fir_taps16, (*ec)->taps); - (*ec)->rx_power_threshold = 10000000; - (*ec)->use_suppressor = FALSE; - /* Non-linear processor - a fancy way to say "zap small signals, to avoid - accumulating noise". */ - (*ec)->use_nlp = FALSE; - - return 0; + struct echo_can_state *ec; + void *ptr; + + ptr = ec = (struct echo_can_state *) MALLOC(sizeof(*ec) + len * sizeof(int32_t) + + len * sizeof(int16_t)); + if (ec == NULL) + return NULL; + memset(ec, 0, sizeof(*ec) + len * sizeof(int32_t) + len * sizeof(int16_t)); + ec->taps = len; + ec->curr_pos = len - 1; + ec->tap_mask = len - 1; + ec->fir_taps32 = (int32_t *) (ptr + sizeof(*ec)); + ec->fir_taps16 = (int16_t *) (ptr + sizeof(*ec) + len * sizeof(int32_t)); + /* Create FIR filter */ + fir16_create(&ec->fir_state, ec->fir_taps16, ec->taps); + ec->rx_power_threshold = 10000000; + ec->use_suppressor = FALSE; + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + accumulating noise". */ + ec->use_nlp = FALSE; + return ec; } /*- End of function --------------------------------------------------------*/ static inline void echo_can_free(struct echo_can_state *ec) { - fir16_free(&ec->fir_state); - FREE(ec); + fir16_free(&ec->fir_state); + FREE(ec); } /*- End of function --------------------------------------------------------*/ @@ -114,6 +114,7 @@ static void echo_can_shutdown(void) { } +static struct echo_can_state *echo_can_create(int len, int adaption_mode); static void echo_can_free(struct echo_can_state *ec); static int16_t echo_can_update(struct echo_can_state *ec, int16_t tx, int16_t rx); @@ -130,44 +131,33 @@ static int16_t echo_can_update(struct echo_can_state *ec, int16_t tx, int16_t rx #define MIN_RX_POWER_FOR_ADAPTION 64 */ -static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, - struct echo_can_state **ec) +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) { - size_t size; - - if (ecp->param_count > 0) { - printk(KERN_WARNING "SEC echo canceler does not support parameters; failing request\n"); - return -EINVAL; - } - - size = sizeof(**ec) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t); - - if (!(*ec = MALLOC(size))) - return -ENOMEM; - - memset(*ec, 0, size); - - (*ec)->taps = ecp->tap_length; - (*ec)->tap_mask = ecp->tap_length - 1; - (*ec)->tx_history = (int16_t *) (*ec + sizeof(**ec)); - (*ec)->fir_taps = (int32_t *) (*ec + sizeof(**ec) + - ecp->tap_length * 2 * sizeof(int16_t)); - (*ec)->fir_taps_short = (int16_t *) (*ec + sizeof(**ec) + - ecp->tap_length * sizeof(int32_t) + - ecp->tap_length * 2 * sizeof(int16_t)); - (*ec)->rx_power_threshold = 10000000; - (*ec)->use_suppressor = FALSE; - /* Non-linear processor - a fancy way to say "zap small signals, to avoid - accumulating noise". */ - (*ec)->use_nlp = TRUE; - - return 0; + struct echo_can_state *ec; + void *ptr; + + ptr = ec = (struct echo_can_state *) MALLOC(sizeof(*ec) + len * sizeof(int32_t) + + len * 3 * sizeof(int16_t)); + if (ec == NULL) + return NULL; + memset(ec, 0, sizeof(*ec) + len * sizeof(int32_t) + len * 3 * sizeof(int16_t)); + ec->taps = len; + ec->tap_mask = len - 1; + ec->tx_history = (int16_t *) (ptr + sizeof(*ec) ); + ec->fir_taps = (int32_t *) (ptr + sizeof(*ec) + len * 2 * sizeof(int16_t)); + ec->fir_taps_short = (int16_t *) (ptr + sizeof(*ec) + len * sizeof(int32_t) + len * 2 * sizeof(int16_t)); + ec->rx_power_threshold = 10000000; + ec->use_suppressor = FALSE; + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + accumulating noise". */ + ec->use_nlp = TRUE; + return ec; } /*- End of function --------------------------------------------------------*/ static inline void echo_can_free(struct echo_can_state *ec) { - FREE(ec); + FREE(ec); } /*- End of function --------------------------------------------------------*/ diff --git a/zaptel-base.c b/zaptel-base.c index b90eb42..309b813 100644 --- a/zaptel-base.c +++ b/zaptel-base.c @@ -419,12 +419,18 @@ static struct zt_zone *tone_zones[ZT_TONE_ZONE_MAX]; #include "sec.h" #elif defined(ECHO_CAN_STEVE2) #include "sec-2.h" +#elif defined(ECHO_CAN_MARK) +#include "mec.h" +#elif defined(ECHO_CAN_MARK2) +#include "mec2.h" #elif defined(ECHO_CAN_KB1) #include "kb1ec.h" #elif defined(ECHO_CAN_MG2) #include "mg2ec.h" #elif defined(ECHO_CAN_JP1) #include "jpah.h" +#else +#include "mec3.h" #endif static inline void rotate_sums(void) @@ -4302,95 +4308,6 @@ static void do_ppp_calls(unsigned long data) } #endif -#define MAX_ECHOCANPARAMS 8 - -static int ioctl_echocancel(struct zt_chan *chan, struct zt_echocanparams *ecp, void *data) -{ - struct echo_can_state *ec = NULL, *tec; - struct zt_echocanparam params[MAX_ECHOCANPARAMS]; - int ret; - unsigned long flags; - - if (ecp->param_count > MAX_ECHOCANPARAMS) - return -E2BIG; - - if (ecp->tap_length == 0) { - /* disable mode, don't need to inspect params */ - spin_lock_irqsave(&chan->lock, flags); - tec = chan->ec; - chan->echocancel = 0; - chan->ec = NULL; - chan->echostate = ECHO_STATE_IDLE; - chan->echolastupdate = 0; - chan->echotimer = 0; - spin_unlock_irqrestore(&chan->lock, flags); - if (chan->span && chan->span->echocan) - chan->span->echocan(chan, 0); - if (tec) - echo_can_free(tec); - - return 0; - } - - /* if parameters were supplied and this channel's span provides an echocan, - but not one that takes params, then we must punt here and return an error */ - if (ecp->param_count && chan->span && chan->span->echocan && - !chan->span->echocan_with_params) - return -EINVAL; - - /* enable mode, need the params */ - - if (copy_from_user(params, (struct zt_echocanparam *) data, sizeof(params[0]) * ecp->param_count)) - return -EFAULT; - - spin_lock_irqsave(&chan->lock, flags); - tec = chan->ec; - chan->ec = NULL; - spin_unlock_irqrestore(&chan->lock, flags); - - ret = -ENOTTY; - - /* attempt to use the span's echo canceler; fall back to built-in - if it fails (but not if an error occurs) */ - if (chan->span && chan->span->echocan_with_params) - ret = chan->span->echocan_with_params(chan, ecp, params); - - if (ret == -ENOTTY) { - switch (ecp->tap_length) { - case 32: - case 64: - case 128: - case 256: - case 512: - case 1024: - break; - default: - ecp->tap_length = deftaps; - } - - if ((ret = echo_can_create(ecp, params, &ec))) { - if (tec) - echo_can_free(tec); - return ret; - } - - spin_lock_irqsave(&chan->lock, flags); - chan->echocancel = ecp->tap_length; - chan->ec = ec; - chan->echostate = ECHO_STATE_IDLE; - chan->echolastupdate = 0; - chan->echotimer = 0; - echo_can_disable_detector_init(&chan->txecdis); - echo_can_disable_detector_init(&chan->rxecdis); - spin_unlock_irqrestore(&chan->lock, flags); - } - - if (tec) - echo_can_free(tec); - - return 0; -} - static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit) { struct zt_chan *chan = chans[unit]; @@ -4400,7 +4317,6 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm int oldconf; void *rxgain=NULL; struct echo_can_state *ec, *tec; - struct zt_echocanparams ecp; if (!chan) return -ENOSYS; @@ -4565,20 +4481,62 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm case ZT_ECHOCANCEL: if (!(chan->flags & ZT_FLAG_AUDIO)) return -EINVAL; - if (copy_from_user(&ecp, (struct zt_echocanparams *) data, sizeof(ecp))) - return -EFAULT; - data += sizeof(ecp); - if ((ret = ioctl_echocancel(chan, &ecp, (void *) data))) - return ret; - break; - case ZT_ECHOCANCEL_V1: - if (!(chan->flags & ZT_FLAG_AUDIO)) - return -EINVAL; - get_user(j, (int *) data); - ecp.tap_length = j; - ecp.param_count = 0; - if ((ret = ioctl_echocancel(chan, &ecp, (void *) data))) - return ret; + get_user(j, (int *)data); + if (j) { + spin_lock_irqsave(&chan->lock, flags); + /* If we had an old echo can, zap it now */ + tec = chan->ec; + chan->ec = NULL; + /* Attempt hardware native echo can */ + spin_unlock_irqrestore(&chan->lock, flags); + + if (chan->span && chan->span->echocan) + ret = chan->span->echocan(chan, j); + else + ret = -ENOTTY; + + if (ret) { + /* Use built-in echo can */ + if ((j == 32) || + (j == 64) || + (j == 128) || + (j == 256) || + (j == 512) || + (j == 1024)) { + /* Okay */ + } else { + j = deftaps; + } + ec = echo_can_create(j, 0); + if (!ec) + return -ENOMEM; + spin_lock_irqsave(&chan->lock, flags); + chan->echocancel = j; + chan->ec = ec; + chan->echostate = ECHO_STATE_IDLE; + chan->echolastupdate = 0; + chan->echotimer = 0; + echo_can_disable_detector_init(&chan->txecdis); + echo_can_disable_detector_init(&chan->rxecdis); + spin_unlock_irqrestore(&chan->lock, flags); + } + if (tec) + echo_can_free(tec); + } else { + spin_lock_irqsave(&chan->lock, flags); + tec = chan->ec; + chan->echocancel = 0; + chan->ec = NULL; + chan->echostate = ECHO_STATE_IDLE; + chan->echolastupdate = 0; + chan->echotimer = 0; + spin_unlock_irqrestore(&chan->lock, flags); + /* Attempt hardware native echo can */ + if (chan->span && chan->span->echocan) + chan->span->echocan(chan, 0); + if (tec) + echo_can_free(tec); + } break; case ZT_ECHOTRAIN: get_user(j, (int *)data); /* get pre-training time from user */ @@ -560,18 +560,11 @@ struct zt_hwgain{ /* * Enable or disable echo cancellation on a channel - * - * For ECHOCANCEL_V1: * The number is zero to disable echo cancellation and non-zero * to enable echo cancellation. If the number is between 32 - * and 1024, it will also set the number of taps in the echo canceller - * - * For ECHOCANCEL: - * The structure contains parameters that should be passed to the - * echo canceler instance for the selected channel. + * and 256, it will also set the number of taps in the echo canceller */ -#define ZT_ECHOCANCEL_V1 _IOW (ZT_CODE, 33, int) -#define ZT_ECHOCANCEL _IOW (ZT_CODE, 33, struct zt_echocanparams) +#define ZT_ECHOCANCEL _IOW (ZT_CODE, 33, int) /* * Return a channel's channel number (useful for the /dev/zap/pseudo type interfaces @@ -875,17 +868,6 @@ struct zt_ring_cadence { int ringcadence[ZT_MAX_CADENCE]; }; -struct zt_echocanparam { - char name[8]; - unsigned int value; -}; - -struct zt_echocanparams { - unsigned int tap_length; /* 8 taps per millisecond */ - unsigned int param_count; /* number of parameters supplied */ - /* immediately follow this structure with zt_echocanparam structures */ -}; - struct zt_tone_def_header { int count; /* How many samples follow */ int zone; /* Which zone we are loading */ @@ -1183,7 +1165,7 @@ struct echo_can_state; void echo_can_init(void); void echo_chan_shutdown(void); void echo_can_identify(char *buf, size_t len); -int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, struct echo_can_state **ec); +struct echo_can_state *echo_can_create(int len, int adaption_mode); void echo_can_free(struct echo_can_state *ec); short echo_can_update(struct echo_can_state *ec, short iref, short isig); void echo_can_array_update(struct echo_can_state *ec, short *iref, short *isig); @@ -1532,11 +1514,9 @@ struct zt_span { /* Opt: IOCTL */ int (*ioctl)(struct zt_chan *chan, unsigned int cmd, unsigned long data); - /* Opt: Native echo cancellation (simple) */ + /* Opt: Native echo cancellation */ int (*echocan)(struct zt_chan *chan, int ecval); - int (*echocan_with_params)(struct zt_chan *chan, struct zt_echocanparams *ecp, struct zt_echocanparam *p); - /* Okay, now we get to the signalling. You have several options: */ /* Option 1: If you're a T1 like interface, you can just provide a @@ -63,6 +63,9 @@ */ /* #define ECHO_CAN_STEVE */ /* #define ECHO_CAN_STEVE2 */ +/* #define ECHO_CAN_MARK */ +/* #define ECHO_CAN_MARK2 */ +/* #define ECHO_CAN_MARK3 */ /* #define ECHO_CAN_KB1 */ /* This is the new latest and greatest */ #define ECHO_CAN_MG2 |