/* * 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 #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 /* 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; 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 */ 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 */ #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, sig, 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 } } #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 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