summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2005-10-27 16:45:49 +0000
committermattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2005-10-27 16:45:49 +0000
commit436e753065f3186d3bee62a1b73fb079857a3292 (patch)
tree026feb3e33fdaa8b2536a40959cc6febc1d78ead
parent2fb165f0add1912328203c7be07f3d82ab632c2c (diff)
silly me, forgot to cvs add the new echo can :-) (Bug #5520)
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@799 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rwxr-xr-xmg2ec.h613
-rwxr-xr-xmg2ec_const.h81
2 files changed, 694 insertions, 0 deletions
diff --git a/mg2ec.h b/mg2ec.h
new file mode 100755
index 0000000..84f3f10
--- /dev/null
+++ b/mg2ec.h
@@ -0,0 +1,613 @@
+/*
+ * ECHO_CAN_MG2
+ *
+ * by Michael Gernoth
+ *
+ * Based upon kb1ec.h and mec2.h
+ *
+ * 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.
+ *
+ * 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/
+ *
+ */
+
+#ifndef _MG2_ECHO_H
+#define _MG2_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
+
+#define ABS(a) abs(a!=-32768?a:-32767)
+
+/* 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 */
+
+/* Get optimized routines for math */
+#include "arith.h"
+
+/* Bring in definitions for the various constants and thresholds */
+#include "mg2ec_const.h"
+
+#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;
+
+/* Echo canceller definition */
+typedef struct {
+ /* 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;
+ /* 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
+ short lastsig[256];
+ int lastpos;
+
+} echo_can_state_t;
+
+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(echo_can_state_t *ec, int N, int maxy, int maxu)
+{
+
+ void *ptr = ec;
+ unsigned long tmp;
+ /* Double-word align past end of state */
+ ptr += sizeof(echo_can_state_t);
+ 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 Sigma 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 the absolute time index */
+ 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;
+
+#ifdef MEC2_STATS
+ /* set the identity */
+ ec->id = (int)&ptr;
+
+ /* Reset performance stats */
+ ec->cntr_nearend_speech_frames = (int)0;
+ ec->cntr_residualcorrected_frames = (int)0;
+ ec->cntr_residualcorrected_framesskipped = (int)0;
+ ec->cntr_coeff_updates = (int)0;
+ ec->cntr_coeff_missedupdates = (int)0;
+
+ ec->avg_Lu_i_toolow = (int)0;
+ ec->avg_Lu_i_ok = (int)0;
+#endif
+
+ /* Reset the near-end speech detector */
+ ec->s_tilde_i = (int)0;
+ ec->y_tilde_i = (int)0;
+ ec->HCNTR_d = (int)0;
+
+}
+
+static inline void echo_can_free(echo_can_state_t *ec)
+{
+ FREE(ec);
+}
+
+static inline short echo_can_update(echo_can_state_t *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);
+ */
+
+ /* Update the Far-end receive signal circular buffers and accumulators */
+ /* ------------------------------------------------------------------- */
+ /* Delete the oldest sample from the power estimate accumulator */
+ ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
+ /* Add the new sample to the power estimate accumulator */
+ ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
+ /* Push a copy of the new sample into its 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;
+
+ ec->lastsig[ec->lastpos++] = isig;
+ if (ec->lastpos >= 256)
+ ec->lastpos = 0;
+
+ for (k=0; k < 256; k++) {
+ if (isig != ec->lastsig[k])
+ break;
+ }
+
+ if (isig == 0) {
+ u = 0;
+ } else if (k == 256) {
+ u = isig;
+ } else {
+ if (rs < -32768) {
+ rs = -32768;
+ ec->HCNTR_d = DEFAULT_HANGT;
+ memset(ec->a_i, 0, sizeof(int) * ec->N_d);
+ memset(ec->a_s, 0, sizeof(short) * ec->N_d);
+ } else if (rs > 32767) {
+ rs = 32767;
+ ec->HCNTR_d = DEFAULT_HANGT;
+ memset(ec->a_i, 0, sizeof(int) * ec->N_d);
+ memset(ec->a_s, 0, sizeof(short) * ec->N_d);
+ }
+
+ if (ABS(ABS(rs)-ABS(isig)) > 3000)
+ {
+ rs = 0;
+ memset(ec->a_i, 0, sizeof(int) * ec->N_d);
+ memset(ec->a_s, 0, sizeof(short) * ec->N_d);
+ }
+
+ /* 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(&ec->u_s, u);
+
+
+ /* Update the Near-end hybrid signal circular buffers and accumulators */
+ /* ------------------------------------------------------------------- */
+ /* Delete the oldest sample from the power estimate accumulator */
+ ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
+ /* Add the new sample to the power estimate accumulator */
+ ec->s_tilde_i += abs(isig);
+ /* Push a copy of the new sample into it's circular buffer */
+ add_cc_s(&ec->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(&ec->y_tilde_s, ec->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 (!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
+ /* 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 (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
+
+ /* Fixed point, inverted */
+ ec->beta2_i = DEFAULT_BETA1_I;
+
+ /* Fixed point version, inverted */
+ two_beta_i = (ec->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 */
+ ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
+ /* Add the new sample to the power estimate accumulator */
+ ec->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 */
+ ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
+ /* Add the new sample to the power estimate accumulator */
+ ec->Ly_i += abs(iref);
+
+ if (ec->Ly_i < DEFAULT_CUTOFF_I)
+ ec->Ly_i = DEFAULT_CUTOFF_I;
+
+
+ /* Update the Peak far-end receive signal detected */
+ /* ----------------------------------------------- */
+ 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);
+ }
+
+ /* Determine if near end speech was detected in this sample */
+ /* -------------------------------------------------------- */
+ if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
+ && (ec->max_y_tilde > 0)) {
+ /* Then start the Hangover counter */
+ ec->HCNTR_d = DEFAULT_HANGT;
+#ifdef MEC2_STATS_DETAILED
+ printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
+#endif
+#ifdef MEC2_STATS
+ ++ec->cntr_nearend_speech_frames;
+#endif
+ } else if (ec->HCNTR_d > (int)0) {
+ /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
+#ifdef MEC2_STATS
+ ++ec->cntr_nearend_speech_frames;
+#endif
+ ec->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 (!ec->HCNTR_d && /* no near-end speech present */
+ !(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
+ if (ec->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 MEC2_STATS_DETAILED
+ 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;
+ ++ec->cntr_coeff_updates;
+#endif
+ 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;
+ }
+ } 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);
+#endif
+#ifdef MEC2_STATS
+ ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i;
+ ++ec->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 (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)));
+#endif
+
+#ifndef NO_ECHO_SUPPRESSOR
+#ifdef AGGRESSIVE_SUPPRESSOR
+ if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
+ for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
+ 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)));
+#endif
+#ifdef MEC2_STATS
+ ++ec->cntr_residualcorrected_frames;
+#endif
+ }
+#else
+ if (ec->HCNTR_d == 0) {
+ if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) {
+ for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
+ 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)));
+#endif
+#ifdef MEC2_STATS
+ ++ec->cntr_residualcorrected_frames;
+#endif
+ }
+#ifdef MEC2_STATS
+ else {
+ ++ec->cntr_residualcorrected_framesskipped;
+ }
+#endif
+ }
+#endif
+#endif
+
+#if 0
+ /* This will generate a non-linear supression factor, once converted */
+ 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
+
+#ifdef MEC2_STATS
+ /* Periodically dump performance stats */
+ if ((ec->i_d % MEC2_STATS) == 0) {
+ /* make sure to avoid div0's! */
+ if (ec->cntr_coeff_missedupdates > 0)
+ ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates);
+ else
+ ec->avg_Lu_i_toolow = -1;
+
+ if (ec->cntr_coeff_updates > 0)
+ ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates);
+ 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",
+ ec->id,
+ ec->cntr_nearend_speech_frames,
+ ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped,
+ ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates,
+ ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow);
+
+ ec->cntr_nearend_speech_frames = 0;
+ ec->cntr_residualcorrected_frames = 0;
+ ec->cntr_residualcorrected_framesskipped = 0;
+ ec->cntr_coeff_updates = 0;
+ ec->cntr_coeff_missedupdates = 0;
+ ec->avg_Lu_i_ok = 0;
+ ec->avg_Lu_i_toolow = 0;
+ }
+#endif
+
+ /* Increment the sample index and return the corrected sample */
+ ec->i_d++;
+ return u;
+}
+
+static inline echo_can_state_t *echo_can_create(int len, int adaption_mode)
+{
+ echo_can_state_t *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 = (echo_can_state_t *)MALLOC(sizeof(echo_can_state_t) +
+ 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(echo_can_state_t) +
+ 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(echo_can_state_t *ec, int pos, short val)
+{
+ /* Set the hangover counter to the length of the can to
+ * avoid adjustments occuring immediately 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/mg2ec_const.h b/mg2ec_const.h
new file mode 100755
index 0000000..c4e6a67
--- /dev/null
+++ b/mg2ec_const.h
@@ -0,0 +1,81 @@
+/*
+ Important constants for tuning mg2 echo can
+ */
+#ifndef _MG2_CONST_H
+#define _MG2_CONST_H
+
+
+/* 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 4096
+
+/* 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 */
+
+/* This knob controls the number of passes the residual echo supression
+ * algorithm makes.
+ */
+#ifdef AGGRESSIVE_SUPPRESSOR
+ #define RESIDUAL_SUPRESSION_PASSES 2
+#else
+ #define RESIDUAL_SUPRESSION_PASSES 1
+#endif
+
+
+/***************************************************************/
+/* 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
+
+
+#endif /* _MG2_CONST_H */
+