summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi_echocan_mg2.c
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
committerShaun Ruffell <sruffell@digium.com>2009-04-29 18:24:04 +0000
commit5f94a3b91de2c3835d6852d59cab9f6876177156 (patch)
tree4c25956b6ecdcd5b902ac80b40237bc3e938be44 /drivers/dahdi/dahdi_echocan_mg2.c
parent4a192a3e8f16ed6143377b5726e1fb53b446f5e9 (diff)
echocan: Improve interface for echo cancelers.
Echo cancelers are now able to report if they are able to automatically disable their NLP portions in the presence of tones in the audio stream. Also, the interface is changed to allow user space to just disable the NLP portion of the echo canceler. These changes improve fax and modem handling in DAHDI. This commit merges in the changes on http://svn.digium.com/svn/dahdi/linux/team/kpfleming/echocan_work Patch by: kpfleming Also contains improvements to CED tone detection. (closes issue #13286) Reported by: viniciusfontes git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@6529 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi_echocan_mg2.c')
-rw-r--r--drivers/dahdi/dahdi_echocan_mg2.c475
1 files changed, 257 insertions, 218 deletions
diff --git a/drivers/dahdi/dahdi_echocan_mg2.c b/drivers/dahdi/dahdi_echocan_mg2.c
index 4291edd..5dfd882 100644
--- a/drivers/dahdi/dahdi_echocan_mg2.c
+++ b/drivers/dahdi/dahdi_echocan_mg2.c
@@ -51,11 +51,11 @@ static int aggressive;
#define RESTORE_COEFFS {\
int x;\
- memcpy(ec->a_i, ec->c_i, ec->N_d*sizeof(int));\
- for (x=0;x<ec->N_d;x++) {\
- ec->a_s[x] = ec->a_i[x] >> 16;\
+ 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;\
}\
- ec->backup = BACKUP;\
+ pvt->backup = BACKUP;\
}
/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */
@@ -174,8 +174,33 @@ typedef struct {
short *buf_d;
} echo_can_cb_s;
-/* Echo canceller definition */
-struct echo_can_state {
+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 struct dahdi_echocan_factory my_factory = {
+ .name = "MG2",
+ .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 = {
+ .name = "MG2",
+ .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;
@@ -249,9 +274,11 @@ struct echo_can_state {
#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;
@@ -278,83 +305,85 @@ static inline short get_cc_s(echo_can_cb_s *cb, int pos)
return cb->buf_d[cb->idx_d + pos];
}
-static inline void init_cc(struct echo_can_state *ec, int N, int maxy, int maxu)
+static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu)
{
-
- void *ptr = ec;
+ void *ptr = pvt;
unsigned long tmp;
+
/* Double-word align past end of state */
- ptr += sizeof(struct echo_can_state);
+ ptr += sizeof(*pvt);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
- ec->N_d = N;
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->N_d = N;
+ pvt->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);
+ pvt->a_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
+ pvt->a_s = ptr;
+ ptr += (sizeof(short) * pvt->N_d);
/* Allocate backup memory */
- ec->b_i = ptr;
- ptr += (sizeof(int) * ec->N_d);
- ec->c_i = ptr;
- ptr += (sizeof(int) * ec->N_d);
+ pvt->b_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
+ pvt->c_i = ptr;
+ ptr += (sizeof(int) * pvt->N_d);
/* Reset Y circular buffer (short version) */
- init_cb_s(&ec->y_s, maxy, ptr);
+ 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(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
+ init_cb_s(&pvt->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);
+ 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(&ec->y_tilde_s, ec->N_d, ptr);
+ init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr);
/* Reset the absolute time index */
- ec->i_d = (int)0;
+ pvt->i_d = (int)0;
/* Reset the power computations (for y and u) */
- ec->Ly_i = DEFAULT_CUTOFF_I;
- ec->Lu_i = DEFAULT_CUTOFF_I;
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
+ pvt->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
- ec->id = (int)&ptr;
+ pvt->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;
+ 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 */
- ec->s_tilde_i = (int)0;
- ec->y_tilde_i = (int)0;
- ec->HCNTR_d = (int)0;
+ pvt->s_tilde_i = (int)0;
+ pvt->y_tilde_i = (int)0;
+ pvt->HCNTR_d = (int)0;
}
-static void echo_can_free(struct echo_can_state *ec)
+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", ec->dc_estimate >> 15);
+ printk(KERN_INFO "EC: DC bias calculated: %d V\n", pvt->dc_estimate >> 15);
#endif
- kfree(ec);
+ kfree(pvt);
}
#ifdef DC_NORMALIZE
@@ -365,7 +394,7 @@ short inline dc_removal(int *dc_estimate, short samp)
}
#endif
-static inline short sample_update(struct echo_can_state *ec, short iref, short isig)
+static inline short sample_update(struct ec_pvt *pvt, short iref, short isig)
{
/* Declare local variables that are used more than once */
/* ... */
@@ -380,7 +409,7 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
int two_beta_i;
#ifdef DC_NORMALIZE
- isig = dc_removal(&ec->dc_estimate, isig);
+ isig = dc_removal(&pvt->dc_estimate, isig);
#endif
/* flow A on pg. 428 */
@@ -398,29 +427,29 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
/* 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;
+ 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 */
- ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
+ pvt->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);
+ add_cc_s(&pvt->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 = CONVOLVE2(pvt->a_s,
+ pvt->y_s.buf_d + pvt->y_s.idx_d,
+ pvt->N_d);
rs >>= 15;
- if (ec->lastsig == isig) {
- ec->lastcount++;
+ if (pvt->lastsig == isig) {
+ pvt->lastcount++;
} else {
- ec->lastcount = 0;
- ec->lastsig = isig;
+ pvt->lastcount = 0;
+ pvt->lastsig = isig;
}
if (isig == 0) {
u = 0;
- } else if (ec->lastcount > 255) {
+ } 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;
@@ -429,11 +458,11 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
if (rs < -32768) {
rs = -32768;
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
} else if (rs > 32767) {
rs = 32767;
- ec->HCNTR_d = DEFAULT_HANGT;
+ pvt->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
}
@@ -456,35 +485,35 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
}
/* Push a copy of the output value sample into its circular buffer */
- add_cc_s(&ec->u_s, u);
+ add_cc_s(&pvt->u_s, u);
- if (!ec->backup) {
+ if (!pvt->backup) {
/* Backup coefficients periodically */
- ec->backup = BACKUP;
- memcpy(ec->c_i,ec->b_i,ec->N_d*sizeof(int));
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
+ 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
- ec->backup--;
+ pvt->backup--;
/* 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 ));
+ 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 */
- ec->s_tilde_i += abs(isig);
+ pvt->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
- add_cc_s(&ec->s_s, isig);
+ 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(&ec->y_tilde_s, ec->y_tilde_i);
+ 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 (!ec->HCNTR_d) {
- Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I);
+ 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);
@@ -498,139 +527,139 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* 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)));
+ 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 {
- ec->beta2_d = DEFAULT_BETA1;
+ pvt->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
- ec->beta2_i = DEFAULT_BETA1_I;
+ pvt->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
- two_beta_i = (ec->beta2_i * Py_i) >> 15;
+ 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 */
- ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
+ 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 */
- ec->Lu_i += abs(u);
+ 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 */
- ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
+ 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 */
- ec->Ly_i += abs(iref);
+ pvt->Ly_i += abs(iref);
- if (ec->Ly_i < DEFAULT_CUTOFF_I)
- ec->Ly_i = DEFAULT_CUTOFF_I;
+ if (pvt->Ly_i < DEFAULT_CUTOFF_I)
+ pvt->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
- if (ec->y_tilde_i > ec->max_y_tilde) {
+ if (pvt->y_tilde_i > pvt->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) {
+ 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... */
- 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);
+ 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 (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
- && (ec->max_y_tilde > 0)) {
+ if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde)
+ && (pvt->max_y_tilde > 0)) {
/* Then start the Hangover counter */
- ec->HCNTR_d = DEFAULT_HANGT;
+ 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", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
+ 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
- ++ec->cntr_nearend_speech_frames;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- } else if (ec->HCNTR_d > (int)0) {
+ } else if (pvt->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;
+ ++pvt->cntr_nearend_speech_frames;
#endif
- ec->HCNTR_d--;
+ 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 (!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 */
+ 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;
+ int max_coeffs[USED_COEFFS];
+ int *pos;
- if (ec->N_d > USED_COEFFS)
- memset(max_coeffs, 0, USED_COEFFS*sizeof(int));
+ 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: ec->Lu_i %9d\n", ec->Lu_i);
+ printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i);
#endif
#ifdef MEC2_STATS
- ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i;
- ++ec->cntr_coeff_updates;
+ pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i;
+ ++pvt->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;
+ 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 (ec->N_d > USED_COEFFS) {
- if (abs(ec->a_i[k]) > max_coeffs[USED_COEFFS-1]) {
- /* More or less insertion-sort... */
- pos = max_coeffs;
- while (*pos > abs(ec->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(ec->a_i[k]);
- }
+ 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
}
+#endif
+ }
#ifdef USED_COEFFS
- /* Filter out irrelevant coefficients */
- if (ec->N_d > USED_COEFFS)
- for (k=0; k < ec->N_d; k++)
- if (abs(ec->a_i[k]) < max_coeffs[USED_COEFFS-1])
- ec->a_i[k] = ec->a_s[k] = 0;
+ /* 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 {
+ } 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 pvt->Lu_i %5d < %5d\n", pvt->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;
+ 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
@@ -638,112 +667,116 @@ static inline short sample_update(struct echo_can_state *ec, short iref, short i
* 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)));
+ 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 (ec->aggressive) {
- if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
- for (k=0; k < 2; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
- }
+ 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 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 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
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->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 < 1; k++) {
- u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
- }
+ }
+ } 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 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 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
- ++ec->cntr_residualcorrected_frames;
+ ++pvt->cntr_residualcorrected_frames;
#endif
- }
+ }
#ifdef MEC2_STATS
- else {
- ++ec->cntr_residualcorrected_framesskipped;
- }
+ else {
+ ++pvt->cntr_residualcorrected_framesskipped;
+ }
#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);
+ 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 ((ec->i_d % MEC2_STATS) == 0) {
+ if ((pvt->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);
+ if (pvt->cntr_coeff_missedupdates > 0)
+ pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates);
else
- ec->avg_Lu_i_toolow = -1;
+ pvt->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);
+ if (pvt->cntr_coeff_updates > 0)
+ pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates);
else
- ec->avg_Lu_i_ok = -1;
+ 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",
- 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;
+ 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 */
- ec->i_d++;
+ pvt->i_d++;
return u;
}
-static void echo_can_update(struct echo_can_state *ec, short *isig, short *iref)
+static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size)
{
- unsigned int x;
+ struct ec_pvt *pvt = dahdi_to_pvt(ec);
+ u32 x;
short result;
- for (x = 0; x < DAHDI_CHUNKSIZE; x++) {
- result = sample_update(ec, *iref, *isig);
+ for (x = 0; x < size; x++) {
+ result = sample_update(pvt, *iref, *isig);
*isig++ = result;
++iref;
}
}
-static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
- struct echo_can_state **ec)
+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;
@@ -764,81 +797,87 @@ static int echo_can_create(struct dahdi_echocanparams *ecp, struct dahdi_echocan
2 * sizeof(short) * (maxu) + /* u_s */
2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */
- if (!(*ec = kmalloc(size, GFP_KERNEL)))
+ pvt = kzalloc(size, GFP_KERNEL);
+ if (!pvt)
return -ENOMEM;
- memset(*ec, 0, size);
+ pvt->dahdi.ops = &my_ops;
- (*ec)->aggressive = aggressive;
+ 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")) {
- (*ec)->aggressive = p[x].value ? 1 : 0;
+ pvt->aggressive = p[x].value ? 1 : 0;
} else {
printk(KERN_WARNING "Unknown parameter supplied to MG2 echo canceler: '%s'\n", p[x].name);
- kfree(*ec);
+ kfree(pvt);
return -EINVAL;
}
}
- init_cc(*ec, ecp->tap_length, maxy, maxu);
+ 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 echo_can_state *ec, int pos, short val)
+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
*/
- ec->HCNTR_d = ec->N_d << 1;
+ pvt->HCNTR_d = pvt->N_d << 1;
- if (pos >= ec->N_d) {
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
- memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
+ 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;
}
- ec->a_i[pos] = val << 17;
- ec->a_s[pos] = val << 1;
+ pvt->a_i[pos] = val << 17;
+ pvt->a_s[pos] = val << 1;
- if (++pos >= ec->N_d) {
- memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
- memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
+ 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 const struct dahdi_echocan me = {
- .name = "MG2",
- .owner = THIS_MODULE,
- .echo_can_create = echo_can_create,
- .echo_can_free = echo_can_free,
- .echo_can_array_update = echo_can_update,
- .echo_can_traintap = echo_can_traintap,
-};
+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(&me)) {
+ 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", me.name);
+ module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name);
return 0;
}
static void __exit mod_exit(void)
{
- dahdi_unregister_echocan(&me);
+ dahdi_unregister_echocan_factory(&my_factory);
}
module_param(debug, int, S_IRUGO | S_IWUSR);