Index: zaptel.h =================================================================== RCS file: /usr/cvsroot/zaptel/zaptel.h,v retrieving revision 1.46 diff -u -r1.46 zaptel.h --- zaptel.h 26 Aug 2005 16:40:29 -0000 1.46 +++ zaptel.h 3 Sep 2005 21:21:01 -0000 @@ -157,6 +157,8 @@ #include "mec2.h" #elif defined(ECHO_CAN_KB1) #include "kb1ec.h" +#elif defined(ECHO_CAN_MG1) +#include "mg1ec.h" #else #include "mec3.h" #endif Index: zconfig.h =================================================================== RCS file: /usr/cvsroot/zaptel/zconfig.h,v retrieving revision 1.20 diff -u -r1.20 zconfig.h --- zconfig.h 31 Aug 2005 04:57:25 -0000 1.20 +++ zconfig.h 3 Sep 2005 21:21:01 -0000 @@ -55,6 +55,7 @@ #define ECHO_CAN_MARK2 /* #define ECHO_CAN_MARK3 */ /* #define ECHO_CAN_KB1 */ +/* #define ECHO_CAN_MG1 */ /* * Uncomment for aggressive residual echo supression under --- /dev/null 2005-04-08 20:07:54.000000000 +0200 +++ mg1ec.h 2005-09-03 23:23:50.000000000 +0200 @@ -0,0 +1,315 @@ +/* + * ECHO_CAN_MG1 + * + * Based upon mec3.h + * + * 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 _MG1_ECHO_H +#define _MG1_ECHO_H + + + +#ifdef __KERNEL__ +#include +#include +#define MALLOC(a) kmalloc((a), GFP_KERNEL) +#define FREE(a) kfree(a) +#else +#include +#include +#include +#include +#include +#define MALLOC(a) malloc(a) +#define FREE(a) free(a) +#endif + +#define ABS(a) abs(a!=-32768?a:-32767) + +/* 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_ECHO_ERROR 256 /* Maximum error when signedness of cancelled signal differs from signal */ + +#define MAX_ATTENUATION_SHIFT 8 /* 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 */ + +#if 0 +#define DEBUG 8000 /* Debug output every 8000 sample */ +#endif + +#include "arith.h" + +typedef struct { + short buf[NTAPS * 2]; + short max; + int maxexp; +} cbuf_s; + +typedef struct { + 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 */ +#ifdef DEBUG + int pos2; +#endif + int backup; /* Backup timer */ +} echo_can_state_t; + +static inline void echo_can_free(echo_can_state_t *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;xmax < abs(b->buf[pos + x])) { + b->max = abs(b->buf[pos + x]); + b->maxexp = x + 1; + } + } + } +} + +static inline short echo_can_update(echo_can_state_t *ec, short ref, short sig) +{ + int x; + short u; + int refpwr; + int beta; /* Factor */ + int se; /* Simulated echo */ + + /* 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; + +#ifdef DEBUG + if (!(ec->pos2 % DEBUG)) { + printk("sig: %hd, se: %d", sig, se); + } +#endif + + for (x=0; x < NTAPS; x++) { + if (sig != ec->sig.buf[ec->pos+x]) + break; + } + + if (sig == 0) { + /* Don't substract echo if signal == 0 */ + u = 0; + } else if (x == NTAPS) { + /* If the signal doesn't change over a long time, don't + * substract simulated echo, don't learn */ + ec->hcntr = HANG_TIME+1; + u = sig; + } else { + /* Do not "underflow" the signal, prevents "big room"-effect */ + if (se < -32768) { + se = -32768; + ec->hcntr = HANG_TIME+1; +#ifdef DO_BACKUP + memcpy(ec->a_i, ec->c_i, sizeof(ec->a_i)); + for (x=0;xtaps;x++) { + ec->a_s[x] = ec->a_i[x] >> 16; + } + ec->backup = BACKUP+1; +#endif + } else if (se > 32767) { + se = 32767; + ec->hcntr = HANG_TIME+1; +#ifdef DO_BACKUP + memcpy(ec->a_i, ec->c_i, sizeof(ec->a_i)); + for (x=0;xtaps;x++) { + ec->a_s[x] = ec->a_i[x] >> 16; + } + ec->backup = BACKUP+1; +#endif + } + + u = sig - se; + + if (ABS(ABS(se)-ABS(sig)) > MAX_ECHO_ERROR) + ec->hcntr = HANG_TIME+1; + + if (u / sig < 0) + u = sig - (se >> 1); + } + + if (ec->hcntr) + ec->hcntr--; + +#ifdef DEBUG + if (!(ec->pos2++ % DEBUG)) { + printk(" -> sig: %hd, se: %d, u: %hd\n", sig, se, u); + } +#endif + +#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 + + /* 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;xtaps;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;xtaps;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 + } + } +#ifdef AGGRESSIVE_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 echo_can_state_t *echo_can_create(int taps, int adaption_mode) +{ + echo_can_state_t *ec; + int x; + + taps = NTAPS; + ec = MALLOC(sizeof(echo_can_state_t)); + if (ec) { + memset(ec, 0, sizeof(echo_can_state_t)); + 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(echo_can_state_t *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